aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Finlay <dan@danfinlay.com>2017-02-16 08:09:16 +0800
committerDan Finlay <dan@danfinlay.com>2017-02-16 08:09:16 +0800
commit6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf (patch)
tree47906b974dee789ffd544b4c85da67e6f2323016
parent245e779f37763ce0633119c257877706d0bf3554 (diff)
parent943bcec0d702b2c70b323000ed25d3c425e2a44f (diff)
downloadtangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.gz
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.bz2
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.lz
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.xz
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.tar.zst
tangerine-wallet-browser-6d103dc1e7eeb9a1f55e4387fcc1fe194c7eb4cf.zip
Merge branch 'kumavis-patch-1' of github.com:MetaMask/metamask-plugin into kumavis-patch-1
-rw-r--r--.eslintignore1
-rw-r--r--.eslintrc13
-rw-r--r--.gitignore6
-rw-r--r--CHANGELOG.md196
-rw-r--r--README.md73
-rw-r--r--USER_AGREEMENT.md180
-rw-r--r--app/images/icon-32.pngbin0 -> 1730 bytes
-rw-r--r--app/images/icon-64.pngbin0 -> 3573 bytes
-rw-r--r--app/manifest.json11
-rw-r--r--app/notification.html16
-rw-r--r--app/scripts/account-import-strategies/index.js45
-rw-r--r--app/scripts/background.js281
-rw-r--r--app/scripts/chromereload.js6
-rw-r--r--app/scripts/config.js7
-rw-r--r--app/scripts/contentscript.js52
-rw-r--r--app/scripts/first-time-state.js11
-rw-r--r--app/scripts/inpage.js25
-rw-r--r--app/scripts/keyring-controller.js554
-rw-r--r--app/scripts/keyrings/hd.js125
-rw-r--r--app/scripts/keyrings/simple.js100
-rw-r--r--app/scripts/lib/auto-faucet.js5
-rw-r--r--app/scripts/lib/auto-reload.js21
-rw-r--r--app/scripts/lib/config-manager.js275
-rw-r--r--app/scripts/lib/controllers/currency.js70
-rw-r--r--app/scripts/lib/controllers/preferences.js33
-rw-r--r--app/scripts/lib/controllers/shapeshift.js104
-rw-r--r--app/scripts/lib/eth-store.js132
-rw-r--r--app/scripts/lib/extension-instance.js19
-rw-r--r--app/scripts/lib/id-management.js19
-rw-r--r--app/scripts/lib/idStore-migrator.js80
-rw-r--r--app/scripts/lib/idStore.js364
-rw-r--r--app/scripts/lib/inpage-provider.js130
-rw-r--r--app/scripts/lib/is-popup-or-notification.js8
-rw-r--r--app/scripts/lib/message-manager.js151
-rw-r--r--app/scripts/lib/migrations.js5
-rw-r--r--app/scripts/lib/migrator/index.js51
-rw-r--r--app/scripts/lib/nodeify.js24
-rw-r--r--app/scripts/lib/notifications.js174
-rw-r--r--app/scripts/lib/obj-multiplex.js8
-rw-r--r--app/scripts/lib/port-stream.js10
-rw-r--r--app/scripts/lib/random-id.js9
-rw-r--r--app/scripts/lib/remote-store.js97
-rw-r--r--app/scripts/lib/sig-util.js28
-rw-r--r--app/scripts/lib/stream-utils.js9
-rw-r--r--app/scripts/lib/tx-utils.js132
-rw-r--r--app/scripts/metamask-controller.js729
-rw-r--r--app/scripts/migrations/002.js19
-rw-r--r--app/scripts/migrations/003.js19
-rw-r--r--app/scripts/migrations/004.js20
-rw-r--r--app/scripts/migrations/005.js44
-rw-r--r--app/scripts/migrations/006.js43
-rw-r--r--app/scripts/migrations/007.js40
-rw-r--r--app/scripts/migrations/008.js38
-rw-r--r--app/scripts/migrations/009.js43
-rw-r--r--app/scripts/migrations/010.js38
-rw-r--r--app/scripts/migrations/011.js33
-rw-r--r--app/scripts/migrations/_multi-keyring.js51
-rw-r--r--app/scripts/migrations/index.js25
-rw-r--r--app/scripts/notice-controller.js94
-rw-r--r--app/scripts/popup-core.js63
-rw-r--r--app/scripts/popup.js96
-rw-r--r--app/scripts/transaction-manager.js390
-rw-r--r--circle.yml1
-rw-r--r--development/announcer.js12
-rw-r--r--development/states.js1
-rw-r--r--development/states.json2
-rw-r--r--development/states/account-detail-with-shapeshift-tx.json3
-rw-r--r--development/states/account-detail-with-transaction-history.json3
-rw-r--r--development/states/account-detail.json9
-rw-r--r--development/states/account-list-with-imported.json83
-rw-r--r--development/states/accounts-loose.json125
-rw-r--r--development/states/accounts.json97
-rw-r--r--development/states/compilation-bug.json123
-rw-r--r--development/states/config.json4
-rw-r--r--development/states/create-vault-password.json3
-rw-r--r--development/states/custom-rpc.json7
-rw-r--r--development/states/empty-account-detail.json3
-rw-r--r--development/states/first-time.json39
-rw-r--r--development/states/help.json4
-rw-r--r--development/states/import-private-key-warning.json91
-rw-r--r--development/states/import-private-key.json63
-rw-r--r--development/states/locked.json84
-rw-r--r--development/states/lost-accounts.json90
-rw-r--r--development/states/new-account.json65
-rw-r--r--development/states/new-vault.json3
-rw-r--r--development/states/notice.json61
-rw-r--r--development/states/pending-crash.json2
-rw-r--r--development/states/pending-signature.json6
-rw-r--r--development/states/pending-tx-contract.json2
-rw-r--r--development/states/pending-tx-send-coin.json2
-rw-r--r--development/states/pending-tx-value.json2
-rw-r--r--development/states/private-network.json84
-rw-r--r--development/states/restore-vault.json (renamed from development/states/terms.json)16
-rw-r--r--development/states/send.json74
-rw-r--r--development/states/shapeshift.json346
-rw-r--r--development/states/show-seed-words.json1
-rw-r--r--development/states/terms-and-conditions.json39
-rw-r--r--development/test.html31
-rw-r--r--docs/extension_description/en.txt8
-rw-r--r--docs/form_persisting_architecture.md28
-rw-r--r--docs/multi_vault_planning.md188
-rw-r--r--gulpfile.js183
-rw-r--r--library/README.md24
-rw-r--r--library/controller.js159
-rw-r--r--library/example/index.html17
-rw-r--r--library/example/index.js56
-rw-r--r--library/index.js43
-rw-r--r--library/lib/setup-iframe.js19
-rw-r--r--library/lib/setup-provider.js25
-rw-r--r--library/popup.js19
-rw-r--r--library/server.js96
-rw-r--r--library/server/index.html20
-rw-r--r--mock-dev.js93
-rw-r--r--notices/archive/notice_0.md180
-rw-r--r--notices/notice-delete.js27
-rw-r--r--notices/notice-generator.js33
-rw-r--r--notices/notice-nonce.json1
-rw-r--r--notices/notices.json1
-rw-r--r--package.json69
-rw-r--r--test/integration/helpers.js4
-rw-r--r--test/integration/index.html4
-rw-r--r--test/integration/index.js21
-rw-r--r--test/integration/lib/first-time.js103
-rw-r--r--test/integration/lib/idStore-migrator-test.js92
-rw-r--r--test/integration/mocks/badVault.json1
-rw-r--r--test/integration/mocks/badVault2.json1
-rw-r--r--test/integration/mocks/oldVault.json19
-rw-r--r--test/integration/tests.js21
-rw-r--r--test/lib/example-code.json3
-rw-r--r--test/lib/migrations/004.json138
-rw-r--r--test/lib/mock-config-manager.js63
-rw-r--r--test/lib/mock-encryptor.js32
-rw-r--r--test/lib/mock-simple-keychain.js38
-rw-r--r--test/unit/account-link-test.js10
-rw-r--r--test/unit/actions/restore_vault_test.js60
-rw-r--r--test/unit/actions/set_selected_account_test.js3
-rw-r--r--test/unit/actions/tx_test.js14
-rw-r--r--test/unit/config-manager-test.js212
-rw-r--r--test/unit/currency-controller-test.js87
-rw-r--r--test/unit/explorer-link-test.js2
-rw-r--r--test/unit/extension-test.js41
-rw-r--r--test/unit/idStore-migration-test.js83
-rw-r--r--test/unit/idStore-test.js85
-rw-r--r--test/unit/keyring-controller-test.js172
-rw-r--r--test/unit/keyrings/hd-test.js127
-rw-r--r--test/unit/keyrings/simple-test.js149
-rw-r--r--test/unit/message-manager-test.js89
-rw-r--r--test/unit/metamask-controller-test.js29
-rw-r--r--test/unit/migrations-test.js105
-rw-r--r--test/unit/nodeify-test.js22
-rw-r--r--test/unit/notice-controller-test.js116
-rw-r--r--test/unit/tx-manager-test.js211
-rw-r--r--test/unit/util_test.js22
-rw-r--r--testem.yml1
-rw-r--r--ui-dev.js4
-rw-r--r--ui/app/account-detail.js54
-rw-r--r--ui/app/accounts/account-list-item.js42
-rw-r--r--ui/app/accounts/import/index.js91
-rw-r--r--ui/app/accounts/import/json.js98
-rw-r--r--ui/app/accounts/import/private-key.js68
-rw-r--r--ui/app/accounts/import/seed.js30
-rw-r--r--ui/app/accounts/index.js59
-rw-r--r--ui/app/actions.js492
-rw-r--r--ui/app/app.js179
-rw-r--r--ui/app/components/account-eth-balance.js140
-rw-r--r--ui/app/components/account-export.js9
-rw-r--r--ui/app/components/account-info-link.js2
-rw-r--r--ui/app/components/buy-button-subview.js106
-rw-r--r--ui/app/components/coinbase-form.js21
-rw-r--r--ui/app/components/copyButton.js4
-rw-r--r--ui/app/components/drop-menu-item.js10
-rw-r--r--ui/app/components/eth-balance.js27
-rw-r--r--ui/app/components/fiat-value.js71
-rw-r--r--ui/app/components/identicon.js30
-rw-r--r--ui/app/components/loading.js (renamed from ui/app/loading.js)19
-rw-r--r--ui/app/components/mascot.js27
-rw-r--r--ui/app/components/network.js25
-rw-r--r--ui/app/components/notice.js110
-rw-r--r--ui/app/components/pending-msg.js9
-rw-r--r--ui/app/components/pending-tx-details.js87
-rw-r--r--ui/app/components/pending-tx.js32
-rw-r--r--ui/app/components/qr-code.js14
-rw-r--r--ui/app/components/range-slider.js58
-rw-r--r--ui/app/components/shapeshift-form.js27
-rw-r--r--ui/app/components/shift-list-item.js1
-rw-r--r--ui/app/components/tab-bar.js35
-rw-r--r--ui/app/components/tooltip.js8
-rw-r--r--ui/app/components/transaction-list-item-icon.js33
-rw-r--r--ui/app/components/transaction-list-item.js34
-rw-r--r--ui/app/components/transaction-list.js9
-rw-r--r--ui/app/conf-tx.js69
-rw-r--r--ui/app/config.js34
-rw-r--r--ui/app/conversion-util.js5
-rw-r--r--ui/app/conversion.json5730
-rw-r--r--ui/app/css/index.css49
-rw-r--r--ui/app/css/lib.css27
-rw-r--r--ui/app/eth-store-warning.js90
-rw-r--r--ui/app/first-time/create-vault.js129
-rw-r--r--ui/app/first-time/disclaimer.js77
-rw-r--r--ui/app/first-time/init-menu.js130
-rw-r--r--ui/app/info.js53
-rw-r--r--ui/app/keychains/hd/create-vault-complete.js (renamed from ui/app/first-time/create-vault-complete.js)4
-rw-r--r--ui/app/keychains/hd/recover-seed/confirmation.js (renamed from ui/app/recover-seed/confirmation.js)22
-rw-r--r--ui/app/keychains/hd/restore-vault.js (renamed from ui/app/first-time/restore-vault.js)39
-rw-r--r--ui/app/new-keychain.js29
-rw-r--r--ui/app/reducers.js2
-rw-r--r--ui/app/reducers/app.js128
-rw-r--r--ui/app/reducers/metamask.js38
-rw-r--r--ui/app/send.js174
-rw-r--r--ui/app/store.js2
-rw-r--r--ui/app/unlock.js96
-rw-r--r--ui/app/util.js5
-rw-r--r--ui/css.js1
-rw-r--r--ui/example.js14
-rw-r--r--ui/index.js11
-rw-r--r--ui/lib/account-link.js5
-rw-r--r--ui/lib/contract-namer.js9
-rw-r--r--ui/lib/explorer-link.js2
-rw-r--r--ui/lib/icon-factory.js4
-rw-r--r--ui/lib/lost-accounts-notice.js23
-rw-r--r--ui/lib/persistent-form.js61
-rw-r--r--ui/lib/tx-helper.js6
222 files changed, 16338 insertions, 3290 deletions
diff --git a/.eslintignore b/.eslintignore
index bfb2163d2..df49525be 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1 @@
app/scripts/lib/extension-instance.js
-ui/app/conversion-util.js
diff --git a/.eslintrc b/.eslintrc
index 7fab8f127..17a59d22d 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,5 +1,6 @@
{
"parserOptions": {
+ "sourceType": "module",
"ecmaVersion": 6,
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
@@ -44,10 +45,10 @@
"eol-last": 1,
"eqeqeq": [2, "allow-null"],
"generator-star-spacing": [2, { "before": true, "after": true }],
- "handle-callback-err": [2, "^(err|error)$" ],
+ "handle-callback-err": [1, "^(err|error)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"jsx-quotes": [2, "prefer-single"],
- "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
+ "key-spacing": 1,
"keyword-spacing": [2, { "before": true, "after": true }],
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"new-parens": 2,
@@ -127,14 +128,14 @@
"no-whitespace-before-property": 2,
"no-with": 2,
"one-var": [2, { "initialized": "never" }],
- "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }],
+ "operator-linebreak": [1, "after", { "overrides": { "?": "ignore", ":": "ignore" } }],
"padded-blocks": [1, "never"],
- "quotes": [2, "single", "avoid-escape"],
+ "quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}],
"semi": [2, "never"],
"semi-spacing": [2, { "before": false, "after": true }],
"space-before-blocks": [1, "always"],
"space-before-function-paren": [1, "always"],
- "space-in-parens": [2, "never"],
+ "space-in-parens": [1, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
@@ -145,6 +146,6 @@
"wrap-iife": [2, "any"],
"yield-star-spacing": [2, "both"],
"yoda": [2, "never"],
- "prefer-const": 1
+ "prefer-const": 1,
}
}
diff --git a/.gitignore b/.gitignore
index fa8a9151f..c61847aab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
dist
-
+npm-debug.log
node_modules
temp
.tmp
@@ -7,10 +7,12 @@ temp
app/bower_components
test/bower_components
package
-
.DS_Store
builds/
+disc/
notes.txt
app/.DS_Store
development/bundle.js
builds.zip
+test/integration/bundle.js
+development/states.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e6bc5299..f4f2148b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,204 @@
# Changelog
## Current Master
+- net_version has been made synchronous.
+- Test suite for migrations expanded.
+
+- Improve test coverage of eth.sign behavior, including a code example of verifying a signature.
+
+## 3.2.2 2017-2-8
+
+- Revert eth.sign behavior to the previous one with a big warning. We will be gradually implementing the new behavior over the coming time. https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign
+
+## 3.2.1 2017-2-8
+
+- Revert back to old style message signing.
+- Fixed some build errors that were causing a variety of bugs.
+
+## 3.2.0 2017-2-8
+
+- Add ability to import accounts in JSON file format (used by Mist, Geth, MyEtherWallet, and more!)
+- Fix unapproved messages not being included in extension badge.
+- Fix rendering bug where the Confirm transaction view would lets you approve transactions when the account has insufficient balance.
+
+## 3.1.2 2017-1-24
+
+- Fix "New Account" default keychain
+
+## 3.1.1 2017-1-20
+
+- Fix HD wallet seed export
+
+## 3.1.0 2017-1-18
+
+- Add ability to import accounts by private key.
+- Fixed bug that returned the wrong transaction hashes on private networks that had not implemented EIP 155 replay protection (like TestRPC).
+
+## 3.0.1 2017-1-17
+
+- Fixed bug that prevented eth.sign from working.
+- Fix the displaying of transactions that have been submitted to the network in Transaction History
+
+## 3.0.0 2017-1-16
+
+- Fix seed word account generation (https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.t4i1qmmsz).
+- Fix Bug where you see a empty transaction flash by on the confirm transaction view.
+- Create visible difference in transaction history between a approved but not yet included in a block transaction and a transaction who has been confirmed.
+- Fix memory leak in RPC Cache
+- Override RPC commands eth_syncing and web3_clientVersion
+- Remove certain non-essential permissions from certain builds.
+- Add a check for when a tx is included in a block.
+- Fix bug where browser-solidity would sometimes warn of a contract creation error when there was none.
+- Minor modifications to network display.
+- Network now displays properly for pending transactions.
+- Implement replay attack protections allowed by EIP 155.
+- Fix bug where sometimes loading account data would fail by querying a future block.
+
+## 2.14.1 2016-12-20
+
+- Update Coinbase info. and increase the buy amount to $15
+- Fixed ropsten transaction links
+- Temporarily disable extension reload detection causing infinite reload bug.
+- Implemented basic checking for valid RPC URIs.
+
+## 2.14.0 2016-12-16
+
+- Removed Morden testnet provider from provider menu.
+- Add support for notices.
+- Fix broken reload detection.
+- Fix transaction forever cached-as-pending bug.
+
+## 2.13.11 2016-11-23
+
+- Add support for synchronous RPC method "eth_uninstallFilter".
+- Forgotten password prompts now send users directly to seed word restoration.
+
+## 2.13.10 2016-11-22
+
+- Improve gas calculation logic.
+- Default to Dapp-specified gas limits for transactions.
+- Ropsten networks now properly point to the faucet when attempting to buy ether.
+- Ropsten transactions now link to etherscan correctly.
+
+## 2.13.9 2016-11-21
+
+- Add support for the new, default Ropsten Test Network.
+- Fix bug that would cause MetaMask to occasionally lose its StreamProvider connection and drop requests.
+- Fix bug that would cause the Custom RPC menu item to not appear when Localhost 8545 was selected.
+- Point ropsten faucet button to actual faucet.
+- Phase out ethereumjs-util from our encryptor module.
+
+## 2.13.8 2016-11-16
+
+- Show a warning when a transaction fails during simulation.
+- Fix bug where 20% of gas estimate was not being added properly.
+- Render error messages in confirmation screen more gracefully.
+
+## 2.13.7 2016-11-8
+
+- Fix bug where gas estimate would sometimes be very high.
+- Increased our gas estimate from 100k gas to 20% of estimate.
+- Fix github link on info page to point at current repository.
+
+## 2.13.6 2016-10-26
+
+- Add a check for improper Transaction data.
+- Inject up to date version of web3.js
+- Now nicknaming new accounts "Account #" instead of "Wallet #" for clarity.
+- Fix bug where custom provider selection could show duplicate items.
+- Fix bug where connecting to a local morden node would make two providers appear selected.
+- Fix bug that was sometimes preventing transactions from being sent.
+
+## 2.13.5 2016-10-18
+
+- Increase default max gas to `100000` over the RPC's `estimateGas` response.
+- Fix bug where slow-loading dapps would sometimes trigger infinite reload loops.
+
+## 2.13.4 2016-10-17
+
+- Add custom transaction fee field to send form.
+- Fix bug where web3 was being injected into XML files.
+- Fix bug where changing network would not reload current Dapps.
+
+## 2.13.3 2016-10-4
+
+- Fix bug where log queries were filtered out.
+- Decreased vault confirmation button font size to help some Linux users who could not see it.
+- Made popup a little taller because it would sometimes cut off buttons.
+- Fix bug where long account lists would get scrunched instead of scrolling.
+- Add legal information to relevant pages.
+- Rename UI elements to be more consistent with one another.
+- Updated Terms of Service and Usage.
+- Prompt users to re-agree to the Terms of Service when they are updated.
+
+## 2.13.2 2016-10-4
+
+- Fix bug where chosen FIAT exchange rate does no persist when switching networks
+- Fix additional parameters that made MetaMask sometimes receive errors from Parity.
+- Fix bug where invalid transactions would still open the MetaMask popup.
+- Removed hex prefix from private key export, to increase compatibility with Geth, MyEtherWallet, and Jaxx.
+
+## 2.13.1 2016-09-23
+
+- Fix a bug with estimating gas on Parity
+- Show loading indication when selecting ShapeShift as purchasing method.
+
+## 2.13.0 2016-09-18
+
+- Add Parity compatibility, fixing Geth dependency issues.
+- Add a link to the transaction in history that goes to https://metamask.github.io/eth-tx-viz
+too help visualize transactions and to where they are going.
+- Show "Buy Ether" button and warning on tx confirmation when sender balance is insufficient
+
+## 2.12.1 2016-09-14
+
+- Fixed bug where if you send a transaction from within MetaMask extension the
+popup notification opens up.
+- Fixed bug where some tx errors would block subsequent txs until the plugin was refreshed.
+
+## 2.12.0 2016-09-14
+
+- Add a QR button to the Account detail screen
+- Fixed bug where opening MetaMask could close a non-metamask popup.
+- Fixed memory leak that caused occasional crashes.
+
+## 2.11.1 2016-09-12
+
+- Fix bug that prevented caches from being cleared in Opera.
+
+## 2.11.0 2016-09-12
+
+- Fix bug where pending transactions from Test net (or other networks) show up In Main net.
+- Add fiat conversion values to more views.
+- On fresh install, open a new tab with the MetaMask Introduction video. Does not open on update.
+- Block negative values from transactions.
+- Fixed a memory leak.
+- MetaMask logo now renders as super lightweight SVG, improving compatibility and performance.
+- Now showing loading indication during vault unlocking, to clarify behavior for users who are experience slow unlocks.
+- Now only initially creates one wallet when restoring a vault, to reduce some users' confusion.
+
+## 2.10.2 2016-09-02
+
+- Fix bug where notification popup would not display.
+
+## 2.10.1 2016-09-02
+
+- Fix bug where provider menu did not allow switching to custom network from a custom network.
+- Sending a transaction from within MetaMask no longer triggers a popup.
+- The ability to build without livereload features (such as for production) can be enabled with the gulp --disableLiveReload flag.
+- Fix Ethereum JSON RPC Filters bug.
+
+## 2.10.0 2016-08-29
+
+- Changed transaction approval from notifications system to popup system.
+- Add a back button to locked screen to allow restoring vault from seed words when password is forgotten.
+- Forms now retain their values even when closing the popup and reopening it.
+- Fixed a spelling error in provider menu.
## 2.9.2 2016-08-24
- Fixed shortcut bug from preventing installation.
-
## 2.9.1 2016-08-24
- Added static image as fallback for when WebGL isn't supported.
@@ -27,7 +219,7 @@
## 2.8.0 2016-08-15
- Integrate ShapeShift
-- Add a for for Coinbase to specify amount to buy
+- Add a form for Coinbase to specify amount to buy
- Fix various typos.
- Make dapp-metamask connection more reliable
- Remove Ethereum Classic from provider menu.
diff --git a/README.md b/README.md
index 4c348b56c..aa79f4564 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,12 @@
# MetaMask Plugin [![Build Status](https://circleci.com/gh/MetaMask/metamask-plugin.svg?style=shield&circle-token=a1ddcf3cd38e29267f254c9c59d556d513e3a1fd)](https://circleci.com/gh/MetaMask/metamask-plugin)
+## Developing Compatible Dapps
+
+If you're a web dapp developer, we've got two types of guides for you:
+
+- If you've never built a Dapp before, we've got a gentle introduction on [Developing Dapps with Truffle and MetaMask](https://blog.metamask.io/developing-for-metamask-with-truffle/).
+- If you have a Dapp, and you want to ensure compatibility, [here is our guide on building MetaMask-compatible Dapps](https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md)
+
## Building locally
- Install [Node.js](https://nodejs.org/en/) version 6.3.1 or later.
@@ -11,6 +18,12 @@
Uncompressed builds can be found in `/dist`, compressed builds can be found in `/builds` once they're built.
+## Installing Local Builds on Chrome
+
+To install your locally built extension on Chrome, [follow this guide](http://stackoverflow.com/a/24577660/272576).
+
+The built extension is stored in `./dist/chrome/`.
+
## Architecture
[![Architecture Diagram](./docs/architecture.png)][1]
@@ -19,6 +32,13 @@
```bash
npm install
+npm start
+```
+
+## Build for Publishing
+
+```bash
+npm run dist
```
#### In Chrome
@@ -71,7 +91,7 @@ To enjoy the live-reloading that `gulp dev` offers while working on the `web3-pr
2. `npm install` in its folder.
3. Run `npm link` in its folder.
4. Run `npm link $DEP_NAME` in this project folder.
- 5. Next time you `gulp dev` it will watch the dependency for changes as well!
+ 5. Next time you `npm start` it will watch the dependency for changes as well!
### Running Tests
@@ -83,6 +103,10 @@ You can also test with a continuously watching process, via `npm run watch`.
You can run the linter by itself with `gulp lint`.
+#### Writing Browser Tests
+
+To write tests that will be run in the browser using QUnit, add your test files to `test/integration/lib`.
+
### Deploying the UI
You must be authorized already on the MetaMask plugin.
@@ -93,3 +117,50 @@ You can run the linter by itself with `gulp lint`.
3. Upload the latest zip file from `builds/metamask-$PLATFORM-$VERSION.zip` as the updated package.
[1]: http://www.nomnoml.com/#view/%5B%3Cactor%3Euser%5D%0A%0A%5Bmetamask-ui%7C%0A%20%20%20%5Btools%7C%0A%20%20%20%20%20react%0A%20%20%20%20%20redux%0A%20%20%20%20%20thunk%0A%20%20%20%20%20ethUtils%0A%20%20%20%20%20jazzicon%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20account-detail%0A%20%20%20%20%20accounts%0A%20%20%20%20%20locked-screen%0A%20%20%20%20%20restore-vault%0A%20%20%20%20%20identicon%0A%20%20%20%20%20config%0A%20%20%20%20%20info%0A%20%20%20%5D%0A%20%20%20%5Breducers%7C%0A%20%20%20%20%20app%0A%20%20%20%20%20metamask%0A%20%20%20%20%20identities%0A%20%20%20%5D%0A%20%20%20%5Bactions%7C%0A%20%20%20%20%20%5BaccountManager%5D%0A%20%20%20%5D%0A%20%20%20%5Bcomponents%5D%3A-%3E%5Bactions%5D%0A%20%20%20%5Bactions%5D%3A-%3E%5Breducers%5D%0A%20%20%20%5Breducers%5D%3A-%3E%5Bcomponents%5D%0A%5D%0A%0A%5Bweb%20dapp%7C%0A%20%20%5Bui%20code%5D%0A%20%20%5Bweb3%5D%0A%20%20%5Bmetamask-inpage%5D%0A%20%20%0A%20%20%5B%3Cactor%3Eui%20developer%5D%0A%20%20%5Bui%20developer%5D-%3E%5Bui%20code%5D%0A%20%20%5Bui%20code%5D%3C-%3E%5Bweb3%5D%0A%20%20%5Bweb3%5D%3C-%3E%5Bmetamask-inpage%5D%0A%5D%0A%0A%5Bmetamask-background%7C%0A%20%20%5Bprovider-engine%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bid%20store%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%3E%5Bhooked%20wallet%20subprovider%5D%0A%20%20%5Bhooked%20wallet%20subprovider%5D%3C-%3E%5Bid%20store%5D%0A%20%20%5Bconfig%20manager%7C%0A%20%20%20%20%5Brpc%20configuration%5D%0A%20%20%20%20%5Bencrypted%20keys%5D%0A%20%20%20%20%5Bwallet%20nicknames%5D%0A%20%20%5D%0A%20%20%0A%20%20%5Bprovider-engine%5D%3C-%5Bconfig%20manager%5D%0A%20%20%5Bid%20store%5D%3C-%3E%5Bconfig%20manager%5D%0A%5D%0A%0A%5Buser%5D%3C-%3E%5Bmetamask-ui%5D%0A%0A%5Buser%5D%3C%3A--%3A%3E%5Bweb%20dapp%5D%0A%0A%5Bmetamask-contentscript%7C%0A%20%20%5Bplugin%20restart%20detector%5D%0A%20%20%5Brpc%20passthrough%5D%0A%5D%0A%0A%5Brpc%20%7C%0A%20%20%5Bethereum%20blockchain%20%7C%0A%20%20%20%20%5Bcontracts%5D%0A%20%20%20%20%5Baccounts%5D%0A%20%20%5D%0A%5D%0A%0A%5Bweb%20dapp%5D%3C%3A--%3A%3E%5Bmetamask-contentscript%5D%0A%5Bmetamask-contentscript%5D%3C-%3E%5Bmetamask-background%5D%0A%5Bmetamask-background%5D%3C-%3E%5Bmetamask-ui%5D%0A%5Bmetamask-background%5D%3C-%3E%5Brpc%5D%0A
+
+
+### Generate Development Visualization
+
+This will generate a video of the repo commit history.
+
+Install preqs:
+```
+brew install gource
+brew install ffmpeg
+```
+
+From the repo dir, pipe `gource` into `ffmpeg`:
+```
+gource \
+ --seconds-per-day .1 \
+ --user-scale 1.5 \
+ --default-user-image "./images/icon-512.png" \
+ --viewport 1280x720 \
+ --auto-skip-seconds .1 \
+ --multi-sampling \
+ --stop-at-end \
+ --highlight-users \
+ --hide mouse,progress \
+ --file-idle-time 0 \
+ --max-files 0 \
+ --background-colour 000000 \
+ --font-size 18 \
+ --date-format "%b %d, %Y" \
+ --highlight-dirs \
+ --user-friction 0.1 \
+ --title "MetaMask Development History" \
+ --output-ppm-stream - \
+ --output-framerate 30 \
+ | ffmpeg -y -r 30 -f image2pipe -vcodec ppm -i - -b 65536K metamask-dev-history.mp4
+```
+
+## Generating Notices
+
+To add a notice:
+```
+npm run generateNotice
+```
+To delete a notice:
+```
+npm run deleteNotice
+```
diff --git a/USER_AGREEMENT.md b/USER_AGREEMENT.md
index a4504efcc..8c4c41e58 100644
--- a/USER_AGREEMENT.md
+++ b/USER_AGREEMENT.md
@@ -1,55 +1,179 @@
-# Disclaimer
+# Terms of Use #
-**METAMASK is still Beta software. Do not rely on METAMASK to manage large amounts of Ether.**
+**THIS AGREEMENT IS SUBJECT TO BINDING ARBITRATION AND A WAIVER OF CLASS ACTION RIGHTS AS DETAILED IN SECTION 13. PLEASE READ THE AGREEMENT CAREFULLY.**
-## SECURITY WARNINGS
+_Our Terms of Use have been updated as of September 5, 2016_
-**You are responsible for your own computer's security. If your machine is compromised, you could lose your ether and access to your accounts and contracts.**
+## 1. Acceptance of Terms ##
-You are responsible for your own actions.
+MetaMask provides a platform for managing Ethereum (or "ETH") accounts, and allowing ordinary websites to interact with the Ethereum blockchain, while keeping the user in control over what transactions they approve, through our website located at[ ](http://metamask.io)[https://metamask.io/](https://metamask.io/) and browser plugin (the "Site") — which includes text, images, audio, code and other materials (collectively, the “Content”) and all of the features, and services provided. The Site, and any other features, tools, materials, or other services offered from time to time by MetaMask are referred to here as the “Service.” Please read these Terms of Use (the “Terms” or “Terms of Use”) carefully before using the Service. By using or otherwise accessing the Services, or clicking to accept or agree to these Terms where that option is made available, you (1) accept and agree to these Terms (2) consent to the collection, use, disclosure and other handling of information as described in our Privacy Policy and (3) any additional terms, rules and conditions of participation issued by MetaMask from time to time. If you do not agree to the Terms, then you may not access or use the Content or Services.
-The user acknowledges the following serious risks to any use of METAMASK and ETH and expressly agrees to not hold liable MetaMask or the MetaMask Team should any of these risks occur:
+## 2. Modification of Terms of Use ##
-**METMASK AT THIS POINT IN TIME IS MEANT FOR DEVELOPERS OR PEOPLE WITH A UNDERSTANDING OF THE ETHEREUM PROTOCOL. DUE TO THE NATURE OF METAMASK, THE METAMASK TEAM DOES NOT ADVISE STORING LARGE AMOUNTS OF ETHER IN METAMASK VAULTS. THE METAMASK TEAM DOES NOT TAKE RESPONSIBILITY FOR LOST OR STOLEN ETHER. BY CLICKING AGREE AND USING METAMASK THE USER UNDERSTANDS THE RISKS OF USING EXPERIMENTAL SOFTWARE AND ACKNOWLEDGES THE FACT THAT THERE MAY BE BUGS OR SECURITY RISKS IN METAMASK THAT MAY RESULT IN LOSS OF ETHER AND THE METAMASK TEAM IS NOT HELD RESPONSIBLE FOR REIMBURSEMENT OF LOST FUNDS.**
+Except for Section 13, providing for binding arbitration and waiver of class action rights, MetaMask reserves the right, at its sole discretion, to modify or replace the Terms of Use at any time. The most current version of these Terms will be posted on our Site. You shall be responsible for reviewing and becoming familiar with any such modifications. Use of the Services by you after any modification to the Terms constitutes your acceptance of the Terms of Use as modified.
-### Risk of Regulatory Actions in One or More Jurisdictions
-METAMASK and ETH could be impacted by one or more regulatory inquiries or regulatory action, which could impede or limit the ability of METAMASK to continue to develop, or which could impede or limit the ability of User to use Ethereum Platform or ETH.
-### Risk that METAMASK—As Developed—Will Not Meet the Expectations of User
+## 3. Eligibility ##
-The User recognizes that METAMASK is under development and may undergo significant changes. User acknowledges that any expectations regarding the form and functionality of METAMASK held by the User may not be met upon release of METAMASK, for any number of reasons including a change in the design and implementation plans and execution of the implementation of METAMASK.
+You hereby represent and warrant that you are fully able and competent to enter into the terms, conditions, obligations, affirmations, representations and warranties set forth in these Terms and to abide by and comply with these Terms.
-### Risk of Weaknesses or Exploitable Breakthroughs in the Field of Cryptography
+MetaMask is a global platform and by accessing the Content or Services, you are representing and warranting that, you are of the legal age of majority in your jurisdiction as is required to access such Services and Content and enter into arrangements as provided by the Service. You further represent that you are otherwise legally permitted to use the service in your jurisdiction including owning cryptographic tokens of value, and interacting with the Services or Content in any way. You further represent you are responsible for ensuring compliance with the laws of your jurisdiction and acknowledge that MetaMask is not liable for your compliance with such laws.
-Cryptography is an art, not a science; the state of the art can advance over time. Advances in code cracking or technical advances such as the development of quantum computers may present risks to cryptocurrencies and METAMASK, which could result in the theft or loss of ETH. To the extent possible, METAMASK intends to update the protocol underlying METAMASK to account for any advances in cryptography and to incorporate additional security measures, but cannot it cannot predict the future of cryptography or the success of any future security updates.
+## 4 Account Password and Security ##
-### Warning of the Volatility of Crypto Currencies
+When setting up an account within MetaMask, you will be responsible for keeping your own account secrets, which may be a twelve-word seed phrase, an account file, or other locally stored secret information. MetaMask encrypts this information locally with a password you provide, that we never send to our servers. You agree to (a) never use the same password for MetaMask that you have ever used outside of this service; (b) keep your secret information and password confidential and do not share them with anyone else; (c) immediately notify MetaMask of any unauthorized use of your account or breach of security. MetaMask cannot and will not be liable for any loss or damage arising from your failure to comply with this section.
-ETHEREUM is a new enough technology that the value of its core token and network fee currency is highly volatile, due to a combination of adoption and speculation.
+## 5. Representations, Warranties, and Risks ##
-METAMASK cannot be held responsible for increases in the cost of network resources that affect the viability of any business activities that may take place on the blockchain.
+### 5.1. Warranty Disclaimer ###
-### Warning of Possible Accidental Flaws in Ethereum Apps
+You expressly understand and agree that your use of the Service is at your sole risk. The Service (including the Service and the Content) are provided on an "AS IS" and "as available" basis, without warranties of any kind, either express or implied, including, without limitation, implied warranties of merchantability, fitness for a particular purpose or non-infringement. You acknowledge that MetaMask has no control over, and no duty to take any action regarding: which users gain access to or use the Service; what effects the Content may have on you; how you may interpret or use the Content; or what actions you may take as a result of having been exposed to the Content. You release MetaMask from all liability for you having acquired or not acquired Content through the Service. MetaMask makes no representations concerning any Content contained in or accessed through the Service, and MetaMask will not be responsible or liable for the accuracy, copyright compliance, legality or decency of material contained in or accessed through the Service.
-ETHEREUM applications are only as good as the code they are written with. METAMASK cannot be held responsible if you interact with a flawed contract. ETHEREUM applications are a very new kind of software, and people are still learning how to write fully bulletproof software, so you are responsible for evaluating the trustworthiness of the websites you are willing to submit Ethereum transactions to.
+### 5.2 Sophistication and Risk of Cryptographic Systems ###
-### Warning of Possible Malicious Contracts in Ethereum Apps
+By utilizing the Service or interacting with the Content or platform in any way, you represent that you understand the inherent risks associated with cryptographic systems; and warrant that you have an understanding of the usage and intricacies of native cryptographic tokens, like Ether (ETH) and Bitcoin (BTC), smart contract based tokens such as those that follow the Ethereum Token Standard (https://github.com/ethereum/EIPs/issues/20), and blockchain-based software systems.
-ETHEREUM applications can be written maliciously. METAMASK cannot be held liable if you choose to interact with a contract that does not perform as expected. A contract may keep funds sent to it, or even impersonate the person making a request of it. METAMASK developers will do everything they can to warn you of potential threats, but they cannot be considered omniscient, and so all liabilities of interacting with malevolent contracts must lie on the user.
+### 5.3 Risk of Regulatory Actions in One or More Jurisdictions ###
-### Acknowledgment, Acceptance of all Risks and Disclaimer of Warranties and Liabilities
+MetaMask and ETH could be impacted by one or more regulatory inquiries or regulatory action, which could impede or limit the ability of MetaMask to continue to develop, or which could impede or limit your ability to access or use the Service or Ethereum blockchain.
-THE USER EXPRESSLY KNOWS AND AGREES THAT THE USER IS USING METAMASK AND METAMASK AT THE USER’S SOLE RISK. THE USER REPRESENTS THAT THE USER HAS AN ADEQUATE UNDERSTANDING OF THE RISKS, USAGE AND INTRICACIES OF CRYPTOGRAPHIC TOKENS AND BLOCKCHAIN-BASED OPEN SOURCE SOFTWARE, ETH PLATFORM AND ETH. THE USER ACKNOWLEDGES AND AGREES THAT, TO THE FULLEST EXTENT PERMITTED BY ANY APPLICABLE LAW, THE DISCLAIMERS OF LIABILITY CONTAINED HEREIN APPLY TO ANY AND ALL DAMAGES OR INJURY WHATSOEVER CAUSED BY OR RELATED TO RISKS OF, USE OF, OR INABILITY TO USE, ETH OR METAMASK UNDER ANY CAUSE OR ACTION WHATSOEVER OF ANY KIND IN ANY JURISDICTION, INCLUDING, WITHOUT LIMITATION, ACTIONS FOR BREACH OF WARRANTY, BREACH OF CONTRACT OR TORT (INCLUDING NEGLIGENCE) AND THAT NEITHER METAMASK NOR THE METAMASK TEAM WILL BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES, INCLUDING FOR LOSS OF PROFITS, GOODWILL OR DATA.
+### 5.4 Risk of Weaknesses or Exploits in the Field of Cryptography ###
-### Acknowledgement of User's Duty to All Laws and Regulations
+You acknowledge and understand that Cryptography is a progressing field. Advances in code cracking or technical advances such as the development of quantum computers may present risks to cryptocurrencies and Services of Content, which could result in the theft or loss of your cryptographic tokens or property. To the extent possible, MetaMask intends to update the protocol underlying Services to account for any advances in cryptography and to incorporate additional security measures, but does not guarantee or otherwise represent full security of the system. By using the Service or accessing Content, you acknowledge these inherent risks.
-The USER is responsible for their own actions with the use of the METAMASK software. Like any tool, it could be conceivably be used in ways not legal in some jurisdictions.
+### 5.5 Volatility of Crypto Currencies ###
-THE USER agrees to adhere to all binding laws and regulations for the conditions in which they use METAMASK.
+You understand that Ethereum and other blockchain technologies and associated currencies or tokens are highly volatile due to many factors including but not limited to adoption, speculation, technology and security risks. You also acknowledge that the cost of transacting on such technologies is variable and may increase at any time causing impact to any activities taking place on the Ethereum blockchain. You acknowledge these risks and represent that MetaMask cannot be held liable for such fluctuations or increased costs.
-### Risk of Temporary Network Incoherence
+### 5.6 Application Security ###
-METAMASK is not liable for unavoidable casualty, delays in delivery of materials, embargoes, government orders, acts of civil or military authorities, lack of energy, sickness or other ill health, injurious dance parties, rancid bacon, acts of Gods, Nymphs, Angels (fallen or otherwise) or Djinn, or any similarly unforeseen events that render misfortune.
+You acknowledge that Ethereum applications are code subject to flaws and acknowledge that you are solely responsible for evaluating any code provided by the Services or Content and the trustworthiness of any third-party websites, products, smart-contracts, or Content you access or use through the Service. You further expressly acknowledge and represent that Ethereum applications can be written maliciously or negligently, that MetaMask cannot be held liable for your interaction with such applications and that such applications may cause the loss of property or even identity. This warning and others later provided by MetaMask in no way evidence or represent an on-going duty to alert you to all of the potential risks of utilizing the Service or Content.
-The METAMASK DEVELOPERS wish the very best luck in creating a better life for yourself, those you love, and your greater community, the world.
+## 6. Indemnity ##
+
+You agree to release and to indemnify, defend and hold harmless MetaMask and its parents, subsidiaries, affiliates and agencies, as well as the officers, directors, employees, shareholders and representatives of any of the foregoing entities, from and against any and all losses, liabilities, expenses, damages, costs (including attorneys’ fees and court costs) claims or actions of any kind whatsoever arising or resulting from your use of the Service, your violation of these Terms of Use, and any of your acts or omissions that implicate publicity rights, defamation or invasion of privacy. MetaMask reserves the right, at its own expense, to assume exclusive defense and control of any matter otherwise subject to indemnification by you and, in such case, you agree to cooperate with MetaMask in the defense of such matter.
+
+## 7. Limitation on liability ##
+
+YOU ACKNOWLEDGE AND AGREE THAT YOU ASSUME FULL RESPONSIBILITY FOR YOUR USE OF THE SITE AND SERVICE. YOU ACKNOWLEDGE AND AGREE THAT ANY INFORMATION YOU SEND OR RECEIVE DURING YOUR USE OF THE SITE AND SERVICE MAY NOT BE SECURE AND MAY BE INTERCEPTED OR LATER ACQUIRED BY UNAUTHORIZED PARTIES. YOU ACKNOWLEDGE AND AGREE THAT YOUR USE OF THE SITE AND SERVICE IS AT YOUR OWN RISK. RECOGNIZING SUCH, YOU UNDERSTAND AND AGREE THAT, TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, NEITHER METAMASK NOR ITS SUPPLIERS OR LICENSORS WILL BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY OR OTHER DAMAGES OF ANY KIND, INCLUDING WITHOUT LIMITATION DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER TANGIBLE OR INTANGIBLE LOSSES OR ANY OTHER DAMAGES BASED ON CONTRACT, TORT, STRICT LIABILITY OR ANY OTHER THEORY (EVEN IF METAMASK HAD BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES), RESULTING FROM THE SITE OR SERVICE; THE USE OR THE INABILITY TO USE THE SITE OR SERVICE; UNAUTHORIZED ACCESS TO OR ALTERATION OF YOUR TRANSMISSIONS OR DATA; STATEMENTS OR CONDUCT OF ANY THIRD PARTY ON THE SITE OR SERVICE; ANY ACTIONS WE TAKE OR FAIL TO TAKE AS A RESULT OF COMMUNICATIONS YOU SEND TO US; HUMAN ERRORS; TECHNICAL MALFUNCTIONS; FAILURES, INCLUDING PUBLIC UTILITY OR TELEPHONE OUTAGES; OMISSIONS, INTERRUPTIONS, LATENCY, DELETIONS OR DEFECTS OF ANY DEVICE OR NETWORK, PROVIDERS, OR SOFTWARE (INCLUDING, BUT NOT LIMITED TO, THOSE THAT DO NOT PERMIT PARTICIPATION IN THE SERVICE); ANY INJURY OR DAMAGE TO COMPUTER EQUIPMENT; INABILITY TO FULLY ACCESS THE SITE OR SERVICE OR ANY OTHER WEBSITE; THEFT, TAMPERING, DESTRUCTION, OR UNAUTHORIZED ACCESS TO, IMAGES OR OTHER CONTENT OF ANY KIND; DATA THAT IS PROCESSED LATE OR INCORRECTLY OR IS INCOMPLETE OR LOST; TYPOGRAPHICAL, PRINTING OR OTHER ERRORS, OR ANY COMBINATION THEREOF; OR ANY OTHER MATTER RELATING TO THE SITE OR SERVICE.
+
+SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.
+
+## 8. Our Proprietary Rights ##
+
+All title, ownership and intellectual property rights in and to the Service are owned by MetaMask or its licensors. You acknowledge and agree that the Service contains proprietary and confidential information that is protected by applicable intellectual property and other laws. Except as expressly authorized by MetaMask, you agree not to copy, modify, rent, lease, loan, sell, distribute, perform, display or create derivative works based on the Service, in whole or in part. MetaMask issues a license for MetaMask, found [here](https://github.com/MetaMask/metamask-plugin/blob/master/LICENSE). For information on other licenses utilized in the development of MetaMask, please see our attribution page at: [https://metamask.io/attributions.html](https://metamask.io/attributions.html)
+
+## 9. Links ##
+
+The Service provides, or third parties may provide, links to other World Wide Web or accessible sites, applications or resources. Because MetaMask has no control over such sites, applications and resources, you acknowledge and agree that MetaMask is not responsible for the availability of such external sites, applications or resources, and does not endorse and is not responsible or liable for any content, advertising, products or other materials on or available from such sites or resources. You further acknowledge and agree that MetaMask shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such site or resource.
+
+## 10. Termination and Suspension ##
+
+MetaMask may terminate or suspend all or part of the Service and your MetaMask access immediately, without prior notice or liability, if you breach any of the terms or conditions of the Terms. Upon termination of your access, your right to use the Service will immediately cease.
+
+The following provisions of the Terms survive any termination of these Terms: INDEMNITY; WARRANTY DISCLAIMERS; LIMITATION ON LIABILITY; OUR PROPRIETARY RIGHTS; LINKS; TERMINATION; NO THIRD PARTY BENEFICIARIES; BINDING ARBITRATION AND CLASS ACTION WAIVER; GENERAL INFORMATION.
+
+## 11. No Third Party Beneficiaries ##
+
+You agree that, except as otherwise expressly provided in these Terms, there shall be no third party beneficiaries to the Terms.
+
+## 12. Notice and Procedure For Making Claims of Copyright Infringement ##
+
+If you believe that your copyright or the copyright of a person on whose behalf you are authorized to act has been infringed, please provide MetaMask’s Copyright Agent a written Notice containing the following information:
+
+· an electronic or physical signature of the person authorized to act on behalf of the owner of the copyright or other intellectual property interest;
+
+· a description of the copyrighted work or other intellectual property that you claim has been infringed;
+
+· a description of where the material that you claim is infringing is located on the Service;
+
+· your address, telephone number, and email address;
+
+· a statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agent, or the law;
+
+· a statement by you, made under penalty of perjury, that the above information in your Notice is accurate and that you are the copyright or intellectual property owner or authorized to act on the copyright or intellectual property owner's behalf.
+
+MetaMask’s Copyright Agent can be reached at:
+
+Email: copyright [at] metamask [dot] io
+
+Mail:
+
+Attention:
+
+MetaMask Copyright ℅ ConsenSys
+
+49 Bogart Street
+
+Brooklyn, NY 11206
+
+## 13. Binding Arbitration and Class Action Waiver ##
+
+PLEASE READ THIS SECTION CAREFULLY – IT MAY SIGNIFICANTLY AFFECT YOUR LEGAL RIGHTS, INCLUDING YOUR RIGHT TO FILE A LAWSUIT IN COURT
+
+### 13.1 Initial Dispute Resolution ###
+
+The parties shall use their best efforts to engage directly to settle any dispute, claim, question, or disagreement and engage in good faith negotiations which shall be a condition to either party initiating a lawsuit or arbitration.
+
+### 13.2 Binding Arbitration ###
+
+If the parties do not reach an agreed upon solution within a period of 30 days from the time informal dispute resolution under the Initial Dispute Resolution provision begins, then either party may initiate binding arbitration as the sole means to resolve claims, subject to the terms set forth below. Specifically, all claims arising out of or relating to these Terms (including their formation, performance and breach), the parties’ relationship with each other and/or your use of the Service shall be finally settled by binding arbitration administered by the American Arbitration Association in accordance with the provisions of its Commercial Arbitration Rules and the supplementary procedures for consumer related disputes of the American Arbitration Association (the "AAA"), excluding any rules or procedures governing or permitting class actions.
+
+The arbitrator, and not any federal, state or local court or agency, shall have exclusive authority to resolve all disputes arising out of or relating to the interpretation, applicability, enforceability or formation of these Terms, including, but not limited to any claim that all or any part of these Terms are void or voidable, or whether a claim is subject to arbitration. The arbitrator shall be empowered to grant whatever relief would be available in a court under law or in equity. The arbitrator’s award shall be written, and binding on the parties and may be entered as a judgment in any court of competent jurisdiction.
+
+The parties understand that, absent this mandatory provision, they would have the right to sue in court and have a jury trial. They further understand that, in some instances, the costs of arbitration could exceed the costs of litigation and the right to discovery may be more limited in arbitration than in court.
+
+### 13.3 Location ###
+
+Binding arbitration shall take place in New York. You agree to submit to the personal jurisdiction of any federal or state court in New York County, New York, in order to compel arbitration, to stay proceedings pending arbitration, or to confirm, modify, vacate or enter judgment on the award entered by the arbitrator.
+
+### 13.4 Class Action Waiver ###
+
+The parties further agree that any arbitration shall be conducted in their individual capacities only and not as a class action or other representative action, and the parties expressly waive their right to file a class action or seek relief on a class basis. YOU AND METAMASK AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. If any court or arbitrator determines that the class action waiver set forth in this paragraph is void or unenforceable for any reason or that an arbitration can proceed on a class basis, then the arbitration provision set forth above shall be deemed null and void in its entirety and the parties shall be deemed to have not agreed to arbitrate disputes.
+
+### 13.5 Exception - Litigation of Intellectual Property and Small Claims Court Claims ###
+
+Notwithstanding the parties' decision to resolve all disputes through arbitration, either party may bring an action in state or federal court to protect its intellectual property rights ("intellectual property rights" means patents, copyrights, moral rights, trademarks, and trade secrets, but not privacy or publicity rights). Either party may also seek relief in a small claims court for disputes or claims within the scope of that court’s jurisdiction.
+
+### 13.6 30-Day Right to Opt Out ###
+
+You have the right to opt-out and not be bound by the arbitration and class action waiver provisions set forth above by sending written notice of your decision to opt-out to the following address: MetaMask ℅ ConsenSys, 49 Bogart Street, Brooklyn NY 11206 and via email at legal-opt@metamask.io. The notice must be sent within 30 days of September 6, 2016 or your first use of the Service, whichever is later, otherwise you shall be bound to arbitrate disputes in accordance with the terms of those paragraphs. If you opt-out of these arbitration provisions, MetaMask also will not be bound by them.
+
+### 13.7 Changes to This Section ###
+
+MetaMask will provide 60-days’ notice of any changes to this section. Changes will become effective on the 60th day, and will apply prospectively only to any claims arising after the 60th day.
+
+For any dispute not subject to arbitration you and MetaMask agree to submit to the personal and exclusive jurisdiction of and venue in the federal and state courts located in New York, New York. You further agree to accept service of process by mail, and hereby waive any and all jurisdictional and venue defenses otherwise available.
+
+The Terms and the relationship between you and MetaMask shall be governed by the laws of the State of New York without regard to conflict of law provisions.
+
+## 14. General Information ##
+
+### 14.1 Entire Agreement ###
+
+These Terms (and any additional terms, rules and conditions of participation that MetaMask may post on the Service) constitute the entire agreement between you and MetaMask with respect to the Service and supersedes any prior agreements, oral or written, between you and MetaMask. In the event of a conflict between these Terms and the additional terms, rules and conditions of participation, the latter will prevail over the Terms to the extent of the conflict.
+
+### 14.2 Waiver and Severability of Terms ###
+
+The failure of MetaMask to exercise or enforce any right or provision of the Terms shall not constitute a waiver of such right or provision. If any provision of the Terms is found by an arbitrator or court of competent jurisdiction to be invalid, the parties nevertheless agree that the arbitrator or court should endeavor to give effect to the parties' intentions as reflected in the provision, and the other provisions of the Terms remain in full force and effect.
+
+### 14.3 Statute of Limitations ###
+
+You agree that regardless of any statute or law to the contrary, any claim or cause of action arising out of or related to the use of the Service or the Terms must be filed within one (1) year after such claim or cause of action arose or be forever barred.
+
+### 14.4 Section Titles ###
+
+The section titles in the Terms are for convenience only and have no legal or contractual effect.
+
+### 14.5 Communications ###
+
+Users with questions, complaints or claims with respect to the Service may contact us using the relevant contact information set forth above and at communications@metamask.io.
+
+## 15 Related Links ##
+
+**[Terms of Use](https://metamask.io/terms.html)**
+
+**[Privacy](https://metamask.io/privacy.html)**
+
+**[Attributions](https://metamask.io/attributions.html)**
diff --git a/app/images/icon-32.png b/app/images/icon-32.png
new file mode 100644
index 000000000..f801ebb6b
--- /dev/null
+++ b/app/images/icon-32.png
Binary files differ
diff --git a/app/images/icon-64.png b/app/images/icon-64.png
new file mode 100644
index 000000000..b3019ad65
--- /dev/null
+++ b/app/images/icon-64.png
Binary files differ
diff --git a/app/manifest.json b/app/manifest.json
index e5e08c4b6..95475ddf1 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,15 +1,16 @@
{
"name": "MetaMask",
"short_name": "Metamask",
- "version": "2.9.2",
+ "version": "3.2.2",
"manifest_version": 2,
+ "author": "https://metamask.io",
"description": "Ethereum Browser Extension",
"commands": {
"_execute_browser_action": {
"suggested_key": {
"windows": "Alt+Shift+M",
"mac": "Alt+Shift+M",
- "chromeos": "Search+M",
+ "chromeos": "Alt+Shift+M",
"linux": "Alt+Shift+M"
}
}
@@ -28,7 +29,8 @@
"scripts": [
"scripts/chromereload.js",
"scripts/background.js"
- ]
+ ],
+ "persistent": true
},
"browser_action": {
"default_icon": {
@@ -53,9 +55,8 @@
}
],
"permissions": [
- "notifications",
"storage",
- "tabs",
+ "clipboardWrite",
"http://localhost:8545/"
],
"web_accessible_resources": [
diff --git a/app/notification.html b/app/notification.html
new file mode 100644
index 000000000..cc485da7f
--- /dev/null
+++ b/app/notification.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>MetaMask Notification</title>
+ <style>
+ body {
+ overflow: hidden;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="app-content"></div>
+ <script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script>
+ </body>
+</html>
diff --git a/app/scripts/account-import-strategies/index.js b/app/scripts/account-import-strategies/index.js
new file mode 100644
index 000000000..d5124eb7f
--- /dev/null
+++ b/app/scripts/account-import-strategies/index.js
@@ -0,0 +1,45 @@
+const Wallet = require('ethereumjs-wallet')
+const importers = require('ethereumjs-wallet/thirdparty')
+const ethUtil = require('ethereumjs-util')
+
+const accountImporter = {
+
+ importAccount(strategy, args) {
+ try {
+ const importer = this.strategies[strategy]
+ const privateKeyHex = importer.apply(null, args)
+ return Promise.resolve(privateKeyHex)
+ } catch (e) {
+ return Promise.reject(e)
+ }
+ },
+
+ strategies: {
+ 'Private Key': (privateKey) => {
+ const stripped = ethUtil.stripHexPrefix(privateKey)
+ return stripped
+ },
+ 'JSON File': (input, password) => {
+ let wallet
+ try {
+ wallet = importers.fromEtherWallet(input, password)
+ } catch (e) {
+ console.log('Attempt to import as EtherWallet format failed, trying V3...')
+ }
+
+ if (!wallet) {
+ wallet = Wallet.fromV3(input, password, true)
+ }
+
+ return walletToPrivateKey(wallet)
+ },
+ },
+
+}
+
+function walletToPrivateKey (wallet) {
+ const privateKeyBuffer = wallet.getPrivateKey()
+ return ethUtil.bufferToHex(privateKeyBuffer)
+}
+
+module.exports = accountImporter
diff --git a/app/scripts/background.js b/app/scripts/background.js
index e04309e74..2e5a992b9 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -1,192 +1,147 @@
const urlUtil = require('url')
-const extend = require('xtend')
-const Dnode = require('dnode')
-const eos = require('end-of-stream')
+const endOfStream = require('end-of-stream')
+const asyncQ = require('async-q')
+const pipe = require('pump')
+const LocalStorageStore = require('obs-store/lib/localStorage')
+const storeTransform = require('obs-store/lib/transform')
+const Migrator = require('./lib/migrator/')
+const migrations = require('./migrations/')
const PortStream = require('./lib/port-stream.js')
-const createUnlockRequestNotification = require('./lib/notifications.js').createUnlockRequestNotification
-const createTxNotification = require('./lib/notifications.js').createTxNotification
-const createMsgNotification = require('./lib/notifications.js').createMsgNotification
-const messageManager = require('./lib/message-manager')
-const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
+const notification = require('./lib/notifications.js')
const MetamaskController = require('./metamask-controller')
const extension = require('./lib/extension')
+const firstTimeState = require('./first-time-state')
const STORAGE_KEY = 'metamask-config'
+const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+let popupIsOpen = false
-const controller = new MetamaskController({
- // User confirmation callbacks:
- showUnconfirmedMessage,
- unlockAccountMessage,
- showUnconfirmedTx,
- // Persistence Methods:
- setData,
- loadData,
-})
-const idStore = controller.idStore
+// state persistence
+const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
-function unlockAccountMessage () {
- createUnlockRequestNotification({
- title: 'Account Unlock Request',
- })
-}
+// initialization flow
+asyncQ.waterfall([
+ () => loadStateFromPersistence(),
+ (initState) => setupController(initState),
+])
+.then(() => console.log('MetaMask initialization complete.'))
+.catch((err) => { console.error(err) })
-function showUnconfirmedMessage (msgParams, msgId) {
- var controllerState = controller.getState()
+//
+// State and Persistence
+//
- createMsgNotification({
- imageifyIdenticons: false,
- txData: {
- msgParams: msgParams,
- time: (new Date()).getTime(),
+function loadStateFromPersistence() {
+ // migrations
+ let migrator = new Migrator({ migrations })
+ let initialState = migrator.generateInitialState(firstTimeState)
+ return asyncQ.waterfall([
+ // read from disk
+ () => Promise.resolve(diskStore.getState() || initialState),
+ // migrate data
+ (versionedData) => migrator.migrateData(versionedData),
+ // write to disk
+ (versionedData) => {
+ diskStore.putState(versionedData)
+ return Promise.resolve(versionedData)
},
- identities: controllerState.identities,
- accounts: controllerState.accounts,
- onConfirm: idStore.approveMessage.bind(idStore, msgId, noop),
- onCancel: idStore.cancelMessage.bind(idStore, msgId),
- })
+ // resolve to just data
+ (versionedData) => Promise.resolve(versionedData.data),
+ ])
}
-function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
- var controllerState = controller.getState()
+function setupController (initState) {
- createTxNotification({
- imageifyIdenticons: false,
- txData: {
- txParams: txParams,
- time: (new Date()).getTime(),
- },
- identities: controllerState.identities,
- accounts: controllerState.accounts,
- onConfirm: idStore.approveTransaction.bind(idStore, txData.id, noop),
- onCancel: idStore.cancelTransaction.bind(idStore, txData.id),
- })
-}
+ //
+ // MetaMask Controller
+ //
-//
-// connect to other contexts
-//
-
-extension.runtime.onConnect.addListener(connectRemote)
-function connectRemote (remotePort) {
- var isMetaMaskInternalProcess = (remotePort.name === 'popup')
- var portStream = new PortStream(remotePort)
- if (isMetaMaskInternalProcess) {
- // communication with popup
- setupTrustedCommunication(portStream, 'MetaMask')
- } else {
- // communication with page
- var originDomain = urlUtil.parse(remotePort.sender.url).hostname
- setupUntrustedCommunication(portStream, originDomain)
+ const controller = new MetamaskController({
+ // User confirmation callbacks:
+ showUnconfirmedMessage: triggerUi,
+ unlockAccountMessage: triggerUi,
+ showUnapprovedTx: triggerUi,
+ // initial state
+ initState,
+ })
+ global.metamaskController = controller
+
+ // setup state persistence
+ pipe(
+ controller.store,
+ storeTransform(versionifyData),
+ diskStore
+ )
+
+ function versionifyData(state) {
+ let versionedData = diskStore.getState()
+ versionedData.data = state
+ return versionedData
}
-}
-function setupUntrustedCommunication (connectionStream, originDomain) {
- // setup multiplexing
- var mx = setupMultiplex(connectionStream)
- // connect features
- controller.setupProviderConnection(mx.createStream('provider'), originDomain)
- controller.setupPublicConfig(mx.createStream('publicConfig'))
-}
+ //
+ // connect to other contexts
+ //
+
+ extension.runtime.onConnect.addListener(connectRemote)
+ function connectRemote (remotePort) {
+ var isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification'
+ var portStream = new PortStream(remotePort)
+ if (isMetaMaskInternalProcess) {
+ // communication with popup
+ popupIsOpen = popupIsOpen || (remotePort.name === 'popup')
+ controller.setupTrustedCommunication(portStream, 'MetaMask', remotePort.name)
+ // record popup as closed
+ if (remotePort.name === 'popup') {
+ endOfStream(portStream, () => {
+ popupIsOpen = false
+ })
+ }
+ } else {
+ // communication with page
+ var originDomain = urlUtil.parse(remotePort.sender.url).hostname
+ controller.setupUntrustedCommunication(portStream, originDomain)
+ }
+ }
-function setupTrustedCommunication (connectionStream, originDomain) {
- // setup multiplexing
- var mx = setupMultiplex(connectionStream)
- // connect features
- setupControllerConnection(mx.createStream('controller'))
- controller.setupProviderConnection(mx.createStream('provider'), originDomain)
-}
+ //
+ // User Interface setup
+ //
+
+ updateBadge()
+ controller.txManager.on('updateBadge', updateBadge)
+ controller.messageManager.on('updateBadge', updateBadge)
+
+ // plugin badge text
+ function updateBadge () {
+ var label = ''
+ var unapprovedTxCount = controller.txManager.unapprovedTxCount
+ var unapprovedMsgCount = controller.messageManager.unapprovedMsgCount
+ var count = unapprovedTxCount + unapprovedMsgCount
+ if (count) {
+ label = String(count)
+ }
+ extension.browserAction.setBadgeText({ text: label })
+ extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' })
+ }
-//
-// remote features
-//
+ return Promise.resolve()
-function setupControllerConnection (stream) {
- controller.stream = stream
- var api = controller.getApi()
- var dnode = Dnode(api)
- stream.pipe(dnode).pipe(stream)
- dnode.on('remote', (remote) => {
- // push updates to popup
- controller.ethStore.on('update', controller.sendUpdate.bind(controller))
- controller.remote = remote
- idStore.on('update', controller.sendUpdate.bind(controller))
-
- // teardown on disconnect
- eos(stream, () => {
- controller.ethStore.removeListener('update', controller.sendUpdate.bind(controller))
- })
- })
}
//
-// plugin badge text
+// Etc...
//
-idStore.on('update', updateBadge)
-
-function updateBadge (state) {
- var label = ''
- var unconfTxs = controller.configManager.unconfirmedTxs()
- var unconfTxLen = Object.keys(unconfTxs).length
- var unconfMsgs = messageManager.unconfirmedMsgs()
- var unconfMsgLen = Object.keys(unconfMsgs).length
- var count = unconfTxLen + unconfMsgLen
- if (count) {
- label = String(count)
- }
- extension.browserAction.setBadgeText({ text: label })
- extension.browserAction.setBadgeBackgroundColor({ color: '#506F8B' })
+// popup trigger
+function triggerUi () {
+ if (!popupIsOpen) notification.show()
}
-function loadData () {
- var oldData = getOldStyleData()
- var newData
- try {
- newData = JSON.parse(window.localStorage[STORAGE_KEY])
- } catch (e) {}
-
- var data = extend({
- meta: {
- version: 0,
- },
- data: {
- config: {
- provider: {
- type: 'testnet',
- },
- },
- },
- }, oldData || null, newData || null)
- return data
-}
-
-function getOldStyleData () {
- var config, wallet, seedWords
-
- var result = {
- meta: { version: 0 },
- data: {},
+// On first install, open a window to MetaMask website to how-it-works.
+extension.runtime.onInstalled.addListener(function (details) {
+ if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
+ extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
}
-
- try {
- config = JSON.parse(window.localStorage['config'])
- result.data.config = config
- } catch (e) {}
- try {
- wallet = JSON.parse(window.localStorage['lightwallet'])
- result.data.wallet = wallet
- } catch (e) {}
- try {
- seedWords = window.localStorage['seedWords']
- result.data.seedWords = seedWords
- } catch (e) {}
-
- return result
-}
-
-function setData (data) {
- window.localStorage[STORAGE_KEY] = JSON.stringify(data)
-}
-
-function noop () {}
+})
diff --git a/app/scripts/chromereload.js b/app/scripts/chromereload.js
index 88333ba8a..f0bae403c 100644
--- a/app/scripts/chromereload.js
+++ b/app/scripts/chromereload.js
@@ -324,13 +324,13 @@ window.LiveReloadOptions = { host: 'localhost' };
this.pluginIdentifiers = {}
this.console = this.window.console && this.window.console.log && this.window.console.error ? this.window.location.href.match(/LR-verbose/) ? this.window.console : {
log: function () {},
- error: this.window.console.error.bind(this.window.console),
+ error: console.error,
} : {
log: function () {},
error: function () {},
}
if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) {
- this.console.error('LiveReload disabled because the browser does not seem to support web sockets')
+ console.error('LiveReload disabled because the browser does not seem to support web sockets')
return
}
if ('LiveReloadOptions' in window) {
@@ -344,7 +344,7 @@ window.LiveReloadOptions = { host: 'localhost' };
} else {
this.options = Options.extract(this.window.document)
if (!this.options) {
- this.console.error('LiveReload disabled because it could not find its own <SCRIPT> tag')
+ console.error('LiveReload disabled because it could not find its own <SCRIPT> tag')
return
}
}
diff --git a/app/scripts/config.js b/app/scripts/config.js
index 04e2907d4..b4541a04a 100644
--- a/app/scripts/config.js
+++ b/app/scripts/config.js
@@ -1,13 +1,14 @@
-const MAINET_RPC_URL = 'https://mainnet.infura.io/'
-const TESTNET_RPC_URL = 'https://morden.infura.io/'
+const MAINET_RPC_URL = 'https://mainnet.infura.io/metamask'
+const TESTNET_RPC_URL = 'https://ropsten.infura.io/metamask'
const DEFAULT_RPC_URL = TESTNET_RPC_URL
-global.METAMASK_DEBUG = false
+global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
module.exports = {
network: {
default: DEFAULT_RPC_URL,
mainnet: MAINET_RPC_URL,
testnet: TESTNET_RPC_URL,
+ morden: TESTNET_RPC_URL,
},
}
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index de2cf263b..ab64dc9fa 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -1,11 +1,12 @@
const LocalMessageDuplexStream = require('post-message-stream')
+const PongStream = require('ping-pong-stream/pong')
const PortStream = require('./lib/port-stream.js')
const ObjectMultiplex = require('./lib/obj-multiplex')
const extension = require('./lib/extension')
const fs = require('fs')
const path = require('path')
-const inpageText = fs.readFileSync(path.join(__dirname + '/inpage.js')).toString()
+const inpageText = fs.readFileSync(path.join(__dirname, 'inpage.js')).toString()
// Eventually this streaming injection could be replaced with:
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.exportFunction
@@ -19,9 +20,8 @@ if (shouldInjectWeb3()) {
setupStreams()
}
-function setupInjection(){
+function setupInjection () {
try {
-
// inject in-page script
var scriptTag = document.createElement('script')
scriptTag.src = extension.extension.getURL('scripts/inpage.js')
@@ -30,41 +30,53 @@ function setupInjection(){
var container = document.head || document.documentElement
// append as first child
container.insertBefore(scriptTag, container.children[0])
-
} catch (e) {
console.error('Metamask injection failed.', e)
}
}
-function setupStreams(){
-
+function setupStreams () {
// setup communication to page and plugin
var pageStream = new LocalMessageDuplexStream({
name: 'contentscript',
target: 'inpage',
})
- pageStream.on('error', console.error.bind(console))
+ pageStream.on('error', console.error)
var pluginPort = extension.runtime.connect({name: 'contentscript'})
var pluginStream = new PortStream(pluginPort)
- pluginStream.on('error', console.error.bind(console))
+ pluginStream.on('error', console.error)
// forward communication plugin->inpage
pageStream.pipe(pluginStream).pipe(pageStream)
- // connect contentscript->inpage reload stream
+ // setup local multistream channels
var mx = ObjectMultiplex()
- mx.on('error', console.error.bind(console))
- mx.pipe(pageStream)
- var reloadStream = mx.createStream('reload')
- reloadStream.on('error', console.error.bind(console))
+ mx.on('error', console.error)
+ mx.pipe(pageStream).pipe(mx)
- // if we lose connection with the plugin, trigger tab refresh
- pluginStream.on('close', function () {
- reloadStream.write({ method: 'reset' })
- })
+ // connect ping stream
+ var pongStream = new PongStream({ objectMode: true })
+ pongStream.pipe(mx.createStream('pingpong')).pipe(pongStream)
+
+ // ignore unused channels (handled by background)
+ mx.ignoreStream('provider')
+ mx.ignoreStream('publicConfig')
+ mx.ignoreStream('reload')
+}
+
+function shouldInjectWeb3 () {
+ return isAllowedSuffix(window.location.href)
}
-function shouldInjectWeb3(){
- var shouldInject = (window.location.href.indexOf('.pdf') === -1)
- return shouldInject
+function isAllowedSuffix (testCase) {
+ var prohibitedTypes = ['xml', 'pdf']
+ var currentUrl = window.location.href
+ var currentRegex
+ for (let i = 0; i < prohibitedTypes.length; i++) {
+ currentRegex = new RegExp(`\.${prohibitedTypes[i]}$`)
+ if (currentRegex.test(currentUrl)) {
+ return false
+ }
+ }
+ return true
}
diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js
new file mode 100644
index 000000000..3196981ba
--- /dev/null
+++ b/app/scripts/first-time-state.js
@@ -0,0 +1,11 @@
+//
+// The default state of MetaMask
+//
+
+module.exports = {
+ config: {
+ provider: {
+ type: 'testnet',
+ },
+ },
+} \ No newline at end of file
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index 28a1223ac..419f78cd6 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -2,6 +2,8 @@
cleanContextForImports()
require('web3/dist/web3.min.js')
const LocalMessageDuplexStream = require('post-message-stream')
+// const PingStream = require('ping-pong-stream/ping')
+// const endOfStream = require('end-of-stream')
const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
restoreContextAfterImports()
@@ -29,15 +31,26 @@ web3.setProvider = function () {
console.log('MetaMask - overrode web3.setProvider')
}
console.log('MetaMask - injected web3')
+// export global web3, with usage-detection reload fn
+var triggerReload = setupDappAutoReload(web3)
-//
-// export global web3 with auto dapp reload
-//
-
+// listen for reset requests from metamask
var reloadStream = inpageProvider.multiStream.createStream('reload')
-setupDappAutoReload(web3, reloadStream)
+reloadStream.once('data', triggerReload)
+
+// setup ping timeout autoreload
+// LocalMessageDuplexStream does not self-close, so reload if pingStream fails
+// var pingChannel = inpageProvider.multiStream.createStream('pingpong')
+// var pingStream = new PingStream({ objectMode: true })
+// wait for first successful reponse
+
+// disable pingStream until https://github.com/MetaMask/metamask-plugin/issues/746 is resolved more gracefully
+// metamaskStream.once('data', function(){
+// pingStream.pipe(pingChannel).pipe(pingStream)
+// })
+// endOfStream(pingStream, triggerReload)
-// set web3 defaultAcount
+// set web3 defaultAccount
inpageProvider.publicConfigStore.subscribe(function (state) {
web3.eth.defaultAccount = state.selectedAddress
})
diff --git a/app/scripts/keyring-controller.js b/app/scripts/keyring-controller.js
new file mode 100644
index 000000000..20af221ff
--- /dev/null
+++ b/app/scripts/keyring-controller.js
@@ -0,0 +1,554 @@
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const bip39 = require('bip39')
+const EventEmitter = require('events').EventEmitter
+const ObservableStore = require('obs-store')
+const filter = require('promise-filter')
+const encryptor = require('browser-passworder')
+const normalizeAddress = require('./lib/sig-util').normalize
+// Keyrings:
+const SimpleKeyring = require('./keyrings/simple')
+const HdKeyring = require('./keyrings/hd')
+const keyringTypes = [
+ SimpleKeyring,
+ HdKeyring,
+]
+
+class KeyringController extends EventEmitter {
+
+ // PUBLIC METHODS
+ //
+ // THE FIRST SECTION OF METHODS ARE PUBLIC-FACING,
+ // MEANING THEY ARE USED BY CONSUMERS OF THIS CLASS.
+ //
+ // THEIR SURFACE AREA SHOULD BE CHANGED WITH GREAT CARE.
+
+ constructor (opts) {
+ super()
+ const initState = opts.initState || {}
+ this.keyringTypes = keyringTypes
+ this.store = new ObservableStore(initState)
+ this.memStore = new ObservableStore({
+ isUnlocked: false,
+ keyringTypes: this.keyringTypes.map(krt => krt.type),
+ keyrings: [],
+ identities: {},
+ })
+ this.ethStore = opts.ethStore
+ this.encryptor = encryptor
+ this.keyrings = []
+ this.getNetwork = opts.getNetwork
+ }
+
+ // Full Update
+ // returns Promise( @object state )
+ //
+ // Emits the `update` event and
+ // returns a Promise that resolves to the current state.
+ //
+ // Frequently used to end asynchronous chains in this class,
+ // indicating consumers can often either listen for updates,
+ // or accept a state-resolving promise to consume their results.
+ //
+ // Not all methods end with this, that might be a nice refactor.
+ fullUpdate () {
+ this.emit('update')
+ return Promise.resolve(this.memStore.getState())
+ }
+
+ // Create New Vault And Keychain
+ // @string password - The password to encrypt the vault with
+ //
+ // returns Promise( @object state )
+ //
+ // Destroys any old encrypted storage,
+ // creates a new encrypted store with the given password,
+ // randomly creates a new HD wallet with 1 account,
+ // faucets that account on the testnet.
+ createNewVaultAndKeychain (password) {
+ return this.persistAllKeyrings(password)
+ .then(this.createFirstKeyTree.bind(this))
+ .then(this.fullUpdate.bind(this))
+ }
+
+ // CreateNewVaultAndRestore
+ // @string password - The password to encrypt the vault with
+ // @string seed - The BIP44-compliant seed phrase.
+ //
+ // returns Promise( @object state )
+ //
+ // Destroys any old encrypted storage,
+ // creates a new encrypted store with the given password,
+ // creates a new HD wallet from the given seed with 1 account.
+ createNewVaultAndRestore (password, seed) {
+ if (typeof password !== 'string') {
+ return Promise.reject('Password must be text.')
+ }
+
+ if (!bip39.validateMnemonic(seed)) {
+ return Promise.reject('Seed phrase is invalid.')
+ }
+
+ this.clearKeyrings()
+
+ return this.persistAllKeyrings(password)
+ .then(() => {
+ return this.addNewKeyring('HD Key Tree', {
+ mnemonic: seed,
+ numberOfAccounts: 1,
+ })
+ })
+ .then((firstKeyring) => {
+ return firstKeyring.getAccounts()
+ })
+ .then((accounts) => {
+ const firstAccount = accounts[0]
+ if (!firstAccount) throw new Error('KeyringController - First Account not found.')
+ const hexAccount = normalizeAddress(firstAccount)
+ this.emit('newAccount', hexAccount)
+ return this.setupAccounts(accounts)
+ })
+ .then(this.persistAllKeyrings.bind(this, password))
+ .then(this.fullUpdate.bind(this))
+ }
+
+ // Set Locked
+ // returns Promise( @object state )
+ //
+ // This method deallocates all secrets, and effectively locks metamask.
+ setLocked () {
+ // set locked
+ this.password = null
+ this.memStore.updateState({ isUnlocked: false })
+ // remove keyrings
+ this.keyrings = []
+ this._updateMemStoreKeyrings()
+ return this.fullUpdate()
+ }
+
+ // Submit Password
+ // @string password
+ //
+ // returns Promise( @object state )
+ //
+ // Attempts to decrypt the current vault and load its keyrings
+ // into memory.
+ //
+ // Temporarily also migrates any old-style vaults first, as well.
+ // (Pre MetaMask 3.0.0)
+ submitPassword (password) {
+ return this.unlockKeyrings(password)
+ .then((keyrings) => {
+ this.keyrings = keyrings
+ return this.fullUpdate()
+ })
+ }
+
+ // Add New Keyring
+ // @string type
+ // @object opts
+ //
+ // returns Promise( @Keyring keyring )
+ //
+ // Adds a new Keyring of the given `type` to the vault
+ // and the current decrypted Keyrings array.
+ //
+ // All Keyring classes implement a unique `type` string,
+ // and this is used to retrieve them from the keyringTypes array.
+ addNewKeyring (type, opts) {
+ const Keyring = this.getKeyringClassForType(type)
+ const keyring = new Keyring(opts)
+ return keyring.deserialize(opts)
+ .then(() => {
+ return keyring.getAccounts()
+ })
+ .then((accounts) => {
+ this.keyrings.push(keyring)
+ return this.setupAccounts(accounts)
+ })
+ .then(() => this.persistAllKeyrings())
+ .then(() => this.fullUpdate())
+ .then(() => {
+ return keyring
+ })
+ }
+
+ // Add New Account
+ // @number keyRingNum
+ //
+ // returns Promise( @object state )
+ //
+ // Calls the `addAccounts` method on the Keyring
+ // in the kryings array at index `keyringNum`,
+ // and then saves those changes.
+ addNewAccount (selectedKeyring) {
+ return selectedKeyring.addAccounts(1)
+ .then(this.setupAccounts.bind(this))
+ .then(this.persistAllKeyrings.bind(this))
+ .then(this.fullUpdate.bind(this))
+ }
+
+ // Save Account Label
+ // @string account
+ // @string label
+ //
+ // returns Promise( @string label )
+ //
+ // Persists a nickname equal to `label` for the specified account.
+ saveAccountLabel (account, label) {
+ try {
+ const hexAddress = normalizeAddress(account)
+ // update state on diskStore
+ const state = this.store.getState()
+ const walletNicknames = state.walletNicknames || {}
+ walletNicknames[hexAddress] = label
+ this.store.updateState({ walletNicknames })
+ // update state on memStore
+ const identities = this.memStore.getState().identities
+ identities[hexAddress].name = label
+ this.memStore.updateState({ identities })
+ return Promise.resolve(label)
+ } catch (err) {
+ return Promise.reject(err)
+ }
+ }
+
+ // Export Account
+ // @string address
+ //
+ // returns Promise( @string privateKey )
+ //
+ // Requests the private key from the keyring controlling
+ // the specified address.
+ //
+ // Returns a Promise that may resolve with the private key string.
+ exportAccount (address) {
+ try {
+ return this.getKeyringForAccount(address)
+ .then((keyring) => {
+ return keyring.exportAccount(normalizeAddress(address))
+ })
+ } catch (e) {
+ return Promise.reject(e)
+ }
+ }
+
+
+ // SIGNING METHODS
+ //
+ // This method signs tx and returns a promise for
+ // TX Manager to update the state after signing
+
+ signTransaction (ethTx, _fromAddress) {
+ const fromAddress = normalizeAddress(_fromAddress)
+ return this.getKeyringForAccount(fromAddress)
+ .then((keyring) => {
+ return keyring.signTransaction(fromAddress, ethTx)
+ })
+ }
+
+ // Sign Message
+ // @object msgParams
+ //
+ // returns Promise(@buffer rawSig)
+ //
+ // Attempts to sign the provided @object msgParams.
+ signMessage (msgParams) {
+ const address = normalizeAddress(msgParams.from)
+ return this.getKeyringForAccount(address)
+ .then((keyring) => {
+ return keyring.signMessage(address, msgParams.data)
+ })
+ }
+
+ // PRIVATE METHODS
+ //
+ // THESE METHODS ARE ONLY USED INTERNALLY TO THE KEYRING-CONTROLLER
+ // AND SO MAY BE CHANGED MORE LIBERALLY THAN THE ABOVE METHODS.
+
+ // Create First Key Tree
+ // returns @Promise
+ //
+ // Clears the vault,
+ // creates a new one,
+ // creates a random new HD Keyring with 1 account,
+ // makes that account the selected account,
+ // faucets that account on testnet,
+ // puts the current seed words into the state tree.
+ createFirstKeyTree () {
+ this.clearKeyrings()
+ return this.addNewKeyring('HD Key Tree', { numberOfAccounts: 1 })
+ .then((keyring) => {
+ return keyring.getAccounts()
+ })
+ .then((accounts) => {
+ const firstAccount = accounts[0]
+ if (!firstAccount) throw new Error('KeyringController - No account found on keychain.')
+ const hexAccount = normalizeAddress(firstAccount)
+ this.emit('newAccount', hexAccount)
+ return this.setupAccounts(accounts)
+ })
+ .then(this.persistAllKeyrings.bind(this))
+ }
+
+ // Setup Accounts
+ // @array accounts
+ //
+ // returns @Promise(@object account)
+ //
+ // Initializes the provided account array
+ // Gives them numerically incremented nicknames,
+ // and adds them to the ethStore for regular balance checking.
+ setupAccounts (accounts) {
+ return this.getAccounts()
+ .then((loadedAccounts) => {
+ const arr = accounts || loadedAccounts
+ return Promise.all(arr.map((account) => {
+ return this.getBalanceAndNickname(account)
+ }))
+ })
+ }
+
+ // Get Balance And Nickname
+ // @string account
+ //
+ // returns Promise( @string label )
+ //
+ // Takes an account address and an iterator representing
+ // the current number of named accounts.
+ getBalanceAndNickname (account) {
+ if (!account) {
+ throw new Error('Problem loading account.')
+ }
+ const address = normalizeAddress(account)
+ this.ethStore.addAccount(address)
+ return this.createNickname(address)
+ }
+
+ // Create Nickname
+ // @string address
+ //
+ // returns Promise( @string label )
+ //
+ // Takes an address, and assigns it an incremented nickname, persisting it.
+ createNickname (address) {
+ const hexAddress = normalizeAddress(address)
+ const identities = this.memStore.getState().identities
+ const currentIdentityCount = Object.keys(identities).length + 1
+ const nicknames = this.store.getState().walletNicknames || {}
+ const existingNickname = nicknames[hexAddress]
+ const name = existingNickname || `Account ${currentIdentityCount}`
+ identities[hexAddress] = {
+ address: hexAddress,
+ name,
+ }
+ this.memStore.updateState({ identities })
+ return this.saveAccountLabel(hexAddress, name)
+ }
+
+ // Persist All Keyrings
+ // @password string
+ //
+ // returns Promise
+ //
+ // Iterates the current `keyrings` array,
+ // serializes each one into a serialized array,
+ // encrypts that array with the provided `password`,
+ // and persists that encrypted string to storage.
+ persistAllKeyrings (password = this.password) {
+ if (typeof password === 'string') {
+ this.password = password
+ this.memStore.updateState({ isUnlocked: true })
+ }
+ return Promise.all(this.keyrings.map((keyring) => {
+ return Promise.all([keyring.type, keyring.serialize()])
+ .then((serializedKeyringArray) => {
+ // Label the output values on each serialized Keyring:
+ return {
+ type: serializedKeyringArray[0],
+ data: serializedKeyringArray[1],
+ }
+ })
+ }))
+ .then((serializedKeyrings) => {
+ return this.encryptor.encrypt(this.password, serializedKeyrings)
+ })
+ .then((encryptedString) => {
+ this.store.updateState({ vault: encryptedString })
+ return true
+ })
+ }
+
+ // Unlock Keyrings
+ // @string password
+ //
+ // returns Promise( @array keyrings )
+ //
+ // Attempts to unlock the persisted encrypted storage,
+ // initializing the persisted keyrings to RAM.
+ unlockKeyrings (password) {
+ const encryptedVault = this.store.getState().vault
+ if (!encryptedVault) {
+ throw new Error('Cannot unlock without a previous vault.')
+ }
+
+ return this.encryptor.decrypt(password, encryptedVault)
+ .then((vault) => {
+ this.password = password
+ this.memStore.updateState({ isUnlocked: true })
+ vault.forEach(this.restoreKeyring.bind(this))
+ return this.keyrings
+ })
+ }
+
+ // Restore Keyring
+ // @object serialized
+ //
+ // returns Promise( @Keyring deserialized )
+ //
+ // Attempts to initialize a new keyring from the provided
+ // serialized payload.
+ //
+ // On success, returns the resulting @Keyring instance.
+ restoreKeyring (serialized) {
+ const { type, data } = serialized
+
+ const Keyring = this.getKeyringClassForType(type)
+ const keyring = new Keyring()
+ return keyring.deserialize(data)
+ .then(() => {
+ return keyring.getAccounts()
+ })
+ .then((accounts) => {
+ return this.setupAccounts(accounts)
+ })
+ .then(() => {
+ this.keyrings.push(keyring)
+ this._updateMemStoreKeyrings()
+ return keyring
+ })
+ }
+
+ // Get Keyring Class For Type
+ // @string type
+ //
+ // Returns @class Keyring
+ //
+ // Searches the current `keyringTypes` array
+ // for a Keyring class whose unique `type` property
+ // matches the provided `type`,
+ // returning it if it exists.
+ getKeyringClassForType (type) {
+ return this.keyringTypes.find(kr => kr.type === type)
+ }
+
+ getKeyringsByType (type) {
+ return this.keyrings.filter((keyring) => keyring.type === type)
+ }
+
+ // Get Accounts
+ // returns Promise( @Array[ @string accounts ] )
+ //
+ // Returns the public addresses of all current accounts
+ // managed by all currently unlocked keyrings.
+ getAccounts () {
+ const keyrings = this.keyrings || []
+ return Promise.all(keyrings.map(kr => kr.getAccounts()))
+ .then((keyringArrays) => {
+ return keyringArrays.reduce((res, arr) => {
+ return res.concat(arr)
+ }, [])
+ })
+ }
+
+ // Get Keyring For Account
+ // @string address
+ //
+ // returns Promise(@Keyring keyring)
+ //
+ // Returns the currently initialized keyring that manages
+ // the specified `address` if one exists.
+ getKeyringForAccount (address) {
+ const hexed = normalizeAddress(address)
+
+ return Promise.all(this.keyrings.map((keyring) => {
+ return Promise.all([
+ keyring,
+ keyring.getAccounts(),
+ ])
+ }))
+ .then(filter((candidate) => {
+ const accounts = candidate[1].map(normalizeAddress)
+ return accounts.includes(hexed)
+ }))
+ .then((winners) => {
+ if (winners && winners.length > 0) {
+ return winners[0][0]
+ } else {
+ throw new Error('No keyring found for the requested account.')
+ }
+ })
+ }
+
+ // Display For Keyring
+ // @Keyring keyring
+ //
+ // returns Promise( @Object { type:String, accounts:Array } )
+ //
+ // Is used for adding the current keyrings to the state object.
+ displayForKeyring (keyring) {
+ return keyring.getAccounts()
+ .then((accounts) => {
+ return {
+ type: keyring.type,
+ accounts: accounts,
+ }
+ })
+ }
+
+ // Add Gas Buffer
+ // @string gas (as hexadecimal value)
+ //
+ // returns @string bufferedGas (as hexadecimal value)
+ //
+ // Adds a healthy buffer of gas to an initial gas estimate.
+ addGasBuffer (gas) {
+ const gasBuffer = new BN('100000', 10)
+ const bnGas = new BN(ethUtil.stripHexPrefix(gas), 16)
+ const correct = bnGas.add(gasBuffer)
+ return ethUtil.addHexPrefix(correct.toString(16))
+ }
+
+ // Clear Keyrings
+ //
+ // Deallocates all currently managed keyrings and accounts.
+ // Used before initializing a new vault.
+ clearKeyrings () {
+ let accounts
+ try {
+ accounts = Object.keys(this.ethStore.getState())
+ } catch (e) {
+ accounts = []
+ }
+ accounts.forEach((address) => {
+ this.ethStore.removeAccount(address)
+ })
+
+ // clear keyrings from memory
+ this.keyrings = []
+ this.memStore.updateState({
+ keyrings: [],
+ identities: {},
+ })
+ }
+
+ _updateMemStoreKeyrings() {
+ Promise.all(this.keyrings.map(this.displayForKeyring))
+ .then((keyrings) => {
+ this.memStore.updateState({ keyrings })
+ })
+ }
+
+}
+
+module.exports = KeyringController
diff --git a/app/scripts/keyrings/hd.js b/app/scripts/keyrings/hd.js
new file mode 100644
index 000000000..3a66f7868
--- /dev/null
+++ b/app/scripts/keyrings/hd.js
@@ -0,0 +1,125 @@
+const EventEmitter = require('events').EventEmitter
+const hdkey = require('ethereumjs-wallet/hdkey')
+const bip39 = require('bip39')
+const ethUtil = require('ethereumjs-util')
+
+// *Internal Deps
+const sigUtil = require('../lib/sig-util')
+
+// Options:
+const hdPathString = `m/44'/60'/0'/0`
+const type = 'HD Key Tree'
+
+class HdKeyring extends EventEmitter {
+
+ /* PUBLIC METHODS */
+
+ constructor (opts = {}) {
+ super()
+ this.type = type
+ this.deserialize(opts)
+ }
+
+ serialize () {
+ return Promise.resolve({
+ mnemonic: this.mnemonic,
+ numberOfAccounts: this.wallets.length,
+ })
+ }
+
+ deserialize (opts = {}) {
+ this.opts = opts || {}
+ this.wallets = []
+ this.mnemonic = null
+ this.root = null
+
+ if (opts.mnemonic) {
+ this._initFromMnemonic(opts.mnemonic)
+ }
+
+ if (opts.numberOfAccounts) {
+ return this.addAccounts(opts.numberOfAccounts)
+ }
+
+ return Promise.resolve([])
+ }
+
+ addAccounts (numberOfAccounts = 1) {
+ if (!this.root) {
+ this._initFromMnemonic(bip39.generateMnemonic())
+ }
+
+ const oldLen = this.wallets.length
+ const newWallets = []
+ for (let i = oldLen; i < numberOfAccounts + oldLen; i++) {
+ const child = this.root.deriveChild(i)
+ const wallet = child.getWallet()
+ newWallets.push(wallet)
+ this.wallets.push(wallet)
+ }
+ const hexWallets = newWallets.map(w => w.getAddress().toString('hex'))
+ return Promise.resolve(hexWallets)
+ }
+
+ getAccounts () {
+ return Promise.resolve(this.wallets.map(w => w.getAddress().toString('hex')))
+ }
+
+ // tx is an instance of the ethereumjs-transaction class.
+ signTransaction (address, tx) {
+ const wallet = this._getWalletForAccount(address)
+ var privKey = wallet.getPrivateKey()
+ tx.sign(privKey)
+ return Promise.resolve(tx)
+ }
+
+ // For eth_sign, we need to sign transactions:
+ // hd
+ signMessage (withAccount, data) {
+ const wallet = this._getWalletForAccount(withAccount)
+ const message = ethUtil.stripHexPrefix(data)
+ var privKey = wallet.getPrivateKey()
+ var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
+ var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
+ return Promise.resolve(rawMsgSig)
+ }
+
+ // For eth_sign, we need to sign transactions:
+ newGethSignMessage (withAccount, msgHex) {
+ const wallet = this._getWalletForAccount(withAccount)
+ const privKey = wallet.getPrivateKey()
+ const msgBuffer = ethUtil.toBuffer(msgHex)
+ const msgHash = ethUtil.hashPersonalMessage(msgBuffer)
+ const msgSig = ethUtil.ecsign(msgHash, privKey)
+ const rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
+ return Promise.resolve(rawMsgSig)
+ }
+
+ exportAccount (address) {
+ const wallet = this._getWalletForAccount(address)
+ return Promise.resolve(wallet.getPrivateKey().toString('hex'))
+ }
+
+
+ /* PRIVATE METHODS */
+
+ _initFromMnemonic (mnemonic) {
+ this.mnemonic = mnemonic
+ const seed = bip39.mnemonicToSeed(mnemonic)
+ this.hdWallet = hdkey.fromMasterSeed(seed)
+ this.root = this.hdWallet.derivePath(hdPathString)
+ }
+
+
+ _getWalletForAccount (account) {
+ const targetAddress = sigUtil.normalize(account)
+ return this.wallets.find((w) => {
+ const address = w.getAddress().toString('hex')
+ return ((address === targetAddress) ||
+ (sigUtil.normalize(address) === targetAddress))
+ })
+ }
+}
+
+HdKeyring.type = type
+module.exports = HdKeyring
diff --git a/app/scripts/keyrings/simple.js b/app/scripts/keyrings/simple.js
new file mode 100644
index 000000000..82881aa2d
--- /dev/null
+++ b/app/scripts/keyrings/simple.js
@@ -0,0 +1,100 @@
+const EventEmitter = require('events').EventEmitter
+const Wallet = require('ethereumjs-wallet')
+const ethUtil = require('ethereumjs-util')
+const type = 'Simple Key Pair'
+const sigUtil = require('../lib/sig-util')
+
+class SimpleKeyring extends EventEmitter {
+
+ /* PUBLIC METHODS */
+
+ constructor (opts) {
+ super()
+ this.type = type
+ this.opts = opts || {}
+ this.wallets = []
+ }
+
+ serialize () {
+ return Promise.resolve(this.wallets.map(w => w.getPrivateKey().toString('hex')))
+ }
+
+ deserialize (privateKeys = []) {
+ return new Promise((resolve, reject) => {
+ try {
+ this.wallets = privateKeys.map((privateKey) => {
+ const stripped = ethUtil.stripHexPrefix(privateKey)
+ const buffer = new Buffer(stripped, 'hex')
+ const wallet = Wallet.fromPrivateKey(buffer)
+ return wallet
+ })
+ } catch (e) {
+ reject(e)
+ }
+ resolve()
+ })
+ }
+
+ addAccounts (n = 1) {
+ var newWallets = []
+ for (var i = 0; i < n; i++) {
+ newWallets.push(Wallet.generate())
+ }
+ this.wallets = this.wallets.concat(newWallets)
+ const hexWallets = newWallets.map(w => ethUtil.bufferToHex(w.getAddress()))
+ return Promise.resolve(hexWallets)
+ }
+
+ getAccounts () {
+ return Promise.resolve(this.wallets.map(w => ethUtil.bufferToHex(w.getAddress())))
+ }
+
+ // tx is an instance of the ethereumjs-transaction class.
+ signTransaction (address, tx) {
+ const wallet = this._getWalletForAccount(address)
+ var privKey = wallet.getPrivateKey()
+ tx.sign(privKey)
+ return Promise.resolve(tx)
+ }
+
+ // For eth_sign, we need to sign transactions:
+ signMessage (withAccount, data) {
+ const wallet = this._getWalletForAccount(withAccount)
+ const message = ethUtil.stripHexPrefix(data)
+ var privKey = wallet.getPrivateKey()
+ var msgSig = ethUtil.ecsign(new Buffer(message, 'hex'), privKey)
+ var rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
+ return Promise.resolve(rawMsgSig)
+ }
+
+ // For eth_sign, we need to sign transactions:
+
+ newGethSignMessage (withAccount, msgHex) {
+ const wallet = this._getWalletForAccount(withAccount)
+ const privKey = wallet.getPrivateKey()
+ const msgBuffer = ethUtil.toBuffer(msgHex)
+ const msgHash = ethUtil.hashPersonalMessage(msgBuffer)
+ const msgSig = ethUtil.ecsign(msgHash, privKey)
+ const rawMsgSig = ethUtil.bufferToHex(sigUtil.concatSig(msgSig.v, msgSig.r, msgSig.s))
+ return Promise.resolve(rawMsgSig)
+ }
+
+ exportAccount (address) {
+ const wallet = this._getWalletForAccount(address)
+ return Promise.resolve(wallet.getPrivateKey().toString('hex'))
+ }
+
+
+ /* PRIVATE METHODS */
+
+ _getWalletForAccount (account) {
+ const address = sigUtil.normalize(account)
+ let wallet = this.wallets.find(w => ethUtil.bufferToHex(w.getAddress()) === address)
+ if (!wallet) throw new Error('Simple Keyring - Unable to find matching address.')
+ return wallet
+ }
+
+}
+
+SimpleKeyring.type = type
+module.exports = SimpleKeyring
diff --git a/app/scripts/lib/auto-faucet.js b/app/scripts/lib/auto-faucet.js
index 59cf0ec20..1e86f735e 100644
--- a/app/scripts/lib/auto-faucet.js
+++ b/app/scripts/lib/auto-faucet.js
@@ -1,6 +1,9 @@
-var uri = 'https://faucet.metamask.io/'
+const uri = 'https://faucet.metamask.io/'
+const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
+const env = process.env.METAMASK_ENV
module.exports = function (address) {
+ if (METAMASK_DEBUG || env === 'test') return // Don't faucet in development or test
var http = new XMLHttpRequest()
var data = address
http.open('POST', uri, true)
diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js
index c4c8053f0..1302df35f 100644
--- a/app/scripts/lib/auto-reload.js
+++ b/app/scripts/lib/auto-reload.js
@@ -3,7 +3,7 @@ const ensnare = require('ensnare')
module.exports = setupDappAutoReload
-function setupDappAutoReload (web3, controlStream) {
+function setupDappAutoReload (web3) {
// export web3 as a global, checking for usage
var pageIsUsingWeb3 = false
var resetWasRequested = false
@@ -16,19 +16,18 @@ function setupDappAutoReload (web3, controlStream) {
global.web3 = web3
}))
- // listen for reset requests from metamask
- controlStream.once('data', function () {
+ return handleResetRequest
+
+ function handleResetRequest () {
resetWasRequested = true
// ignore if web3 was not used
if (!pageIsUsingWeb3) return
// reload after short timeout
- triggerReset()
- })
-
- // reload the page
- function triggerReset () {
- setTimeout(function () {
- global.location.reload()
- }, 500)
+ setTimeout(triggerReset, 500)
}
}
+
+// reload the page
+function triggerReset () {
+ global.location.reload()
+}
diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js
index 715efb42e..6267eab68 100644
--- a/app/scripts/lib/config-manager.js
+++ b/app/scripts/lib/config-manager.js
@@ -1,11 +1,10 @@
-const Migrator = require('pojo-migrator')
const MetamaskConfig = require('../config.js')
-const migrations = require('./migrations')
-const rp = require('request-promise')
+const ethUtil = require('ethereumjs-util')
+const normalize = require('./sig-util').normalize
const TESTNET_RPC = MetamaskConfig.network.testnet
const MAINNET_RPC = MetamaskConfig.network.mainnet
-const txLimit = 40
+const MORDEN_RPC = MetamaskConfig.network.morden
/* The config-manager is a convenience object
* wrapping a pojo-migrator.
@@ -16,54 +15,21 @@ const txLimit = 40
*/
module.exports = ConfigManager
function ConfigManager (opts) {
- this.txLimit = txLimit
-
// ConfigManager is observable and will emit updates
this._subs = []
-
- /* The migrator exported on the config-manager
- * has two methods the user should be concerned with:
- *
- * getData(), which returns the app-consumable data object
- * saveData(), which persists the app-consumable data object.
- */
- this.migrator = new Migrator({
-
- // Migrations must start at version 1 or later.
- // They are objects with a `version` number
- // and a `migrate` function.
- //
- // The `migrate` function receives the previous
- // config data format, and returns the new one.
- migrations: migrations,
-
- // How to load initial config.
- // Includes step on migrating pre-pojo-migrator data.
- loadData: opts.loadData,
-
- // How to persist migrated config.
- setData: opts.setData,
- })
+ this.store = opts.store
}
ConfigManager.prototype.setConfig = function (config) {
- var data = this.migrator.getData()
+ var data = this.getData()
data.config = config
this.setData(data)
this._emitUpdates(config)
}
ConfigManager.prototype.getConfig = function () {
- var data = this.migrator.getData()
- if ('config' in data) {
- return data.config
- } else {
- return {
- provider: {
- type: 'testnet',
- },
- }
- }
+ var data = this.getData()
+ return data.config
}
ConfigManager.prototype.setRpcTarget = function (rpcUrl) {
@@ -97,19 +63,40 @@ ConfigManager.prototype.getProvider = function () {
}
ConfigManager.prototype.setData = function (data) {
- this.migrator.saveData(data)
+ this.store.putState(data)
}
ConfigManager.prototype.getData = function () {
- return this.migrator.getData()
+ return this.store.getState()
}
ConfigManager.prototype.setWallet = function (wallet) {
- var data = this.migrator.getData()
+ var data = this.getData()
data.wallet = wallet
this.setData(data)
}
+ConfigManager.prototype.setVault = function (encryptedString) {
+ var data = this.getData()
+ data.vault = encryptedString
+ this.setData(data)
+}
+
+ConfigManager.prototype.getVault = function () {
+ var data = this.getData()
+ return data.vault
+}
+
+ConfigManager.prototype.getKeychains = function () {
+ return this.getData().keychains || []
+}
+
+ConfigManager.prototype.setKeychains = function (keychains) {
+ var data = this.getData()
+ data.keychains = keychains
+ this.setData(data)
+}
+
ConfigManager.prototype.getSelectedAccount = function () {
var config = this.getConfig()
return config.selectedAccount
@@ -117,26 +104,38 @@ ConfigManager.prototype.getSelectedAccount = function () {
ConfigManager.prototype.setSelectedAccount = function (address) {
var config = this.getConfig()
- config.selectedAccount = address
+ config.selectedAccount = ethUtil.addHexPrefix(address)
this.setConfig(config)
}
ConfigManager.prototype.getWallet = function () {
- return this.migrator.getData().wallet
+ return this.getData().wallet
}
// Takes a boolean
ConfigManager.prototype.setShowSeedWords = function (should) {
- var data = this.migrator.getData()
+ var data = this.getData()
data.showSeedWords = should
this.setData(data)
}
+
ConfigManager.prototype.getShouldShowSeedWords = function () {
- var data = this.migrator.getData()
+ var data = this.getData()
return data.showSeedWords
}
+ConfigManager.prototype.setSeedWords = function (words) {
+ var data = this.getData()
+ data.seedWords = words
+ this.setData(data)
+}
+
+ConfigManager.prototype.getSeedWords = function () {
+ var data = this.getData()
+ return data.seedWords
+}
+
ConfigManager.prototype.getCurrentRpcAddress = function () {
var provider = this.getProvider()
if (!provider) return null
@@ -148,21 +147,20 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
case 'testnet':
return TESTNET_RPC
+ case 'morden':
+ return MORDEN_RPC
+
default:
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
}
}
-ConfigManager.prototype.setData = function (data) {
- this.migrator.saveData(data)
-}
-
//
// Tx
//
ConfigManager.prototype.getTxList = function () {
- var data = this.migrator.getData()
+ var data = this.getData()
if (data.transactions !== undefined) {
return data.transactions
} else {
@@ -170,61 +168,12 @@ ConfigManager.prototype.getTxList = function () {
}
}
-ConfigManager.prototype.unconfirmedTxs = function () {
- var transactions = this.getTxList()
- return transactions.filter(tx => tx.status === 'unconfirmed')
- .reduce((result, tx) => { result[tx.id] = tx; return result }, {})
-}
-
-ConfigManager.prototype._saveTxList = function (txList) {
- var data = this.migrator.getData()
+ConfigManager.prototype.setTxList = function (txList) {
+ var data = this.getData()
data.transactions = txList
this.setData(data)
}
-ConfigManager.prototype.addTx = function (tx) {
- var transactions = this.getTxList()
- while (transactions.length > this.txLimit - 1) {
- transactions.shift()
- }
- transactions.push(tx)
- this._saveTxList(transactions)
-}
-
-ConfigManager.prototype.getTx = function (txId) {
- var transactions = this.getTxList()
- var matching = transactions.filter(tx => tx.id === txId)
- return matching.length > 0 ? matching[0] : null
-}
-
-ConfigManager.prototype.confirmTx = function (txId) {
- this._setTxStatus(txId, 'confirmed')
-}
-
-ConfigManager.prototype.rejectTx = function (txId) {
- this._setTxStatus(txId, 'rejected')
-}
-
-ConfigManager.prototype._setTxStatus = function (txId, status) {
- var tx = this.getTx(txId)
- tx.status = status
- this.updateTx(tx)
-}
-
-ConfigManager.prototype.updateTx = function (tx) {
- var transactions = this.getTxList()
- var found, index
- transactions.forEach((otherTx, i) => {
- if (otherTx.id === tx.id) {
- found = true
- index = i
- }
- })
- if (found) {
- transactions[index] = tx
- }
- this._saveTxList(transactions)
-}
// wallet nickname methods
@@ -235,13 +184,15 @@ ConfigManager.prototype.getWalletNicknames = function () {
}
ConfigManager.prototype.nicknameForWallet = function (account) {
+ const address = normalize(account)
const nicknames = this.getWalletNicknames()
- return nicknames[account]
+ return nicknames[address]
}
ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
+ const address = normalize(account)
const nicknames = this.getWalletNicknames()
- nicknames[account] = nickname
+ nicknames[address] = nickname
var data = this.getData()
data.walletNicknames = nicknames
this.setData(data)
@@ -249,6 +200,17 @@ ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
// observable
+ConfigManager.prototype.getSalt = function () {
+ var data = this.getData()
+ return data.salt
+}
+
+ConfigManager.prototype.setSalt = function (salt) {
+ var data = this.getData()
+ data.salt = salt
+ this.setData(data)
+}
+
ConfigManager.prototype.subscribe = function (fn) {
this._subs.push(fn)
var unsubscribe = this.unsubscribe.bind(this, fn)
@@ -266,110 +228,25 @@ ConfigManager.prototype._emitUpdates = function (state) {
})
}
-ConfigManager.prototype.setConfirmed = function (confirmed) {
+ConfigManager.prototype.getGasMultiplier = function () {
var data = this.getData()
- data.isConfirmed = confirmed
- this.setData(data)
+ return data.gasMultiplier
}
-ConfigManager.prototype.getConfirmed = function () {
+ConfigManager.prototype.setGasMultiplier = function (gasMultiplier) {
var data = this.getData()
- return ('isConfirmed' in data) && data.isConfirmed
-}
-ConfigManager.prototype.setCurrentFiat = function (currency) {
- var data = this.getData()
- data.fiatCurrency = currency
- this.setData(data)
-}
-
-ConfigManager.prototype.getCurrentFiat = function () {
- var data = this.getData()
- return ('fiatCurrency' in data) && data.fiatCurrency
-}
-
-ConfigManager.prototype.updateConversionRate = function () {
- var data = this.getData()
- return rp(`https://www.cryptonator.com/api/ticker/eth-${data.fiatCurrency}`)
- .then((response) => {
- const parsedResponse = JSON.parse(response)
- this.setConversionPrice(parsedResponse.ticker.price)
- this.setConversionDate(parsedResponse.timestamp)
- }).catch((err) => {
- console.error('Error in conversion.', err)
- this.setConversionPrice(0)
- this.setConversionDate('N/A')
- })
-
-}
-
-ConfigManager.prototype.setConversionPrice = function (price) {
- var data = this.getData()
- data.conversionRate = Number(price)
+ data.gasMultiplier = gasMultiplier
this.setData(data)
}
-ConfigManager.prototype.setConversionDate = function (datestring) {
+ConfigManager.prototype.setLostAccounts = function (lostAccounts) {
var data = this.getData()
- data.conversionDate = datestring
+ data.lostAccounts = lostAccounts
this.setData(data)
}
-ConfigManager.prototype.getConversionRate = function () {
+ConfigManager.prototype.getLostAccounts = function () {
var data = this.getData()
- return (('conversionRate' in data) && data.conversionRate) || 0
-}
-
-ConfigManager.prototype.getConversionDate = function () {
- var data = this.getData()
- return (('conversionDate' in data) && data.conversionDate) || 'N/A'
-}
-
-ConfigManager.prototype.setShouldntShowWarning = function () {
- var data = this.getData()
- if (data.isEthConfirmed) {
- data.isEthConfirmed = !data.isEthConfirmed
- } else {
- data.isEthConfirmed = true
- }
- this.setData(data)
-}
-
-ConfigManager.prototype.getShouldntShowWarning = function () {
- var data = this.getData()
- return ('isEthConfirmed' in data) && data.isEthConfirmed
-}
-
-ConfigManager.prototype.getShapeShiftTxList = function () {
- var data = this.getData()
- var shapeShiftTxList = data.shapeShiftTxList ? data.shapeShiftTxList : []
- shapeShiftTxList.forEach((tx) => {
- if (tx.response.status !== 'complete') {
- var requestListner = function (request) {
- tx.response = JSON.parse(this.responseText)
- if (tx.response.status === 'complete') {
- tx.time = new Date().getTime()
- }
- }
-
- var shapShiftReq = new XMLHttpRequest()
- shapShiftReq.addEventListener('load', requestListner)
- shapShiftReq.open('GET', `https://shapeshift.io/txStat/${tx.depositAddress}`, true)
- shapShiftReq.send()
- }
- })
- this.setData(data)
- return shapeShiftTxList
-}
-
-ConfigManager.prototype.createShapeShiftTx = function (depositAddress, depositType) {
- var data = this.getData()
-
- var shapeShiftTx = {depositAddress, depositType, key: 'shapeshift', time: new Date().getTime(), response: {}}
- if (!data.shapeShiftTxList) {
- data.shapeShiftTxList = [shapeShiftTx]
- } else {
- data.shapeShiftTxList.push(shapeShiftTx)
- }
- this.setData(data)
+ return data.lostAccounts || []
}
diff --git a/app/scripts/lib/controllers/currency.js b/app/scripts/lib/controllers/currency.js
new file mode 100644
index 000000000..c4904f8ac
--- /dev/null
+++ b/app/scripts/lib/controllers/currency.js
@@ -0,0 +1,70 @@
+const ObservableStore = require('obs-store')
+const extend = require('xtend')
+
+// every ten minutes
+const POLLING_INTERVAL = 600000
+
+class CurrencyController {
+
+ constructor (opts = {}) {
+ const initState = extend({
+ currentCurrency: 'USD',
+ conversionRate: 0,
+ conversionDate: 'N/A',
+ }, opts.initState)
+ this.store = new ObservableStore(initState)
+ }
+
+ //
+ // PUBLIC METHODS
+ //
+
+ getCurrentCurrency () {
+ return this.store.getState().currentCurrency
+ }
+
+ setCurrentCurrency (currentCurrency) {
+ this.store.updateState({ currentCurrency })
+ }
+
+ getConversionRate () {
+ return this.store.getState().conversionRate
+ }
+
+ setConversionRate (conversionRate) {
+ this.store.updateState({ conversionRate })
+ }
+
+ getConversionDate () {
+ return this.store.getState().conversionDate
+ }
+
+ setConversionDate (conversionDate) {
+ this.store.updateState({ conversionDate })
+ }
+
+ updateConversionRate () {
+ const currentCurrency = this.getCurrentCurrency()
+ return fetch(`https://www.cryptonator.com/api/ticker/eth-${currentCurrency}`)
+ .then(response => response.json())
+ .then((parsedResponse) => {
+ this.setConversionRate(Number(parsedResponse.ticker.price))
+ this.setConversionDate(Number(parsedResponse.timestamp))
+ }).catch((err) => {
+ console.warn('MetaMask - Failed to query currency conversion.')
+ this.setConversionRate(0)
+ this.setConversionDate('N/A')
+ })
+ }
+
+ scheduleConversionInterval () {
+ if (this.conversionInterval) {
+ clearInterval(this.conversionInterval)
+ }
+ this.conversionInterval = setInterval(() => {
+ this.updateConversionRate()
+ }, POLLING_INTERVAL)
+ }
+}
+
+module.exports = CurrencyController
diff --git a/app/scripts/lib/controllers/preferences.js b/app/scripts/lib/controllers/preferences.js
new file mode 100644
index 000000000..dc9464c4e
--- /dev/null
+++ b/app/scripts/lib/controllers/preferences.js
@@ -0,0 +1,33 @@
+const ObservableStore = require('obs-store')
+const normalizeAddress = require('../sig-util').normalize
+
+class PreferencesController {
+
+ constructor (opts = {}) {
+ const initState = opts.initState || {}
+ this.store = new ObservableStore(initState)
+ }
+
+ //
+ // PUBLIC METHODS
+ //
+
+ setSelectedAddress(_address) {
+ return new Promise((resolve, reject) => {
+ const address = normalizeAddress(_address)
+ this.store.updateState({ selectedAddress: address })
+ resolve()
+ })
+ }
+
+ getSelectedAddress(_address) {
+ return this.store.getState().selectedAddress
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+}
+
+module.exports = PreferencesController
diff --git a/app/scripts/lib/controllers/shapeshift.js b/app/scripts/lib/controllers/shapeshift.js
new file mode 100644
index 000000000..3d955c01f
--- /dev/null
+++ b/app/scripts/lib/controllers/shapeshift.js
@@ -0,0 +1,104 @@
+const ObservableStore = require('obs-store')
+const extend = require('xtend')
+
+// every three seconds when an incomplete tx is waiting
+const POLLING_INTERVAL = 3000
+
+class ShapeshiftController {
+
+ constructor (opts = {}) {
+ const initState = extend({
+ shapeShiftTxList: [],
+ }, opts.initState)
+ this.store = new ObservableStore(initState)
+ this.pollForUpdates()
+ }
+
+ //
+ // PUBLIC METHODS
+ //
+
+ getShapeShiftTxList () {
+ const shapeShiftTxList = this.store.getState().shapeShiftTxList
+ return shapeShiftTxList
+ }
+
+ getPendingTxs () {
+ const txs = this.getShapeShiftTxList()
+ const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete')
+ return pending
+ }
+
+ pollForUpdates () {
+ const pendingTxs = this.getPendingTxs()
+
+ if (pendingTxs.length === 0) {
+ return
+ }
+
+ Promise.all(pendingTxs.map((tx) => {
+ return this.updateTx(tx)
+ }))
+ .then((results) => {
+ results.forEach(tx => this.saveTx(tx))
+ this.timeout = setTimeout(this.pollForUpdates.bind(this), POLLING_INTERVAL)
+ })
+ }
+
+ updateTx (tx) {
+ const url = `https://shapeshift.io/txStat/${tx.depositAddress}`
+ return fetch(url)
+ .then((response) => {
+ return response.json()
+ }).then((json) => {
+ tx.response = json
+ if (tx.response.status === 'complete') {
+ tx.time = new Date().getTime()
+ }
+ return tx
+ })
+ }
+
+ saveTx (tx) {
+ const { shapeShiftTxList } = this.store.getState()
+ const index = shapeShiftTxList.indexOf(tx)
+ if (index !== -1) {
+ shapeShiftTxList[index] = tx
+ this.store.updateState({ shapeShiftTxList })
+ }
+ }
+
+ removeShapeShiftTx (tx) {
+ const { shapeShiftTxList } = this.store.getState()
+ const index = shapeShiftTxList.indexOf(index)
+ if (index !== -1) {
+ shapeShiftTxList.splice(index, 1)
+ }
+ this.updateState({ shapeShiftTxList })
+ }
+
+ createShapeShiftTx (depositAddress, depositType) {
+ const state = this.store.getState()
+ let { shapeShiftTxList } = state
+
+ var shapeShiftTx = {
+ depositAddress,
+ depositType,
+ key: 'shapeshift',
+ time: new Date().getTime(),
+ response: {},
+ }
+
+ if (!shapeShiftTxList) {
+ shapeShiftTxList = [shapeShiftTx]
+ } else {
+ shapeShiftTxList.push(shapeShiftTx)
+ }
+
+ this.store.updateState({ shapeShiftTxList })
+ this.pollForUpdates()
+ }
+
+}
+
+module.exports = ShapeshiftController
diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js
new file mode 100644
index 000000000..8812a507b
--- /dev/null
+++ b/app/scripts/lib/eth-store.js
@@ -0,0 +1,132 @@
+/* Ethereum Store
+ *
+ * This module is responsible for tracking any number of accounts
+ * and caching their current balances & transaction counts.
+ *
+ * It also tracks transaction hashes, and checks their inclusion status
+ * on each new block.
+ */
+
+const async = require('async')
+const EthQuery = require('eth-query')
+const ObservableStore = require('obs-store')
+function noop() {}
+
+
+class EthereumStore extends ObservableStore {
+
+ constructor (opts = {}) {
+ super({
+ accounts: {},
+ transactions: {},
+ })
+ this._provider = opts.provider
+ this._query = new EthQuery(this._provider)
+ this._blockTracker = opts.blockTracker
+ // subscribe to latest block
+ this._blockTracker.on('block', this._updateForBlock.bind(this))
+ // blockTracker.currentBlock may be null
+ this._currentBlockNumber = this._blockTracker.currentBlock
+ }
+
+ //
+ // public
+ //
+
+ addAccount (address) {
+ const accounts = this.getState().accounts
+ accounts[address] = {}
+ this.updateState({ accounts })
+ if (!this._currentBlockNumber) return
+ this._updateAccount(address)
+ }
+
+ removeAccount (address) {
+ const accounts = this.getState().accounts
+ delete accounts[address]
+ this.updateState({ accounts })
+ }
+
+ addTransaction (txHash) {
+ const transactions = this.getState().transactions
+ transactions[txHash] = {}
+ this.updateState({ transactions })
+ if (!this._currentBlockNumber) return
+ this._updateTransaction(this._currentBlockNumber, txHash, noop)
+ }
+
+ removeTransaction (txHash) {
+ const transactions = this.getState().transactions
+ delete transactions[txHash]
+ this.updateState({ transactions })
+ }
+
+
+ //
+ // private
+ //
+
+ _updateForBlock (block) {
+ const blockNumber = '0x' + block.number.toString('hex')
+ this._currentBlockNumber = blockNumber
+ async.parallel([
+ this._updateAccounts.bind(this),
+ this._updateTransactions.bind(this, blockNumber),
+ ], (err) => {
+ if (err) return console.error(err)
+ this.emit('block', this.getState())
+ })
+ }
+
+ _updateAccounts (cb = noop) {
+ const accounts = this.getState().accounts
+ const addresses = Object.keys(accounts)
+ async.each(addresses, this._updateAccount.bind(this), cb)
+ }
+
+ _updateAccount (address, cb = noop) {
+ const accounts = this.getState().accounts
+ this._getAccount(address, (err, result) => {
+ if (err) return cb(err)
+ result.address = address
+ // only populate if the entry is still present
+ if (accounts[address]) {
+ accounts[address] = result
+ this.updateState({ accounts })
+ }
+ cb(null, result)
+ })
+ }
+
+ _updateTransactions (block, cb = noop) {
+ const transactions = this.getState().transactions
+ const txHashes = Object.keys(transactions)
+ async.each(txHashes, this._updateTransaction.bind(this, block), cb)
+ }
+
+ _updateTransaction (block, txHash, cb = noop) {
+ // would use the block here to determine how many confirmations the tx has
+ const transactions = this.getState().transactions
+ this._query.getTransaction(txHash, (err, result) => {
+ if (err) return cb(err)
+ // only populate if the entry is still present
+ if (transactions[txHash]) {
+ transactions[txHash] = result
+ this.updateState({ transactions })
+ }
+ cb(null, result)
+ })
+ }
+
+ _getAccount (address, cb = noop) {
+ const query = this._query
+ async.parallel({
+ balance: query.getBalance.bind(query, address),
+ nonce: query.getTransactionCount.bind(query, address),
+ code: query.getCode.bind(query, address),
+ }, cb)
+ }
+
+}
+
+module.exports = EthereumStore \ No newline at end of file
diff --git a/app/scripts/lib/extension-instance.js b/app/scripts/lib/extension-instance.js
index eb3b8a1e9..628b62e3f 100644
--- a/app/scripts/lib/extension-instance.js
+++ b/app/scripts/lib/extension-instance.js
@@ -42,10 +42,27 @@ function Extension () {
} catch (e) {}
try {
+ if (browser[api]) {
+ _this[api] = browser[api]
+ }
+ } catch (e) {}
+ try {
_this.api = browser.extension[api]
} catch (e) {}
-
})
+
+ try {
+ if (browser && browser.runtime) {
+ this.runtime = browser.runtime
+ }
+ } catch (e) {}
+
+ try {
+ if (browser && browser.browserAction) {
+ this.browserAction = browser.browserAction
+ }
+ } catch (e) {}
+
}
module.exports = Extension
diff --git a/app/scripts/lib/id-management.js b/app/scripts/lib/id-management.js
index 9b8ceb415..421f2105f 100644
--- a/app/scripts/lib/id-management.js
+++ b/app/scripts/lib/id-management.js
@@ -1,4 +1,13 @@
+/* ID Management
+ *
+ * This module exists to hold the decrypted credentials for the current session.
+ * It therefore exposes sign methods, because it is able to perform these
+ * with noa dditional authentication, because its very instantiation
+ * means the vault is unlocked.
+ */
+
const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
const Transaction = require('ethereumjs-tx')
module.exports = IdManagement
@@ -16,9 +25,15 @@ function IdManagement (opts) {
}
this.signTx = function (txParams) {
+ // calculate gas with custom gas multiplier
+ var gasMultiplier = this.configManager.getGasMultiplier() || 1
+ var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
+ gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10))
+ txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
// normalize values
+
txParams.to = ethUtil.addHexPrefix(txParams.to)
- txParams.from = ethUtil.addHexPrefix(txParams.from)
+ txParams.from = ethUtil.addHexPrefix(txParams.from.toLowerCase())
txParams.value = ethUtil.addHexPrefix(txParams.value)
txParams.data = ethUtil.addHexPrefix(txParams.data)
txParams.gasLimit = ethUtil.addHexPrefix(txParams.gasLimit || txParams.gas)
@@ -43,7 +58,7 @@ function IdManagement (opts) {
this.signMsg = function (address, message) {
// sign message
- var privKeyHex = this.exportPrivateKey(address)
+ var privKeyHex = this.exportPrivateKey(address.toLowerCase())
var privKey = ethUtil.toBuffer(privKeyHex)
var msgSig = ethUtil.ecsign(new Buffer(message.replace('0x', ''), 'hex'), privKey)
var rawMsgSig = ethUtil.bufferToHex(concatSig(msgSig.v, msgSig.r, msgSig.s))
diff --git a/app/scripts/lib/idStore-migrator.js b/app/scripts/lib/idStore-migrator.js
new file mode 100644
index 000000000..655aed0af
--- /dev/null
+++ b/app/scripts/lib/idStore-migrator.js
@@ -0,0 +1,80 @@
+const IdentityStore = require('./idStore')
+const HdKeyring = require('../keyrings/hd')
+const sigUtil = require('./sig-util')
+const normalize = sigUtil.normalize
+const denodeify = require('denodeify')
+
+module.exports = class IdentityStoreMigrator {
+
+ constructor ({ configManager }) {
+ this.configManager = configManager
+ const hasOldVault = this.hasOldVault()
+ if (!hasOldVault) {
+ this.idStore = new IdentityStore({ configManager })
+ }
+ }
+
+ migratedVaultForPassword (password) {
+ const hasOldVault = this.hasOldVault()
+ const configManager = this.configManager
+
+ if (!this.idStore) {
+ this.idStore = new IdentityStore({ configManager })
+ }
+
+ if (!hasOldVault) {
+ return Promise.resolve(null)
+ }
+
+ const idStore = this.idStore
+ const submitPassword = denodeify(idStore.submitPassword.bind(idStore))
+
+ return submitPassword(password)
+ .then(() => {
+ const serialized = this.serializeVault()
+ return this.checkForLostAccounts(serialized)
+ })
+ }
+
+ serializeVault () {
+ const mnemonic = this.idStore._idmgmt.getSeed()
+ const numberOfAccounts = this.idStore._getAddresses().length
+
+ return {
+ type: 'HD Key Tree',
+ data: { mnemonic, numberOfAccounts },
+ }
+ }
+
+ checkForLostAccounts (serialized) {
+ const hd = new HdKeyring()
+ return hd.deserialize(serialized.data)
+ .then((hexAccounts) => {
+ const newAccounts = hexAccounts.map(normalize)
+ const oldAccounts = this.idStore._getAddresses().map(normalize)
+ const lostAccounts = oldAccounts.reduce((result, account) => {
+ if (newAccounts.includes(account)) {
+ return result
+ } else {
+ result.push(account)
+ return result
+ }
+ }, [])
+
+ return {
+ serialized,
+ lostAccounts: lostAccounts.map((address) => {
+ return {
+ address,
+ privateKey: this.idStore.exportAccount(address),
+ }
+ }),
+ }
+ })
+ }
+
+ hasOldVault () {
+ const wallet = this.configManager.getWallet()
+ return wallet
+ }
+}
diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js
index 7ac71e409..7a6968c6c 100644
--- a/app/scripts/lib/idStore.js
+++ b/app/scripts/lib/idStore.js
@@ -1,18 +1,14 @@
const EventEmitter = require('events').EventEmitter
const inherits = require('util').inherits
-const async = require('async')
const ethUtil = require('ethereumjs-util')
-const EthQuery = require('eth-query')
-const LightwalletKeyStore = require('eth-lightwallet').keystore
+const KeyStore = require('eth-lightwallet').keystore
const clone = require('clone')
const extend = require('xtend')
-const createId = require('web3-provider-engine/util/random-id')
-const ethBinToOps = require('eth-bin-to-ops')
const autoFaucet = require('./auto-faucet')
-const messageManager = require('./message-manager')
const DEFAULT_RPC = 'https://testrpc.metamask.io/'
const IdManagement = require('./id-management')
+
module.exports = IdentityStore
inherits(IdentityStore, EventEmitter)
@@ -33,28 +29,32 @@ function IdentityStore (opts = {}) {
selectedAddress: null,
identities: {},
}
-
// not part of serilized metamask state - only kept in memory
- this._unconfTxCbs = {}
- this._unconfMsgCbs = {}
}
//
// public
//
-IdentityStore.prototype.createNewVault = function (password, entropy, cb) {
+IdentityStore.prototype.createNewVault = function (password, cb) {
delete this._keyStore
+ var serializedKeystore = this.configManager.getWallet()
+
+ if (serializedKeystore) {
+ this.configManager.setData({})
+ }
- this._createIdmgmt(password, null, entropy, (err) => {
+ this.purgeCache()
+ this._createVault(password, null, (err) => {
if (err) return cb(err)
- this._loadIdentities()
- this._didUpdate()
this._autoFaucet()
this.configManager.setShowSeedWords(true)
var seedWords = this._idmgmt.getSeed()
+
+ this._loadIdentities()
+
cb(null, seedWords)
})
}
@@ -67,11 +67,12 @@ IdentityStore.prototype.recoverSeed = function (cb) {
}
IdentityStore.prototype.recoverFromSeed = function (password, seed, cb) {
- this._createIdmgmt(password, seed, null, (err) => {
+ this.purgeCache()
+
+ this._createVault(password, seed, (err) => {
if (err) return cb(err)
this._loadIdentities()
- this._didUpdate()
cb(null, this.getState())
})
}
@@ -93,17 +94,8 @@ IdentityStore.prototype.getState = function () {
isInitialized: !!configManager.getWallet() && !seedWords,
isUnlocked: this._isUnlocked(),
seedWords: seedWords,
- isConfirmed: configManager.getConfirmed(),
- isEthConfirmed: configManager.getShouldntShowWarning(),
- unconfTxs: configManager.unconfirmedTxs(),
- transactions: configManager.getTxList(),
- unconfMsgs: messageManager.unconfirmedMsgs(),
- messages: messageManager.getMsgList(),
selectedAddress: configManager.getSelectedAccount(),
- shapeShiftTxList: configManager.getShapeShiftTxList(),
- currentFiat: configManager.getCurrentFiat(),
- conversionRate: configManager.getConversionRate(),
- conversionDate: configManager.getConversionDate(),
+ gasMultiplier: configManager.getGasMultiplier(),
}))
}
@@ -121,7 +113,7 @@ IdentityStore.prototype.getSelectedAddress = function () {
return configManager.getSelectedAccount()
}
-IdentityStore.prototype.setSelectedAddress = function (address, cb) {
+IdentityStore.prototype.setSelectedAddressSync = function (address) {
const configManager = this.configManager
if (!address) {
var addresses = this._getAddresses()
@@ -129,7 +121,12 @@ IdentityStore.prototype.setSelectedAddress = function (address, cb) {
}
configManager.setSelectedAccount(address)
- if (cb) return cb(null, address)
+ return address
+}
+
+IdentityStore.prototype.setSelectedAddress = function (address, cb) {
+ const resultAddress = this.setSelectedAddressSync(address)
+ if (cb) return cb(null, resultAddress)
}
IdentityStore.prototype.revealAccount = function (cb) {
@@ -139,6 +136,11 @@ IdentityStore.prototype.revealAccount = function (cb) {
keyStore.setDefaultHdDerivationPath(this.hdPathString)
keyStore.generateNewAddress(derivedKey, 1)
+ const addresses = keyStore.getAddresses()
+ const address = addresses[ addresses.length - 1 ]
+
+ this._ethStore.addAccount(ethUtil.addHexPrefix(address))
+
configManager.setWallet(keyStore.serialize())
this._loadIdentities()
@@ -183,192 +185,10 @@ IdentityStore.prototype.submitPassword = function (password, cb) {
IdentityStore.prototype.exportAccount = function (address, cb) {
var privateKey = this._idmgmt.exportPrivateKey(address)
- cb(null, privateKey)
+ if (cb) cb(null, privateKey)
+ return privateKey
}
-//
-// Transactions
-//
-
-// comes from dapp via zero-client hooked-wallet provider
-IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDoneCb, cb) {
- const configManager = this.configManager
- var self = this
- // create txData obj with parameters and meta data
- var time = (new Date()).getTime()
- var txId = createId()
- txParams.metamaskId = txId
- txParams.metamaskNetworkId = self._currentState.network
- var txData = {
- id: txId,
- txParams: txParams,
- time: time,
- status: 'unconfirmed',
- }
-
- console.log('addUnconfirmedTransaction:', txData)
-
- // keep the onTxDoneCb around for after approval/denial (requires user interaction)
- // This onTxDoneCb fires completion to the Dapp's write operation.
- self._unconfTxCbs[txId] = onTxDoneCb
-
- var provider = self._ethStore._query.currentProvider
- var query = new EthQuery(provider)
-
- // calculate metadata for tx
- async.parallel([
- analyzeForDelegateCall,
- estimateGas,
- ], didComplete)
-
- // perform static analyis on the target contract code
- function analyzeForDelegateCall(cb){
- if (txParams.to) {
- query.getCode(txParams.to, function (err, result) {
- if (err) return cb(err)
- var code = ethUtil.toBuffer(result)
- if (code !== '0x') {
- var ops = ethBinToOps(code)
- var containsDelegateCall = ops.some((op) => op.name === 'DELEGATECALL')
- txData.containsDelegateCall = containsDelegateCall
- cb()
- } else {
- cb()
- }
- })
- } else {
- cb()
- }
- }
-
- function estimateGas(cb){
- query.estimateGas(txParams, function(err, result){
- if (err) return cb(err)
- txData.estimatedGas = result
- cb()
- })
- }
-
- function didComplete (err) {
- if (err) return cb(err)
- configManager.addTx(txData)
- // signal update
- self._didUpdate()
- // signal completion of add tx
- cb(null, txData)
- }
-}
-
-// comes from metamask ui
-IdentityStore.prototype.approveTransaction = function (txId, cb) {
- const configManager = this.configManager
- var approvalCb = this._unconfTxCbs[txId] || noop
-
- // accept tx
- cb()
- approvalCb(null, true)
- // clean up
- configManager.confirmTx(txId)
- delete this._unconfTxCbs[txId]
- this._didUpdate()
-}
-
-// comes from metamask ui
-IdentityStore.prototype.cancelTransaction = function (txId) {
- const configManager = this.configManager
- var approvalCb = this._unconfTxCbs[txId] || noop
-
- // reject tx
- approvalCb(null, false)
- // clean up
- configManager.rejectTx(txId)
- delete this._unconfTxCbs[txId]
- this._didUpdate()
-}
-
-// performs the actual signing, no autofill of params
-IdentityStore.prototype.signTransaction = function (txParams, cb) {
- try {
- console.log('signing tx...', txParams)
- var rawTx = this._idmgmt.signTx(txParams)
- cb(null, rawTx)
- } catch (err) {
- cb(err)
- }
-}
-
-//
-// Messages
-//
-
-// comes from dapp via zero-client hooked-wallet provider
-IdentityStore.prototype.addUnconfirmedMessage = function (msgParams, cb) {
- // create txData obj with parameters and meta data
- var time = (new Date()).getTime()
- var msgId = createId()
- var msgData = {
- id: msgId,
- msgParams: msgParams,
- time: time,
- status: 'unconfirmed',
- }
- messageManager.addMsg(msgData)
- console.log('addUnconfirmedMessage:', msgData)
-
- // keep the cb around for after approval (requires user interaction)
- // This cb fires completion to the Dapp's write operation.
- this._unconfMsgCbs[msgId] = cb
-
- // signal update
- this._didUpdate()
-
- return msgId
-}
-
-// comes from metamask ui
-IdentityStore.prototype.approveMessage = function (msgId, cb) {
- var approvalCb = this._unconfMsgCbs[msgId] || noop
-
- // accept msg
- cb()
- approvalCb(null, true)
- // clean up
- messageManager.confirmMsg(msgId)
- delete this._unconfMsgCbs[msgId]
- this._didUpdate()
-}
-
-// comes from metamask ui
-IdentityStore.prototype.cancelMessage = function (msgId) {
- var approvalCb = this._unconfMsgCbs[msgId] || noop
-
- // reject tx
- approvalCb(null, false)
- // clean up
- messageManager.rejectMsg(msgId)
- delete this._unconfTxCbs[msgId]
- this._didUpdate()
-}
-
-// performs the actual signing, no autofill of params
-IdentityStore.prototype.signMessage = function (msgParams, cb) {
- try {
- console.log('signing msg...', msgParams.data)
- var rawMsg = this._idmgmt.signMsg(msgParams.from, msgParams.data)
- if ('metamaskId' in msgParams) {
- var id = msgParams.metamaskId
- delete msgParams.metamaskId
-
- this.approveMessage(id, cb)
- } else {
- cb(null, rawMsg)
- }
- } catch (err) {
- cb(err)
- }
-}
-
-//
// private
//
@@ -389,9 +209,11 @@ IdentityStore.prototype._loadIdentities = function () {
var addresses = this._getAddresses()
addresses.forEach((address, i) => {
// // add to ethStore
- this._ethStore.addAccount(address)
+ if (this._ethStore) {
+ this._ethStore.addAccount(ethUtil.addHexPrefix(address))
+ }
// add to identities
- const defaultLabel = 'Wallet ' + (i + 1)
+ const defaultLabel = 'Account ' + (i + 1)
const nickname = configManager.nicknameForWallet(address)
var identity = {
name: nickname || defaultLabel,
@@ -408,7 +230,6 @@ IdentityStore.prototype.saveAccountLabel = function (account, label, cb) {
configManager.setNicknameForWallet(account, label)
this._loadIdentities()
cb(null, label)
- this._didUpdate()
}
// mayBeFauceting
@@ -432,76 +253,87 @@ IdentityStore.prototype._mayBeFauceting = function (i) {
//
IdentityStore.prototype.tryPassword = function (password, cb) {
- this._createIdmgmt(password, null, null, cb)
+ var serializedKeystore = this.configManager.getWallet()
+ var keyStore = KeyStore.deserialize(serializedKeystore)
+
+ keyStore.keyFromPassword(password, (err, pwDerivedKey) => {
+ if (err) return cb(err)
+
+ const isCorrect = keyStore.isDerivedKeyCorrect(pwDerivedKey)
+ if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
+
+ this._keyStore = keyStore
+ this._createIdMgmt(pwDerivedKey)
+ cb()
+ })
}
-IdentityStore.prototype._createIdmgmt = function (password, seed, entropy, cb) {
- const configManager = this.configManager
- var keyStore = null
- LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
+IdentityStore.prototype._createVault = function (password, seedPhrase, cb) {
+ const opts = {
+ password,
+ hdPathString: this.hdPathString,
+ }
+
+ if (seedPhrase) {
+ opts.seedPhrase = seedPhrase
+ }
+
+ KeyStore.createVault(opts, (err, keyStore) => {
if (err) return cb(err)
- var serializedKeystore = configManager.getWallet()
-
- if (seed) {
- try {
- keyStore = this._restoreFromSeed(password, seed, derivedKey)
- } catch (e) {
- return cb(e)
- }
-
- // returning user, recovering from storage
- } else if (serializedKeystore) {
- keyStore = LightwalletKeyStore.deserialize(serializedKeystore)
- var isCorrect = keyStore.isDerivedKeyCorrect(derivedKey)
- if (!isCorrect) return cb(new Error('Lightwallet - password incorrect'))
-
- // first time here
- } else {
- keyStore = this._createFirstWallet(entropy, derivedKey)
- }
this._keyStore = keyStore
- this._idmgmt = new IdManagement({
- keyStore: keyStore,
- derivedKey: derivedKey,
- hdPathSTring: this.hdPathString,
- configManager: this.configManager,
- })
- cb()
+ keyStore.keyFromPassword(password, (err, derivedKey) => {
+ if (err) return cb(err)
+
+ this.purgeCache()
+
+ keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
+
+ this._createFirstWallet(derivedKey)
+ this._createIdMgmt(derivedKey)
+ this.setSelectedAddressSync()
+
+ cb()
+ })
})
}
-IdentityStore.prototype._restoreFromSeed = function (password, seed, derivedKey) {
- const configManager = this.configManager
- var keyStore = new LightwalletKeyStore(seed, derivedKey, this.hdPathString)
- keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
- keyStore.setDefaultHdDerivationPath(this.hdPathString)
+IdentityStore.prototype._createIdMgmt = function (derivedKey) {
+ this._idmgmt = new IdManagement({
+ keyStore: this._keyStore,
+ derivedKey: derivedKey,
+ configManager: this.configManager,
+ })
+}
- keyStore.generateNewAddress(derivedKey, 3)
- configManager.setWallet(keyStore.serialize())
- if (global.METAMASK_DEBUG) {
- console.log('restored from seed. saved to keystore')
+IdentityStore.prototype.purgeCache = function () {
+ this._currentState.identities = {}
+ let accounts
+ try {
+ accounts = Object.keys(this._ethStore._currentState.accounts)
+ } catch (e) {
+ accounts = []
}
- return keyStore
+ accounts.forEach((address) => {
+ this._ethStore.removeAccount(address)
+ })
}
-IdentityStore.prototype._createFirstWallet = function (entropy, derivedKey) {
- const configManager = this.configManager
- var secretSeed = LightwalletKeyStore.generateRandomSeed(entropy)
- var keyStore = new LightwalletKeyStore(secretSeed, derivedKey, this.hdPathString)
- keyStore.addHdDerivationPath(this.hdPathString, derivedKey, {curve: 'secp256k1', purpose: 'sign'})
+IdentityStore.prototype._createFirstWallet = function (derivedKey) {
+ const keyStore = this._keyStore
keyStore.setDefaultHdDerivationPath(this.hdPathString)
-
keyStore.generateNewAddress(derivedKey, 1)
- configManager.setWallet(keyStore.serialize())
- console.log('saved to keystore')
- return keyStore
+ this.configManager.setWallet(keyStore.serialize())
+ var addresses = keyStore.getAddresses()
+ this._ethStore.addAccount(ethUtil.addHexPrefix(addresses[0]))
}
// get addresses and normalize address hexString
IdentityStore.prototype._getAddresses = function () {
- return this._keyStore.getAddresses(this.hdPathString).map((address) => { return '0x' + address })
+ return this._keyStore.getAddresses(this.hdPathString).map((address) => {
+ return ethUtil.addHexPrefix(address)
+ })
}
IdentityStore.prototype._autoFaucet = function () {
@@ -510,5 +342,3 @@ IdentityStore.prototype._autoFaucet = function () {
}
// util
-
-function noop () {}
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index 65354cd3d..92936de2f 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -1,7 +1,8 @@
-const Streams = require('mississippi')
-const ObjectMultiplex = require('./obj-multiplex')
+const pipe = require('pump')
const StreamProvider = require('web3-stream-provider')
-const RemoteStore = require('./remote-store.js').RemoteStore
+const LocalStorageStore = require('obs-store')
+const ObjectMultiplex = require('./obj-multiplex')
+const createRandomId = require('./random-id')
module.exports = MetamaskInpageProvider
@@ -9,64 +10,89 @@ function MetamaskInpageProvider (connectionStream) {
const self = this
// setup connectionStream multiplexing
- var multiStream = ObjectMultiplex()
- Streams.pipe(connectionStream, multiStream, connectionStream, function (err) {
- console.warn('MetamaskInpageProvider - lost connection to MetaMask')
- if (err) throw err
- })
- self.multiStream = multiStream
-
- // subscribe to metamask public config
- var publicConfigStore = remoteStoreWithLocalStorageCache('MetaMask-Config')
- var storeStream = publicConfigStore.createStream()
- Streams.pipe(storeStream, multiStream.createStream('publicConfig'), storeStream, function (err) {
- console.warn('MetamaskInpageProvider - lost connection to MetaMask publicConfig')
- if (err) throw err
- })
- self.publicConfigStore = publicConfigStore
+ var multiStream = self.multiStream = ObjectMultiplex()
+ pipe(
+ connectionStream,
+ multiStream,
+ connectionStream,
+ (err) => logStreamDisconnectWarning('MetaMask', err)
+ )
+
+ // subscribe to metamask public config (one-way)
+ self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' })
+ pipe(
+ multiStream.createStream('publicConfig'),
+ self.publicConfigStore,
+ (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err)
+ )
// connect to async provider
- var asyncProvider = new StreamProvider()
- Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) {
- console.warn('MetamaskInpageProvider - lost connection to MetaMask provider')
- if (err) throw err
- })
- asyncProvider.on('error', console.error.bind(console))
- self.asyncProvider = asyncProvider
+ const asyncProvider = self.asyncProvider = new StreamProvider()
+ pipe(
+ asyncProvider,
+ multiStream.createStream('provider'),
+ asyncProvider,
+ (err) => logStreamDisconnectWarning('MetaMask RpcProvider', err)
+ )
+
+ self.idMap = {}
// handle sendAsync requests via asyncProvider
- self.sendAsync = function(payload, cb){
+ self.sendAsync = function (payload, cb) {
// rewrite request ids
- var request = jsonrpcMessageTransform(payload, (message) => {
- message.id = createRandomId()
+ var request = eachJsonMessage(payload, (message) => {
+ var newId = createRandomId()
+ self.idMap[newId] = message.id
+ message.id = newId
return message
})
// forward to asyncProvider
- asyncProvider.sendAsync(request, cb)
+ asyncProvider.sendAsync(request, function (err, res) {
+ if (err) return cb(err)
+ // transform messages to original ids
+ eachJsonMessage(res, (message) => {
+ var oldId = self.idMap[message.id]
+ delete self.idMap[message.id]
+ message.id = oldId
+ return message
+ })
+ cb(null, res)
+ })
}
}
MetamaskInpageProvider.prototype.send = function (payload) {
const self = this
-
+
let selectedAddress
let result = null
switch (payload.method) {
case 'eth_accounts':
// read from localStorage
- selectedAddress = self.publicConfigStore.get('selectedAddress')
+ selectedAddress = self.publicConfigStore.getState().selectedAddress
result = selectedAddress ? [selectedAddress] : []
break
case 'eth_coinbase':
// read from localStorage
- selectedAddress = self.publicConfigStore.get('selectedAddress')
- result = selectedAddress || '0x0000000000000000000000000000000000000000'
+ selectedAddress = self.publicConfigStore.getState().selectedAddress
+ result = selectedAddress
+ break
+
+ case 'eth_uninstallFilter':
+ self.sendAsync(payload, noop)
+ result = true
+ break
+
+ case 'net_version':
+ let networkVersion = self.publicConfigStore.getState().networkVersion
+ result = networkVersion
break
// throw not-supported Error
default:
- var message = 'The MetaMask Web3 object does not support synchronous methods. See https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#all-async---think-of-metamask-as-a-light-client for details.'
+ var link = 'https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#dizzy-all-async---think-of-metamask-as-a-light-client'
+ var message = `The MetaMask Web3 object does not support synchronous methods like ${payload.method} without a callback parameter. See ${link} for details.`
throw new Error(message)
}
@@ -87,34 +113,22 @@ MetamaskInpageProvider.prototype.isConnected = function () {
return true
}
-// util
-
-function remoteStoreWithLocalStorageCache (storageKey) {
- // read local cache
- var initState = JSON.parse(localStorage[storageKey] || '{}')
- var store = new RemoteStore(initState)
- // cache the latest state locally
- store.subscribe(function (state) {
- localStorage[storageKey] = JSON.stringify(state)
- })
-
- return store
-}
+MetamaskInpageProvider.prototype.isMetaMask = true
-function createRandomId(){
- const extraDigits = 3
- // 13 time digits
- const datePart = new Date().getTime() * Math.pow(10, extraDigits)
- // 3 random digits
- const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits))
- // 16 digits
- return datePart + extraPart
-}
+// util
-function jsonrpcMessageTransform(payload, transformFn){
+function eachJsonMessage (payload, transformFn) {
if (Array.isArray(payload)) {
return payload.map(transformFn)
} else {
return transformFn(payload)
}
-} \ No newline at end of file
+}
+
+function logStreamDisconnectWarning(remoteLabel, err){
+ let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}`
+ if (err) warningMsg += '\n' + err.stack
+ console.warn(warningMsg)
+}
+
+function noop () {}
diff --git a/app/scripts/lib/is-popup-or-notification.js b/app/scripts/lib/is-popup-or-notification.js
new file mode 100644
index 000000000..693fa8751
--- /dev/null
+++ b/app/scripts/lib/is-popup-or-notification.js
@@ -0,0 +1,8 @@
+module.exports = function isPopupOrNotification () {
+ const url = window.location.href
+ if (url.match(/popup.html$/)) {
+ return 'popup'
+ } else {
+ return 'notification'
+ }
+}
diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js
index b609b820e..ceaf8ee2f 100644
--- a/app/scripts/lib/message-manager.js
+++ b/app/scripts/lib/message-manager.js
@@ -1,61 +1,118 @@
-module.exports = new MessageManager()
+const EventEmitter = require('events')
+const ObservableStore = require('obs-store')
+const ethUtil = require('ethereumjs-util')
+const createId = require('./random-id')
-function MessageManager (opts) {
- this.messages = []
-}
-MessageManager.prototype.getMsgList = function () {
- return this.messages
-}
+module.exports = class MessageManager extends EventEmitter{
+ constructor (opts) {
+ super()
+ this.memStore = new ObservableStore({
+ unapprovedMsgs: {},
+ unapprovedMsgCount: 0,
+ })
+ this.messages = []
+ }
-MessageManager.prototype.unconfirmedMsgs = function () {
- var messages = this.getMsgList()
- return messages.filter(msg => msg.status === 'unconfirmed')
- .reduce((result, msg) => { result[msg.id] = msg; return result }, {})
-}
+ get unapprovedMsgCount () {
+ return Object.keys(this.getUnapprovedMsgs()).length
+ }
-MessageManager.prototype._saveMsgList = function (msgList) {
- this.messages = msgList
-}
+ getUnapprovedMsgs () {
+ return this.messages.filter(msg => msg.status === 'unapproved')
+ .reduce((result, msg) => { result[msg.id] = msg; return result }, {})
+ }
-MessageManager.prototype.addMsg = function (msg) {
- var messages = this.getMsgList()
- messages.push(msg)
- this._saveMsgList(messages)
-}
+ addUnapprovedMessage (msgParams) {
+ msgParams.data = normalizeMsgData(msgParams.data)
+ // create txData obj with parameters and meta data
+ var time = (new Date()).getTime()
+ var msgId = createId()
+ var msgData = {
+ id: msgId,
+ msgParams: msgParams,
+ time: time,
+ status: 'unapproved',
+ }
+ this.addMsg(msgData)
-MessageManager.prototype.getMsg = function (msgId) {
- var messages = this.getMsgList()
- var matching = messages.filter(msg => msg.id === msgId)
- return matching.length > 0 ? matching[0] : null
-}
+ // signal update
+ this.emit('update')
+ return msgId
+ }
-MessageManager.prototype.confirmMsg = function (msgId) {
- this._setMsgStatus(msgId, 'confirmed')
-}
+ addMsg (msg) {
+ this.messages.push(msg)
+ this._saveMsgList()
+ }
-MessageManager.prototype.rejectMsg = function (msgId) {
- this._setMsgStatus(msgId, 'rejected')
-}
+ getMsg (msgId) {
+ return this.messages.find(msg => msg.id === msgId)
+ }
-MessageManager.prototype._setMsgStatus = function (msgId, status) {
- var msg = this.getMsg(msgId)
- if (msg) msg.status = status
- this.updateMsg(msg)
-}
+ approveMessage (msgParams) {
+ this.setMsgStatusApproved(msgParams.metamaskId)
+ return this.prepMsgForSigning(msgParams)
+ }
-MessageManager.prototype.updateMsg = function (msg) {
- var messages = this.getMsgList()
- var found, index
- messages.forEach((otherMsg, i) => {
- if (otherMsg.id === msg.id) {
- found = true
- index = i
+ setMsgStatusApproved (msgId) {
+ this._setMsgStatus(msgId, 'approved')
+ }
+
+ setMsgStatusSigned (msgId, rawSig) {
+ const msg = this.getMsg(msgId)
+ msg.rawSig = rawSig
+ this._updateMsg(msg)
+ this._setMsgStatus(msgId, 'signed')
+ }
+
+ prepMsgForSigning (msgParams) {
+ delete msgParams.metamaskId
+ return Promise.resolve(msgParams)
+ }
+
+ rejectMsg (msgId) {
+ this._setMsgStatus(msgId, 'rejected')
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ _setMsgStatus (msgId, status) {
+ const msg = this.getMsg(msgId)
+ if (!msg) throw new Error('MessageManager - Message not found for id: "${msgId}".')
+ msg.status = status
+ this._updateMsg(msg)
+ this.emit(`${msgId}:${status}`, msg)
+ if (status === 'rejected' || status === 'signed') {
+ this.emit(`${msgId}:finished`, msg)
+ }
+ }
+
+ _updateMsg (msg) {
+ const index = this.messages.findIndex((message) => message.id === msg.id)
+ if (index !== -1) {
+ this.messages[index] = msg
}
- })
- if (found) {
- messages[index] = msg
+ this._saveMsgList()
}
- this._saveMsgList(messages)
+
+ _saveMsgList () {
+ const unapprovedMsgs = this.getUnapprovedMsgs()
+ const unapprovedMsgCount = Object.keys(unapprovedMsgs).length
+ this.memStore.updateState({ unapprovedMsgs, unapprovedMsgCount })
+ this.emit('updateBadge')
+ }
+
}
+function normalizeMsgData(data) {
+ if (data.slice(0, 2) === '0x') {
+ // data is already hex
+ return data
+ } else {
+ // data is unicode, convert to hex
+ return ethUtil.bufferToHex(new Buffer(data, 'utf8'))
+ }
+} \ No newline at end of file
diff --git a/app/scripts/lib/migrations.js b/app/scripts/lib/migrations.js
deleted file mode 100644
index f026cbe53..000000000
--- a/app/scripts/lib/migrations.js
+++ /dev/null
@@ -1,5 +0,0 @@
-module.exports = [
- require('../migrations/002'),
- require('../migrations/003'),
- require('../migrations/004'),
-]
diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js
new file mode 100644
index 000000000..312345263
--- /dev/null
+++ b/app/scripts/lib/migrator/index.js
@@ -0,0 +1,51 @@
+const asyncQ = require('async-q')
+
+class Migrator {
+
+ constructor (opts = {}) {
+ let migrations = opts.migrations || []
+ this.migrations = migrations.sort((a, b) => a.version - b.version)
+ let lastMigration = this.migrations.slice(-1)[0]
+ // use specified defaultVersion or highest migration version
+ this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0
+ }
+
+ // run all pending migrations on meta in place
+ migrateData (versionedData = this.generateInitialState()) {
+ let remaining = this.migrations.filter(migrationIsPending)
+
+ return (
+ asyncQ.eachSeries(remaining, (migration) => this.runMigration(versionedData, migration))
+ .then(() => versionedData)
+ )
+
+ // migration is "pending" if hit has a higher
+ // version number than currentVersion
+ function migrationIsPending(migration) {
+ return migration.version > versionedData.meta.version
+ }
+ }
+
+ runMigration(versionedData, migration) {
+ return (
+ migration.migrate(versionedData)
+ .then((versionedData) => {
+ if (!versionedData.data) return Promise.reject(new Error('Migrator - Migration returned empty data'))
+ if (migration.version !== undefined && versionedData.meta.version !== migration.version) return Promise.reject(new Error('Migrator - Migration did not update version number correctly'))
+ return Promise.resolve(versionedData)
+ })
+ )
+ }
+
+ generateInitialState (initState) {
+ return {
+ meta: {
+ version: this.defaultVersion,
+ },
+ data: initState,
+ }
+ }
+
+}
+
+module.exports = Migrator
diff --git a/app/scripts/lib/nodeify.js b/app/scripts/lib/nodeify.js
new file mode 100644
index 000000000..51d89a8fb
--- /dev/null
+++ b/app/scripts/lib/nodeify.js
@@ -0,0 +1,24 @@
+module.exports = function (promiseFn) {
+ return function () {
+ var args = []
+ for (var i = 0; i < arguments.length - 1; i++) {
+ args.push(arguments[i])
+ }
+ var cb = arguments[arguments.length - 1]
+
+ const nodeified = promiseFn.apply(this, args)
+
+ if (!nodeified) {
+ const methodName = String(promiseFn).split('(')[0]
+ throw new Error(`The ${methodName} did not return a Promise, but was nodeified.`)
+ }
+ nodeified.then(function (result) {
+ cb(null, result)
+ })
+ .catch(function (reason) {
+ cb(reason)
+ })
+
+ return nodeified
+ }
+}
diff --git a/app/scripts/lib/notifications.js b/app/scripts/lib/notifications.js
index 6c1601df1..3db1ac6b5 100644
--- a/app/scripts/lib/notifications.js
+++ b/app/scripts/lib/notifications.js
@@ -1,159 +1,65 @@
-const createId = require('hat')
-const extend = require('xtend')
-const unmountComponentAtNode = require('react-dom').unmountComponentAtNode
-const findDOMNode = require('react-dom').findDOMNode
-const render = require('react-dom').render
-const h = require('react-hyperscript')
-const PendingTxDetails = require('../../../ui/app/components/pending-tx-details')
-const PendingMsgDetails = require('../../../ui/app/components/pending-msg-details')
-const MetaMaskUiCss = require('../../../ui/css')
const extension = require('./extension')
-var notificationHandlers = {}
+const height = 520
+const width = 360
const notifications = {
- createUnlockRequestNotification: createUnlockRequestNotification,
- createTxNotification: createTxNotification,
- createMsgNotification: createMsgNotification,
+ show,
+ getPopup,
+ closePopup,
}
module.exports = notifications
window.METAMASK_NOTIFIER = notifications
-setupListeners()
-
-function setupListeners () {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
+function show () {
+ getPopup((err, popup) => {
+ if (err) throw err
- // notification button press
- extension.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
- var handlers = notificationHandlers[notificationId]
- if (buttonIndex === 0) {
- handlers.confirm()
+ if (popup) {
+ // bring focus to existing popup
+ extension.windows.update(popup.id, { focused: true })
} else {
- handlers.cancel()
+ // create new popup
+ extension.windows.create({
+ url: 'notification.html',
+ type: 'popup',
+ focused: true,
+ width,
+ height,
+ })
}
- extension.notifications.clear(notificationId)
- })
-
- // notification teardown
- extension.notifications.onClosed.addListener(function (notificationId) {
- delete notificationHandlers[notificationId]
})
}
-// creation helper
-function createUnlockRequestNotification (opts) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
- var message = 'An Ethereum app has requested a signature. Please unlock your account.'
-
- var id = createId()
- extension.notifications.create(id, {
- type: 'basic',
- iconUrl: '/images/icon-128.png',
- title: opts.title,
- message: message,
- })
-}
-
-function createTxNotification (state) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
-
- renderTxNotificationSVG(state, function (err, notificationSvgSource) {
- if (err) throw err
+function getWindows (cb) {
+ // Ignore in test environment
+ if (!extension.windows) {
+ return cb()
+ }
- showNotification(extend(state, {
- title: 'New Unsigned Transaction',
- imageUrl: toSvgUri(notificationSvgSource),
- }))
+ extension.windows.getAll({}, (windows) => {
+ cb(null, windows)
})
}
-function createMsgNotification (state) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
-
- renderMsgNotificationSVG(state, function (err, notificationSvgSource) {
+function getPopup (cb) {
+ getWindows((err, windows) => {
if (err) throw err
-
- showNotification(extend(state, {
- title: 'New Unsigned Message',
- imageUrl: toSvgUri(notificationSvgSource),
- }))
+ cb(null, getPopupIn(windows))
})
}
-function showNotification (state) {
- // guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
- if (!extension.notifications) return console.error('Chrome notifications API missing...')
-
- var id = createId()
- extension.notifications.create(id, {
- type: 'image',
- requireInteraction: true,
- iconUrl: '/images/icon-128.png',
- imageUrl: state.imageUrl,
- title: state.title,
- message: '',
- buttons: [{
- title: 'Approve',
- }, {
- title: 'Reject',
- }],
- })
- notificationHandlers[id] = {
- confirm: state.onConfirm,
- cancel: state.onCancel,
- }
-}
-
-function renderTxNotificationSVG (state, cb) {
- var content = h(PendingTxDetails, state)
- renderNotificationSVG(content, cb)
-}
-
-function renderMsgNotificationSVG (state, cb) {
- var content = h(PendingMsgDetails, state)
- renderNotificationSVG(content, cb)
+function getPopupIn (windows) {
+ return windows ? windows.find((win) => {
+ return (win && win.type === 'popup' &&
+ win.height === height &&
+ win.width === width)
+ }) : null
}
-function renderNotificationSVG (content, cb) {
- var container = document.createElement('div')
- var confirmView = h('div.app-primary', {
- style: {
- width: '360px',
- height: '240px',
- padding: '16px',
- // background: '#F7F7F7',
- background: 'white',
- },
- }, [
- h('style', MetaMaskUiCss()),
- content,
- ])
-
- render(confirmView, container, function ready() {
- var rootElement = findDOMNode(this)
- var viewSource = rootElement.outerHTML
- unmountComponentAtNode(container)
- var svgSource = svgWrapper(viewSource)
- // insert content into svg wrapper
- cb(null, svgSource)
+function closePopup () {
+ getPopup((err, popup) => {
+ if (err) throw err
+ if (!popup) return
+ extension.windows.remove(popup.id, console.error)
})
}
-
-function svgWrapper (content) {
- var wrapperSource = `
- <svg xmlns="http://www.w3.org/2000/svg" width="360" height="240">
- <foreignObject x="0" y="0" width="100%" height="100%">
- <body xmlns="http://www.w3.org/1999/xhtml" height="100%">{{content}}</body>
- </foreignObject>
- </svg>
- `
- return wrapperSource.split('{{content}}').join(content)
-}
-
-function toSvgUri (content) {
- return 'data:image/svg+xml;utf8,' + encodeURIComponent(content)
-}
diff --git a/app/scripts/lib/obj-multiplex.js b/app/scripts/lib/obj-multiplex.js
index f54ff7653..bd114c394 100644
--- a/app/scripts/lib/obj-multiplex.js
+++ b/app/scripts/lib/obj-multiplex.js
@@ -10,9 +10,9 @@ function ObjectMultiplex (opts) {
var data = chunk.data
var substream = mx.streams[name]
if (!substream) {
- console.warn('orphaned data for stream ' + name)
+ console.warn(`orphaned data for stream "${name}"`)
} else {
- substream.push(data)
+ if (substream.push) substream.push(data)
}
return cb()
})
@@ -36,5 +36,9 @@ function ObjectMultiplex (opts) {
}
return substream
}
+ // ignore streams (dont display orphaned data warning)
+ mx.ignoreStream = function (name) {
+ mx.streams[name] = true
+ }
return mx
}
diff --git a/app/scripts/lib/port-stream.js b/app/scripts/lib/port-stream.js
index 1889e3c04..607a9c9ed 100644
--- a/app/scripts/lib/port-stream.js
+++ b/app/scripts/lib/port-stream.js
@@ -30,8 +30,7 @@ PortDuplexStream.prototype._onMessage = function (msg) {
PortDuplexStream.prototype._onDisconnect = function () {
try {
- // this.end()
- this.emit('close')
+ this.push(null)
} catch (err) {
this.emit('error', err)
}
@@ -52,12 +51,11 @@ PortDuplexStream.prototype._write = function (msg, encoding, cb) {
// console.log('PortDuplexStream - sent message', msg)
this._port.postMessage(msg)
}
- cb()
} catch (err) {
- console.error(err)
- // this.emit('error', err)
- cb(new Error('PortDuplexStream - disconnected'))
+ // console.error(err)
+ return cb(new Error('PortDuplexStream - disconnected'))
}
+ cb()
}
// util
diff --git a/app/scripts/lib/random-id.js b/app/scripts/lib/random-id.js
new file mode 100644
index 000000000..788f3370f
--- /dev/null
+++ b/app/scripts/lib/random-id.js
@@ -0,0 +1,9 @@
+const MAX = Number.MAX_SAFE_INTEGER
+
+let idCounter = Math.round(Math.random() * MAX)
+function createRandomId () {
+ idCounter = idCounter % MAX
+ return idCounter++
+}
+
+module.exports = createRandomId
diff --git a/app/scripts/lib/remote-store.js b/app/scripts/lib/remote-store.js
deleted file mode 100644
index fbfab7bad..000000000
--- a/app/scripts/lib/remote-store.js
+++ /dev/null
@@ -1,97 +0,0 @@
-const Dnode = require('dnode')
-const inherits = require('util').inherits
-
-module.exports = {
- HostStore: HostStore,
- RemoteStore: RemoteStore,
-}
-
-function BaseStore (initState) {
- this._state = initState || {}
- this._subs = []
-}
-
-BaseStore.prototype.set = function (key, value) {
- throw Error('Not implemented.')
-}
-
-BaseStore.prototype.get = function (key) {
- return this._state[key]
-}
-
-BaseStore.prototype.subscribe = function (fn) {
- this._subs.push(fn)
- var unsubscribe = this.unsubscribe.bind(this, fn)
- return unsubscribe
-}
-
-BaseStore.prototype.unsubscribe = function (fn) {
- var index = this._subs.indexOf(fn)
- if (index !== -1) this._subs.splice(index, 1)
-}
-
-BaseStore.prototype._emitUpdates = function (state) {
- this._subs.forEach(function (handler) {
- handler(state)
- })
-}
-
-//
-// host
-//
-
-inherits(HostStore, BaseStore)
-function HostStore (initState, opts) {
- BaseStore.call(this, initState)
-}
-
-HostStore.prototype.set = function (key, value) {
- this._state[key] = value
- process.nextTick(this._emitUpdates.bind(this, this._state))
-}
-
-HostStore.prototype.createStream = function () {
- var dnode = Dnode({
- // update: this._didUpdate.bind(this),
- })
- dnode.on('remote', this._didConnect.bind(this))
- return dnode
-}
-
-HostStore.prototype._didConnect = function (remote) {
- this.subscribe(function (state) {
- remote.update(state)
- })
- remote.update(this._state)
-}
-
-//
-// remote
-//
-
-inherits(RemoteStore, BaseStore)
-function RemoteStore (initState, opts) {
- BaseStore.call(this, initState)
- this._remote = null
-}
-
-RemoteStore.prototype.set = function (key, value) {
- this._remote.set(key, value)
-}
-
-RemoteStore.prototype.createStream = function () {
- var dnode = Dnode({
- update: this._didUpdate.bind(this),
- })
- dnode.once('remote', this._didConnect.bind(this))
- return dnode
-}
-
-RemoteStore.prototype._didConnect = function (remote) {
- this._remote = remote
-}
-
-RemoteStore.prototype._didUpdate = function (state) {
- this._state = state
- this._emitUpdates(state)
-}
diff --git a/app/scripts/lib/sig-util.js b/app/scripts/lib/sig-util.js
new file mode 100644
index 000000000..193dda381
--- /dev/null
+++ b/app/scripts/lib/sig-util.js
@@ -0,0 +1,28 @@
+const ethUtil = require('ethereumjs-util')
+
+module.exports = {
+
+ concatSig: function (v, r, s) {
+ const rSig = ethUtil.fromSigned(r)
+ const sSig = ethUtil.fromSigned(s)
+ const vSig = ethUtil.bufferToInt(v)
+ const rStr = padWithZeroes(ethUtil.toUnsigned(rSig).toString('hex'), 64)
+ const sStr = padWithZeroes(ethUtil.toUnsigned(sSig).toString('hex'), 64)
+ const vStr = ethUtil.stripHexPrefix(ethUtil.intToHex(vSig))
+ return ethUtil.addHexPrefix(rStr.concat(sStr, vStr)).toString('hex')
+ },
+
+ normalize: function (address) {
+ if (!address) return
+ return ethUtil.addHexPrefix(address.toLowerCase())
+ },
+
+}
+
+function padWithZeroes (number, length) {
+ var myString = '' + number
+ while (myString.length < length) {
+ myString = '0' + myString
+ }
+ return myString
+}
diff --git a/app/scripts/lib/stream-utils.js b/app/scripts/lib/stream-utils.js
index 1b7b89d14..ba79990cc 100644
--- a/app/scripts/lib/stream-utils.js
+++ b/app/scripts/lib/stream-utils.js
@@ -1,4 +1,5 @@
const Through = require('through2')
+const endOfStream = require('end-of-stream')
const ObjectMultiplex = require('./obj-multiplex')
module.exports = {
@@ -24,11 +25,11 @@ function jsonStringifyStream () {
function setupMultiplex (connectionStream) {
var mx = ObjectMultiplex()
connectionStream.pipe(mx).pipe(connectionStream)
- mx.on('error', function (err) {
- console.error(err)
+ endOfStream(mx, function (err) {
+ if (err) console.error(err)
})
- connectionStream.on('error', function (err) {
- console.error(err)
+ endOfStream(connectionStream, function (err) {
+ if (err) console.error(err)
mx.destroy()
})
return mx
diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js
new file mode 100644
index 000000000..5116cb93b
--- /dev/null
+++ b/app/scripts/lib/tx-utils.js
@@ -0,0 +1,132 @@
+const async = require('async')
+const EthQuery = require('eth-query')
+const ethUtil = require('ethereumjs-util')
+const Transaction = require('ethereumjs-tx')
+const normalize = require('./sig-util').normalize
+const BN = ethUtil.BN
+
+/*
+tx-utils are utility methods for Transaction manager
+its passed a provider and that is passed to ethquery
+and used to do things like calculate gas of a tx.
+*/
+
+module.exports = class txProviderUtils {
+ constructor (provider) {
+ this.provider = provider
+ this.query = new EthQuery(provider)
+ }
+
+ analyzeGasUsage (txData, cb) {
+ var self = this
+ this.query.getBlockByNumber('latest', true, (err, block) => {
+ if (err) return cb(err)
+ async.waterfall([
+ self.estimateTxGas.bind(self, txData, block.gasLimit),
+ self.setTxGas.bind(self, txData, block.gasLimit),
+ ], cb)
+ })
+ }
+
+ estimateTxGas (txData, blockGasLimitHex, cb) {
+ const txParams = txData.txParams
+ // check if gasLimit is already specified
+ txData.gasLimitSpecified = Boolean(txParams.gas)
+ // if not, fallback to block gasLimit
+ if (!txData.gasLimitSpecified) {
+ txParams.gas = blockGasLimitHex
+ }
+ // run tx, see if it will OOG
+ this.query.estimateGas(txParams, cb)
+ }
+
+ setTxGas (txData, blockGasLimitHex, estimatedGasHex, cb) {
+ txData.estimatedGas = estimatedGasHex
+ const txParams = txData.txParams
+
+ // if gasLimit was specified and doesnt OOG,
+ // use original specified amount
+ if (txData.gasLimitSpecified) {
+ txData.estimatedGas = txParams.gas
+ cb()
+ return
+ }
+ // if gasLimit not originally specified,
+ // try adding an additional gas buffer to our estimation for safety
+ const estimatedGasBn = new BN(ethUtil.stripHexPrefix(txData.estimatedGas), 16)
+ const blockGasLimitBn = new BN(ethUtil.stripHexPrefix(blockGasLimitHex), 16)
+ const estimationWithBuffer = new BN(this.addGasBuffer(estimatedGasBn), 16)
+ // added gas buffer is too high
+ if (estimationWithBuffer.gt(blockGasLimitBn)) {
+ txParams.gas = txData.estimatedGas
+ // added gas buffer is safe
+ } else {
+ const gasWithBufferHex = ethUtil.intToHex(estimationWithBuffer)
+ txParams.gas = gasWithBufferHex
+ }
+ cb()
+ return
+ }
+
+ addGasBuffer (gas) {
+ const gasBuffer = new BN('100000', 10)
+ const bnGas = new BN(ethUtil.stripHexPrefix(gas), 16)
+ const correct = bnGas.add(gasBuffer)
+ return ethUtil.addHexPrefix(correct.toString(16))
+ }
+
+ fillInTxParams (txParams, cb) {
+ let fromAddress = txParams.from
+ let reqs = {}
+
+ if (isUndef(txParams.gas)) reqs.gas = (cb) => this.query.estimateGas(txParams, cb)
+ if (isUndef(txParams.gasPrice)) reqs.gasPrice = (cb) => this.query.gasPrice(cb)
+ if (isUndef(txParams.nonce)) reqs.nonce = (cb) => this.query.getTransactionCount(fromAddress, 'pending', cb)
+
+ async.parallel(reqs, function(err, result) {
+ if (err) return cb(err)
+ // write results to txParams obj
+ Object.assign(txParams, result)
+ cb()
+ })
+ }
+
+ // builds ethTx from txParams object
+ buildEthTxFromParams (txParams, gasMultiplier = 1) {
+ // apply gas multiplyer
+ let gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
+ // multiply and divide by 100 so as to add percision to integer mul
+ gasPrice = gasPrice.mul(new BN(gasMultiplier * 100, 10)).div(new BN(100, 10))
+ txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
+ // normalize values
+ txParams.to = normalize(txParams.to)
+ txParams.from = normalize(txParams.from)
+ txParams.value = normalize(txParams.value)
+ txParams.data = normalize(txParams.data)
+ txParams.gasLimit = normalize(txParams.gasLimit || txParams.gas)
+ txParams.nonce = normalize(txParams.nonce)
+ // build ethTx
+ const ethTx = new Transaction(txParams)
+ return ethTx
+ }
+
+ publishTransaction (rawTx, cb) {
+ this.query.sendRawTransaction(rawTx, cb)
+ }
+
+ validateTxParams (txParams, cb) {
+ if (('value' in txParams) && txParams.value.indexOf('-') === 0) {
+ cb(new Error(`Invalid transaction value of ${txParams.value} not a positive number.`))
+ } else {
+ cb()
+ }
+ }
+
+
+}
+
+// util
+
+function isUndef(value) {
+ return value === undefined
+}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index d53094e43..29b13dc62 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -1,303 +1,595 @@
+const EventEmitter = require('events')
const extend = require('xtend')
-const EthStore = require('eth-store')
+const promiseToCallback = require('promise-to-callback')
+const pipe = require('pump')
+const Dnode = require('dnode')
+const ObservableStore = require('obs-store')
+const storeTransform = require('obs-store/lib/transform')
+const EthStore = require('./lib/eth-store')
+const EthQuery = require('eth-query')
+const streamIntoProvider = require('web3-stream-provider/handler')
const MetaMaskProvider = require('web3-provider-engine/zero.js')
-const IdentityStore = require('./lib/idStore')
-const messageManager = require('./lib/message-manager')
-const HostStore = require('./lib/remote-store.js').HostStore
-const Web3 = require('web3')
+const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
+const KeyringController = require('./keyring-controller')
+const PreferencesController = require('./lib/controllers/preferences')
+const CurrencyController = require('./lib/controllers/currency')
+const NoticeController = require('./notice-controller')
+const ShapeShiftController = require('./lib/controllers/shapeshift')
+const MessageManager = require('./lib/message-manager')
+const TxManager = require('./transaction-manager')
const ConfigManager = require('./lib/config-manager')
const extension = require('./lib/extension')
+const autoFaucet = require('./lib/auto-faucet')
+const nodeify = require('./lib/nodeify')
+const IdStoreMigrator = require('./lib/idStore-migrator')
+const accountImporter = require('./account-import-strategies')
-module.exports = class MetamaskController {
+const version = require('../manifest.json').version
+
+module.exports = class MetamaskController extends EventEmitter {
constructor (opts) {
+ super()
this.opts = opts
- this.configManager = new ConfigManager(opts)
- this.idStore = new IdentityStore({
- configManager: this.configManager,
+ let initState = opts.initState || {}
+
+ // observable state store
+ this.store = new ObservableStore(initState)
+
+ // network store
+ this.networkStore = new ObservableStore({ network: 'loading' })
+
+ // config manager
+ this.configManager = new ConfigManager({
+ store: this.store,
+ })
+
+ // preferences controller
+ this.preferencesController = new PreferencesController({
+ initState: initState.PreferencesController,
+ })
+
+ // currency controller
+ this.currencyController = new CurrencyController({
+ initState: initState.CurrencyController,
+ })
+ this.currencyController.updateConversionRate()
+ this.currencyController.scheduleConversionInterval()
+
+ // rpc provider
+ this.provider = this.initializeProvider()
+ this.provider.on('block', this.logBlock.bind(this))
+ this.provider.on('error', this.verifyNetwork.bind(this))
+
+ // eth data query tools
+ this.ethQuery = new EthQuery(this.provider)
+ this.ethStore = new EthStore({
+ provider: this.provider,
+ blockTracker: this.provider,
+ })
+
+ // key mgmt
+ this.keyringController = new KeyringController({
+ initState: initState.KeyringController,
+ ethStore: this.ethStore,
+ getNetwork: this.getNetworkState.bind(this),
+ })
+ this.keyringController.on('newAccount', (address) => {
+ this.preferencesController.setSelectedAddress(address)
+ autoFaucet(address)
+ })
+
+ // tx mgmt
+ this.txManager = new TxManager({
+ initState: initState.TransactionManager,
+ networkStore: this.networkStore,
+ preferencesStore: this.preferencesController.store,
+ txHistoryLimit: 40,
+ getNetwork: this.getNetworkState.bind(this),
+ signTransaction: this.keyringController.signTransaction.bind(this.keyringController),
+ provider: this.provider,
+ blockTracker: this.provider,
+ })
+
+ // notices
+ this.noticeController = new NoticeController({
+ initState: initState.NoticeController,
})
- this.provider = this.initializeProvider(opts)
- this.ethStore = new EthStore(this.provider)
- this.idStore.setStore(this.ethStore)
- this.messageManager = messageManager
+ this.noticeController.updateNoticesList()
+ // to be uncommented when retrieving notices from a remote server.
+ // this.noticeController.startPolling()
+
+ this.shapeshiftController = new ShapeShiftController({
+ initState: initState.ShapeShiftController,
+ })
+
+ this.lookupNetwork()
+ this.messageManager = new MessageManager()
this.publicConfigStore = this.initPublicConfigStore()
- this.configManager.setCurrentFiat('USD')
- this.configManager.updateConversionRate()
- this.scheduleConversionInterval()
+
+ // TEMPORARY UNTIL FULL DEPRECATION:
+ this.idStoreMigrator = new IdStoreMigrator({
+ configManager: this.configManager,
+ })
+
+ // manual disk state subscriptions
+ this.txManager.store.subscribe((state) => {
+ this.store.updateState({ TransactionManager: state })
+ })
+ this.keyringController.store.subscribe((state) => {
+ this.store.updateState({ KeyringController: state })
+ })
+ this.preferencesController.store.subscribe((state) => {
+ this.store.updateState({ PreferencesController: state })
+ })
+ this.currencyController.store.subscribe((state) => {
+ this.store.updateState({ CurrencyController: state })
+ })
+ this.noticeController.store.subscribe((state) => {
+ this.store.updateState({ NoticeController: state })
+ })
+ this.shapeshiftController.store.subscribe((state) => {
+ this.store.updateState({ ShapeShiftController: state })
+ })
+
+ // manual mem state subscriptions
+ this.networkStore.subscribe(this.sendUpdate.bind(this))
+ this.ethStore.subscribe(this.sendUpdate.bind(this))
+ this.txManager.memStore.subscribe(this.sendUpdate.bind(this))
+ this.messageManager.memStore.subscribe(this.sendUpdate.bind(this))
+ this.keyringController.memStore.subscribe(this.sendUpdate.bind(this))
+ this.preferencesController.store.subscribe(this.sendUpdate.bind(this))
+ this.currencyController.store.subscribe(this.sendUpdate.bind(this))
+ this.noticeController.memStore.subscribe(this.sendUpdate.bind(this))
+ this.shapeshiftController.store.subscribe(this.sendUpdate.bind(this))
}
+ //
+ // Constructor helpers
+ //
+
+ initializeProvider () {
+ let provider = MetaMaskProvider({
+ static: {
+ eth_syncing: false,
+ web3_clientVersion: `MetaMask/v${version}`,
+ },
+ rpcUrl: this.configManager.getCurrentRpcAddress(),
+ // account mgmt
+ getAccounts: (cb) => {
+ let selectedAddress = this.preferencesController.getSelectedAddress()
+ let result = selectedAddress ? [selectedAddress] : []
+ cb(null, result)
+ },
+ // tx signing
+ processTransaction: (txParams, cb) => this.newUnapprovedTransaction(txParams, cb),
+ // msg signing
+ processMessage: this.newUnsignedMessage.bind(this),
+ })
+ return provider
+ }
+
+ initPublicConfigStore () {
+ // get init state
+ const publicConfigStore = new ObservableStore()
+
+ // sync publicConfigStore with transform
+ pipe(
+ this.store,
+ storeTransform(selectPublicState.bind(this)),
+ publicConfigStore
+ )
+
+ function selectPublicState(state) {
+ const result = { selectedAddress: undefined }
+ try {
+ result.selectedAddress = state.PreferencesController.selectedAddress
+ result.networkVersion = this.getNetworkState()
+ } catch (_) {}
+ return result
+ }
+
+ return publicConfigStore
+ }
+
+ //
+ // State Management
+ //
+
getState () {
+
+ const wallet = this.configManager.getWallet()
+ const vault = this.keyringController.store.getState().vault
+ const isInitialized = (!!wallet || !!vault)
return extend(
+ {
+ isInitialized,
+ },
+ this.networkStore.getState(),
this.ethStore.getState(),
- this.idStore.getState(),
- this.configManager.getConfig()
+ this.txManager.memStore.getState(),
+ this.messageManager.memStore.getState(),
+ this.keyringController.memStore.getState(),
+ this.preferencesController.store.getState(),
+ this.currencyController.store.getState(),
+ this.noticeController.memStore.getState(),
+ // config manager
+ this.configManager.getConfig(),
+ this.shapeshiftController.store.getState(),
+ {
+ lostAccounts: this.configManager.getLostAccounts(),
+ seedWords: this.configManager.getSeedWords(),
+ }
)
}
+ //
+ // Remote Features
+ //
+
getApi () {
- const idStore = this.idStore
+ const keyringController = this.keyringController
+ const preferencesController = this.preferencesController
+ const txManager = this.txManager
+ const messageManager = this.messageManager
+ const noticeController = this.noticeController
return {
- getState: (cb) => { cb(null, this.getState()) },
- setRpcTarget: this.setRpcTarget.bind(this),
- setProviderType: this.setProviderType.bind(this),
- useEtherscanProvider: this.useEtherscanProvider.bind(this),
- agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
- setCurrentFiat: this.setCurrentFiat.bind(this),
- agreeToEthWarning: this.agreeToEthWarning.bind(this),
-
- // forward directly to idStore
- createNewVault: idStore.createNewVault.bind(idStore),
- recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
- submitPassword: idStore.submitPassword.bind(idStore),
- setSelectedAddress: idStore.setSelectedAddress.bind(idStore),
- approveTransaction: idStore.approveTransaction.bind(idStore),
- cancelTransaction: idStore.cancelTransaction.bind(idStore),
- signMessage: idStore.signMessage.bind(idStore),
- cancelMessage: idStore.cancelMessage.bind(idStore),
- setLocked: idStore.setLocked.bind(idStore),
- clearSeedWordCache: idStore.clearSeedWordCache.bind(idStore),
- exportAccount: idStore.exportAccount.bind(idStore),
- revealAccount: idStore.revealAccount.bind(idStore),
- saveAccountLabel: idStore.saveAccountLabel.bind(idStore),
- tryPassword: idStore.tryPassword.bind(idStore),
- recoverSeed: idStore.recoverSeed.bind(idStore),
+ // etc
+ getState: (cb) => cb(null, this.getState()),
+ setRpcTarget: this.setRpcTarget.bind(this),
+ setProviderType: this.setProviderType.bind(this),
+ useEtherscanProvider: this.useEtherscanProvider.bind(this),
+ setCurrentCurrency: this.setCurrentCurrency.bind(this),
+ setGasMultiplier: this.setGasMultiplier.bind(this),
+ markAccountsFound: this.markAccountsFound.bind(this),
// coinbase
buyEth: this.buyEth.bind(this),
// shapeshift
createShapeShiftTx: this.createShapeShiftTx.bind(this),
+
+ // primary HD keyring management
+ addNewAccount: this.addNewAccount.bind(this),
+ placeSeedWords: this.placeSeedWords.bind(this),
+ clearSeedWordCache: this.clearSeedWordCache.bind(this),
+ importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
+
+ // vault management
+ submitPassword: this.submitPassword.bind(this),
+
+ // PreferencesController
+ setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController),
+
+ // KeyringController
+ setLocked: nodeify(keyringController.setLocked).bind(keyringController),
+ createNewVaultAndKeychain: nodeify(keyringController.createNewVaultAndKeychain).bind(keyringController),
+ createNewVaultAndRestore: nodeify(keyringController.createNewVaultAndRestore).bind(keyringController),
+ addNewKeyring: nodeify(keyringController.addNewKeyring).bind(keyringController),
+ saveAccountLabel: nodeify(keyringController.saveAccountLabel).bind(keyringController),
+ exportAccount: nodeify(keyringController.exportAccount).bind(keyringController),
+
+ // txManager
+ approveTransaction: txManager.approveTransaction.bind(txManager),
+ cancelTransaction: txManager.cancelTransaction.bind(txManager),
+
+ // messageManager
+ signMessage: this.signMessage.bind(this),
+ cancelMessage: messageManager.rejectMsg.bind(messageManager),
+
+ // notices
+ checkNotices: noticeController.updateNoticesList.bind(noticeController),
+ markNoticeRead: noticeController.markNoticeRead.bind(noticeController),
}
}
- setupProviderConnection (stream, originDomain) {
- stream.on('data', this.onRpcRequest.bind(this, stream, originDomain))
+ setupUntrustedCommunication (connectionStream, originDomain) {
+ // setup multiplexing
+ var mx = setupMultiplex(connectionStream)
+ // connect features
+ this.setupProviderConnection(mx.createStream('provider'), originDomain)
+ this.setupPublicConfig(mx.createStream('publicConfig'))
}
- onRpcRequest (stream, originDomain, request) {
- var payloads = Array.isArray(request) ? request : [request]
- payloads.forEach(function (payload) {
- // Append origin to rpc payload
- payload.origin = originDomain
- // Append origin to signature request
- if (payload.method === 'eth_sendTransaction') {
- payload.params[0].origin = originDomain
- } else if (payload.method === 'eth_sign') {
- payload.params.push({ origin: originDomain })
- }
- })
+ setupTrustedCommunication (connectionStream, originDomain) {
+ // setup multiplexing
+ var mx = setupMultiplex(connectionStream)
+ // connect features
+ this.setupControllerConnection(mx.createStream('controller'))
+ this.setupProviderConnection(mx.createStream('provider'), originDomain)
+ }
- // handle rpc request
- this.provider.sendAsync(request, function onPayloadHandled (err, response) {
- logger(err, request, response)
- if (response) {
- try {
- stream.write(response)
- } catch (err) {
- logger(err)
- }
- }
+ setupControllerConnection (outStream) {
+ const api = this.getApi()
+ const dnode = Dnode(api)
+ outStream.pipe(dnode).pipe(outStream)
+ dnode.on('remote', (remote) => {
+ // push updates to popup
+ const sendUpdate = remote.sendUpdate.bind(remote)
+ this.on('update', sendUpdate)
})
+ }
+ setupProviderConnection (outStream, originDomain) {
+ streamIntoProvider(outStream, this.provider, logger)
function logger (err, request, response) {
if (err) return console.error(err)
- if (!request.isMetamaskInternal) {
- if (global.METAMASK_DEBUG) {
- console.log(`RPC (${originDomain}):`, request, '->', response)
- }
- if (response.error) {
- console.error('Error in RPC response:\n', response.error)
- }
+ if (response.error) {
+ console.error('Error in RPC response:\n', response.error)
+ }
+ if (request.isMetamaskInternal) return
+ if (global.METAMASK_DEBUG) {
+ console.log(`RPC (${originDomain}):`, request, '->', response)
}
}
}
- sendUpdate () {
- if (this.remote) {
- this.remote.sendUpdate(this.getState())
- }
+ setupPublicConfig (outStream) {
+ pipe(
+ this.publicConfigStore,
+ outStream
+ )
}
- initializeProvider (opts) {
- const idStore = this.idStore
+ sendUpdate () {
+ this.emit('update', this.getState())
+ }
- var providerOpts = {
- rpcUrl: this.configManager.getCurrentRpcAddress(),
- // account mgmt
- getAccounts: (cb) => {
- var selectedAddress = idStore.getSelectedAddress()
- var result = selectedAddress ? [selectedAddress] : []
- cb(null, result)
- },
- // tx signing
- approveTransaction: this.newUnsignedTransaction.bind(this),
- signTransaction: idStore.signTransaction.bind(idStore),
- // msg signing
- approveMessage: this.newUnsignedMessage.bind(this),
- signMessage: idStore.signMessage.bind(idStore),
- }
+ //
+ // Vault Management
+ //
- var provider = MetaMaskProvider(providerOpts)
- var web3 = new Web3(provider)
- idStore.web3 = web3
- idStore.getNetwork()
+ submitPassword (password, cb) {
+ this.migrateOldVaultIfAny(password)
+ .then(this.keyringController.submitPassword.bind(this.keyringController, password))
+ .then((newState) => { cb(null, newState) })
+ .catch((reason) => { cb(reason) })
+ }
- provider.on('block', this.processBlock.bind(this))
- provider.on('error', idStore.getNetwork.bind(idStore))
+ //
+ // Opinionated Keyring Management
+ //
- return provider
+ addNewAccount (cb) {
+ const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
+ if (!primaryKeyring) return cb(new Error('MetamaskController - No HD Key Tree found'))
+ promiseToCallback(this.keyringController.addNewAccount(primaryKeyring))(cb)
}
- initPublicConfigStore () {
- // get init state
- var initPublicState = extend(
- idStoreToPublic(this.idStore.getState()),
- configToPublic(this.configManager.getConfig())
- )
+ // Adds the current vault's seed words to the UI's state tree.
+ //
+ // Used when creating a first vault, to allow confirmation.
+ // Also used when revealing the seed words in the confirmation view.
+ placeSeedWords (cb) {
+ const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
+ if (!primaryKeyring) return cb(new Error('MetamaskController - No HD Key Tree found'))
+ primaryKeyring.serialize()
+ .then((serialized) => {
+ const seedWords = serialized.mnemonic
+ this.configManager.setSeedWords(seedWords)
+ cb()
+ })
+ }
- var publicConfigStore = new HostStore(initPublicState)
+ // ClearSeedWordCache
+ //
+ // Removes the primary account's seed words from the UI's state tree,
+ // ensuring they are only ever available in the background process.
+ clearSeedWordCache (cb) {
+ this.configManager.setSeedWords(null)
+ cb(null, this.preferencesController.getSelectedAddress())
+ }
- // subscribe to changes
- this.configManager.subscribe(function (state) {
- storeSetFromObj(publicConfigStore, configToPublic(state))
- })
- this.idStore.on('update', function (state) {
- storeSetFromObj(publicConfigStore, idStoreToPublic(state))
+ importAccountWithStrategy (strategy, args, cb) {
+ accountImporter.importAccount(strategy, args)
+ .then((privateKey) => {
+ return this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
})
+ .then(keyring => keyring.getAccounts())
+ .then((accounts) => this.preferencesController.setSelectedAddress(accounts[0]))
+ .then(() => { cb(null, this.keyringController.fullUpdate()) })
+ .catch((reason) => { cb(reason) })
+ }
- // idStore substate
- function idStoreToPublic (state) {
- return {
- selectedAddress: state.selectedAddress,
- }
- }
- // config substate
- function configToPublic (state) {
- return {
- provider: state.provider,
- selectedAddress: state.selectedAccount,
- }
- }
- // dump obj into store
- function storeSetFromObj (store, obj) {
- Object.keys(obj).forEach(function (key) {
- store.set(key, obj[key])
+
+ //
+ // Identity Management
+ //
+
+ newUnapprovedTransaction (txParams, cb) {
+ const self = this
+ self.txManager.addUnapprovedTransaction(txParams, (err, txMeta) => {
+ if (err) return cb(err)
+ self.sendUpdate()
+ self.opts.showUnapprovedTx(txMeta)
+ // listen for tx completion (success, fail)
+ self.txManager.once(`${txMeta.id}:finished`, (status) => {
+ switch (status) {
+ case 'submitted':
+ return cb(null, txMeta.hash)
+ case 'rejected':
+ return cb(new Error('MetaMask Tx Signature: User denied transaction signature.'))
+ default:
+ return cb(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(txMeta.txParams)}`))
+ }
})
- }
+ })
+ }
- return publicConfigStore
+ newUnsignedMessage (msgParams, cb) {
+ let msgId = this.messageManager.addUnapprovedMessage(msgParams)
+ this.sendUpdate()
+ this.opts.showUnconfirmedMessage()
+ this.messageManager.once(`${msgId}:finished`, (data) => {
+ switch (data.status) {
+ case 'signed':
+ return cb(null, data.rawSig)
+ case 'rejected':
+ return cb(new Error('MetaMask Message Signature: User denied transaction signature.'))
+ default:
+ return cb(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
+ }
+ })
}
- newUnsignedTransaction (txParams, onTxDoneCb) {
- const idStore = this.idStore
- var state = idStore.getState()
+ signMessage (msgParams, cb) {
+ const msgId = msgParams.metamaskId
+ promiseToCallback(
+ // sets the status op the message to 'approved'
+ // and removes the metamaskId for signing
+ this.messageManager.approveMessage(msgParams)
+ .then((cleanMsgParams) => {
+ // signs the message
+ return this.keyringController.signMessage(cleanMsgParams)
+ })
+ .then((rawSig) => {
+ // tells the listener that the message has been signed
+ // and can be returned to the dapp
+ this.messageManager.setMsgStatusSigned(msgId, rawSig)
+ })
+ )(cb)
+ }
- // It's locked
- if (!state.isUnlocked) {
- this.opts.unlockAccountMessage()
- idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, noop)
- // It's unlocked
- } else {
- idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
- if (err) return onTxDoneCb(err)
- this.opts.showUnconfirmedTx(txParams, txData, onTxDoneCb)
- })
- }
+ markAccountsFound (cb) {
+ this.configManager.setLostAccounts([])
+ this.sendUpdate()
+ cb(null, this.getState())
}
- newUnsignedMessage (msgParams, cb) {
- var state = this.idStore.getState()
- if (!state.isUnlocked) {
- this.idStore.addUnconfirmedMessage(msgParams, cb)
- this.opts.unlockAccountMessage()
- } else {
- this.addUnconfirmedMessage(msgParams, cb)
+ // Migrate Old Vault If Any
+ // @string password
+ //
+ // returns Promise()
+ //
+ // Temporary step used when logging in.
+ // Checks if old style (pre-3.0.0) Metamask Vault exists.
+ // If so, persists that vault in the new vault format
+ // with the provided password, so the other unlock steps
+ // may be completed without interruption.
+ migrateOldVaultIfAny (password) {
+
+ if (!this.checkIfShouldMigrate()) {
+ return Promise.resolve(password)
}
+
+ const keyringController = this.keyringController
+
+ return this.idStoreMigrator.migratedVaultForPassword(password)
+ .then(this.restoreOldVaultAccounts.bind(this))
+ .then(this.restoreOldLostAccounts.bind(this))
+ .then(keyringController.persistAllKeyrings.bind(keyringController, password))
+ .then(() => password)
}
- addUnconfirmedMessage (msgParams, cb) {
- const idStore = this.idStore
- const msgId = idStore.addUnconfirmedMessage(msgParams, cb)
- this.opts.showUnconfirmedMessage(msgParams, msgId)
+ checkIfShouldMigrate() {
+ return !!this.configManager.getWallet() && !this.configManager.getVault()
}
- setupPublicConfig (stream) {
- var storeStream = this.publicConfigStore.createStream()
- stream.pipe(storeStream).pipe(stream)
+ restoreOldVaultAccounts(migratorOutput) {
+ const { serialized } = migratorOutput
+ return this.keyringController.restoreKeyring(serialized)
+ .then(() => migratorOutput)
}
- // Log blocks
- processBlock (block) {
- if (global.METAMASK_DEBUG) {
- console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
+ restoreOldLostAccounts(migratorOutput) {
+ const { lostAccounts } = migratorOutput
+ if (lostAccounts) {
+ this.configManager.setLostAccounts(lostAccounts.map(acct => acct.address))
+ return this.importLostAccounts(migratorOutput)
}
- this.verifyNetwork()
+ return Promise.resolve(migratorOutput)
}
- verifyNetwork () {
- // Check network when restoring connectivity:
- if (this.idStore._currentState.network === 'loading') {
- this.idStore.getNetwork()
- }
+ // IMPORT LOST ACCOUNTS
+ // @Object with key lostAccounts: @Array accounts <{ address, privateKey }>
+ // Uses the array's private keys to create a new Simple Key Pair keychain
+ // and add it to the keyring controller.
+ importLostAccounts ({ lostAccounts }) {
+ const privKeys = lostAccounts.map(acct => acct.privateKey)
+ return this.keyringController.restoreKeyring({
+ type: 'Simple Key Pair',
+ data: privKeys,
+ })
}
+ //
// config
//
- agreeToDisclaimer (cb) {
- try {
- this.configManager.setConfirmed(true)
- cb()
- } catch (e) {
- cb(e)
+ // Log blocks
+ logBlock (block) {
+ if (global.METAMASK_DEBUG) {
+ console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
}
+ this.verifyNetwork()
}
- setCurrentFiat (fiat, cb) {
+ setCurrentCurrency (currencyCode, cb) {
try {
- this.configManager.setCurrentFiat(fiat)
- this.configManager.updateConversionRate()
- this.scheduleConversionInterval()
+ this.currencyController.setCurrentCurrency(currencyCode)
+ this.currencyController.updateConversionRate()
const data = {
- conversionRate: this.configManager.getConversionRate(),
- currentFiat: this.configManager.getCurrentFiat(),
- conversionDate: this.configManager.getConversionDate(),
+ conversionRate: this.currencyController.getConversionRate(),
+ currentFiat: this.currencyController.getCurrentCurrency(),
+ conversionDate: this.currencyController.getConversionDate(),
}
- cb(data)
- } catch (e) {
- cb(null, e)
+ cb(null, data)
+ } catch (err) {
+ cb(err)
}
}
- scheduleConversionInterval () {
- if (this.conversionInterval) {
- clearInterval(this.conversionInterval)
+ buyEth (address, amount) {
+ if (!amount) amount = '5'
+
+ const network = this.getNetworkState()
+ let url
+
+ switch (network) {
+ case '1':
+ url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
+ break
+
+ case '3':
+ url = 'https://faucet.metamask.io/'
+ break
}
- this.conversionInterval = setInterval(() => {
- this.configManager.updateConversionRate()
- }, 300000)
+
+ if (url) extension.tabs.create({ url })
}
- agreeToEthWarning (cb) {
+ createShapeShiftTx (depositAddress, depositType) {
+ this.shapeshiftController.createShapeShiftTx(depositAddress, depositType)
+ }
+
+ setGasMultiplier (gasMultiplier, cb) {
try {
- this.configManager.setShouldntShowWarning()
+ this.txManager.setGasMultiplier(gasMultiplier)
cb()
- } catch (e) {
- cb(e)
+ } catch (err) {
+ cb(err)
}
}
- // called from popup
+ //
+ // network
+ //
+
+ verifyNetwork () {
+ // Check network when restoring connectivity:
+ if (this.isNetworkLoading()) this.lookupNetwork()
+ }
+
setRpcTarget (rpcTarget) {
this.configManager.setRpcTarget(rpcTarget)
extension.runtime.reload()
- this.idStore.getNetwork()
+ this.lookupNetwork()
}
setProviderType (type) {
this.configManager.setProviderType(type)
extension.runtime.reload()
- this.idStore.getNetwork()
+ this.lookupNetwork()
}
useEtherscanProvider () {
@@ -305,24 +597,33 @@ module.exports = class MetamaskController {
extension.runtime.reload()
}
- buyEth (address, amount) {
- if (!amount) amount = '5'
+ getNetworkState () {
+ return this.networkStore.getState().network
+ }
- var network = this.idStore._currentState.network
- var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
+ setNetworkState (network) {
+ return this.networkStore.updateState({ network })
+ }
+
+ isNetworkLoading () {
+ return this.getNetworkState() === 'loading'
+ }
- if (network === '2') {
- url = 'https://testfaucet.metamask.io/'
+ lookupNetwork (err) {
+ if (err) {
+ this.setNetworkState('loading')
}
- extension.tabs.create({
- url,
+ this.ethQuery.sendAsync({ method: 'net_version' }, (err, network) => {
+ if (err) {
+ this.setNetworkState('loading')
+ return
+ }
+ if (global.METAMASK_DEBUG) {
+ console.log('web3.getNetwork returned ' + network)
+ }
+ this.setNetworkState(network)
})
}
- createShapeShiftTx (depositAddress, depositType) {
- this.configManager.createShapeShiftTx(depositAddress, depositType)
- }
}
-
-function noop () {}
diff --git a/app/scripts/migrations/002.js b/app/scripts/migrations/002.js
index 0b654f825..36a870342 100644
--- a/app/scripts/migrations/002.js
+++ b/app/scripts/migrations/002.js
@@ -1,13 +1,20 @@
+const version = 2
+
+const clone = require('clone')
+
+
module.exports = {
- version: 2,
+ version,
- migrate: function (data) {
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
try {
- if (data.config.provider.type === 'etherscan') {
- data.config.provider.type = 'rpc'
- data.config.provider.rpcTarget = 'https://rpc.metamask.io/'
+ if (versionedData.data.config.provider.type === 'etherscan') {
+ versionedData.data.config.provider.type = 'rpc'
+ versionedData.data.config.provider.rpcTarget = 'https://rpc.metamask.io/'
}
} catch (e) {}
- return data
+ return Promise.resolve(versionedData)
},
}
diff --git a/app/scripts/migrations/003.js b/app/scripts/migrations/003.js
index 617c55c09..1893576ad 100644
--- a/app/scripts/migrations/003.js
+++ b/app/scripts/migrations/003.js
@@ -1,15 +1,20 @@
-var oldTestRpc = 'https://rawtestrpc.metamask.io/'
-var newTestRpc = 'https://testrpc.metamask.io/'
+const version = 3
+const oldTestRpc = 'https://rawtestrpc.metamask.io/'
+const newTestRpc = 'https://testrpc.metamask.io/'
+
+const clone = require('clone')
module.exports = {
- version: 3,
+ version,
- migrate: function (data) {
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
try {
- if (data.config.provider.rpcTarget === oldTestRpc) {
- data.config.provider.rpcTarget = newTestRpc
+ if (versionedData.data.config.provider.rpcTarget === oldTestRpc) {
+ versionedData.data.config.provider.rpcTarget = newTestRpc
}
} catch (e) {}
- return data
+ return Promise.resolve(versionedData)
},
}
diff --git a/app/scripts/migrations/004.js b/app/scripts/migrations/004.js
index 1329a1eed..405d932f8 100644
--- a/app/scripts/migrations/004.js
+++ b/app/scripts/migrations/004.js
@@ -1,22 +1,28 @@
+const version = 4
+
+const clone = require('clone')
+
module.exports = {
- version: 4,
+ version,
- migrate: function (data) {
+ migrate: function (versionedData) {
+ let safeVersionedData = clone(versionedData)
+ safeVersionedData.meta.version = version
try {
- if (data.config.provider.type !== 'rpc') return data
- switch (data.config.provider.rpcTarget) {
+ if (safeVersionedData.data.config.provider.type !== 'rpc') return Promise.resolve(safeVersionedData)
+ switch (safeVersionedData.data.config.provider.rpcTarget) {
case 'https://testrpc.metamask.io/':
- data.config.provider = {
+ safeVersionedData.data.config.provider = {
type: 'testnet',
}
break
case 'https://rpc.metamask.io/':
- data.config.provider = {
+ safeVersionedData.data.config.provider = {
type: 'mainnet',
}
break
}
} catch (_) {}
- return data
+ return Promise.resolve(safeVersionedData)
},
}
diff --git a/app/scripts/migrations/005.js b/app/scripts/migrations/005.js
new file mode 100644
index 000000000..e4b84f460
--- /dev/null
+++ b/app/scripts/migrations/005.js
@@ -0,0 +1,44 @@
+const version = 5
+
+/*
+
+This migration moves state from the flat state trie into KeyringController substate
+
+*/
+
+const extend = require('xtend')
+const clone = require('clone')
+
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = selectSubstateForKeyringController(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn('MetaMask Migration #5' + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function selectSubstateForKeyringController (state) {
+ const config = state.config
+ const newState = extend(state, {
+ KeyringController: {
+ vault: state.vault,
+ selectedAccount: config.selectedAccount,
+ walletNicknames: state.walletNicknames,
+ },
+ })
+ delete newState.vault
+ delete newState.walletNicknames
+ delete newState.config.selectedAccount
+
+ return newState
+}
diff --git a/app/scripts/migrations/006.js b/app/scripts/migrations/006.js
new file mode 100644
index 000000000..94d1b6ecd
--- /dev/null
+++ b/app/scripts/migrations/006.js
@@ -0,0 +1,43 @@
+const version = 6
+
+/*
+
+This migration moves KeyringController.selectedAddress to PreferencesController.selectedAddress
+
+*/
+
+const extend = require('xtend')
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = migrateState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function migrateState (state) {
+ const keyringSubstate = state.KeyringController
+
+ // add new state
+ const newState = extend(state, {
+ PreferencesController: {
+ selectedAddress: keyringSubstate.selectedAccount,
+ },
+ })
+
+ // rm old state
+ delete newState.KeyringController.selectedAccount
+
+ return newState
+}
diff --git a/app/scripts/migrations/007.js b/app/scripts/migrations/007.js
new file mode 100644
index 000000000..236e35224
--- /dev/null
+++ b/app/scripts/migrations/007.js
@@ -0,0 +1,40 @@
+const version = 7
+
+/*
+
+This migration breaks out the TransactionManager substate
+
+*/
+
+const extend = require('xtend')
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = extend(state, {
+ TransactionManager: {
+ transactions: state.transactions || [],
+ gasMultiplier: state.gasMultiplier || 1,
+ },
+ })
+ delete newState.transactions
+ delete newState.gasMultiplier
+
+ return newState
+}
diff --git a/app/scripts/migrations/008.js b/app/scripts/migrations/008.js
new file mode 100644
index 000000000..cd5e95d22
--- /dev/null
+++ b/app/scripts/migrations/008.js
@@ -0,0 +1,38 @@
+const version = 8
+
+/*
+
+This migration breaks out the NoticeController substate
+
+*/
+
+const extend = require('xtend')
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = extend(state, {
+ NoticeController: {
+ noticesList: state.noticesList || [],
+ },
+ })
+ delete newState.noticesList
+
+ return newState
+}
diff --git a/app/scripts/migrations/009.js b/app/scripts/migrations/009.js
new file mode 100644
index 000000000..4612fefdc
--- /dev/null
+++ b/app/scripts/migrations/009.js
@@ -0,0 +1,43 @@
+const version = 9
+
+/*
+
+This migration breaks out the CurrencyController substate
+
+*/
+
+const merge = require('deep-extend')
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = merge({}, state, {
+ CurrencyController: {
+ currentCurrency: state.currentFiat || state.fiatCurrency || 'USD',
+ conversionRate: state.conversionRate,
+ conversionDate: state.conversionDate,
+ },
+ })
+ delete newState.currentFiat
+ delete newState.fiatCurrency
+ delete newState.conversionRate
+ delete newState.conversionDate
+
+ return newState
+}
diff --git a/app/scripts/migrations/010.js b/app/scripts/migrations/010.js
new file mode 100644
index 000000000..48a841bc1
--- /dev/null
+++ b/app/scripts/migrations/010.js
@@ -0,0 +1,38 @@
+const version = 10
+
+/*
+
+This migration breaks out the CurrencyController substate
+
+*/
+
+const merge = require('deep-extend')
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = merge({}, state, {
+ ShapeShiftController: {
+ shapeShiftTxList: state.shapeShiftTxList || [],
+ },
+ })
+ delete newState.shapeShiftTxList
+
+ return newState
+}
diff --git a/app/scripts/migrations/011.js b/app/scripts/migrations/011.js
new file mode 100644
index 000000000..bf283ef98
--- /dev/null
+++ b/app/scripts/migrations/011.js
@@ -0,0 +1,33 @@
+const version = 11
+
+/*
+
+This migration breaks out the CurrencyController substate
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+
+ migrate: function (originalVersionedData) {
+ let versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ const newState = transformState(state)
+ versionedData.data = newState
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ const newState = state
+ delete newState.TOSHash
+ delete newState.isDisclaimerConfirmed
+ return newState
+}
diff --git a/app/scripts/migrations/_multi-keyring.js b/app/scripts/migrations/_multi-keyring.js
new file mode 100644
index 000000000..04c966d4d
--- /dev/null
+++ b/app/scripts/migrations/_multi-keyring.js
@@ -0,0 +1,51 @@
+const version = 5
+
+/*
+
+This is an incomplete migration bc it requires post-decrypted data
+which we dont have access to at the time of this writing.
+
+*/
+
+const ObservableStore = require('obs-store')
+const ConfigManager = require('../../app/scripts/lib/config-manager')
+const IdentityStoreMigrator = require('../../app/scripts/lib/idStore-migrator')
+const KeyringController = require('../../app/scripts/lib/keyring-controller')
+
+const password = 'obviously not correct'
+
+module.exports = {
+ version,
+
+ migrate: function (versionedData) {
+ versionedData.meta.version = version
+
+ let store = new ObservableStore(versionedData.data)
+ let configManager = new ConfigManager({ store })
+ let idStoreMigrator = new IdentityStoreMigrator({ configManager })
+ let keyringController = new KeyringController({
+ configManager: configManager,
+ })
+
+ // attempt to migrate to multiVault
+ return idStoreMigrator.migratedVaultForPassword(password)
+ .then((result) => {
+ // skip if nothing to migrate
+ if (!result) return Promise.resolve(versionedData)
+ delete versionedData.data.wallet
+ // create new keyrings
+ const privKeys = result.lostAccounts.map(acct => acct.privateKey)
+ return Promise.all([
+ keyringController.restoreKeyring(result.serialized),
+ keyringController.restoreKeyring({ type: 'Simple Key Pair', data: privKeys }),
+ ]).then(() => {
+ return keyringController.persistAllKeyrings(password)
+ }).then(() => {
+ // copy result on to state object
+ versionedData.data = store.get()
+ return Promise.resolve(versionedData)
+ })
+ })
+
+ },
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
new file mode 100644
index 000000000..a3dd48c17
--- /dev/null
+++ b/app/scripts/migrations/index.js
@@ -0,0 +1,25 @@
+/* The migrator has two methods the user should be concerned with:
+ *
+ * getData(), which returns the app-consumable data object
+ * saveData(), which persists the app-consumable data object.
+ */
+
+// Migrations must start at version 1 or later.
+// They are objects with a `version` number
+// and a `migrate` function.
+//
+// The `migrate` function receives the previous
+// config data format, and returns the new one.
+
+module.exports = [
+ require('./002'),
+ require('./003'),
+ require('./004'),
+ require('./005'),
+ require('./006'),
+ require('./007'),
+ require('./008'),
+ require('./009'),
+ require('./010'),
+ require('./011'),
+]
diff --git a/app/scripts/notice-controller.js b/app/scripts/notice-controller.js
new file mode 100644
index 000000000..0d72760fe
--- /dev/null
+++ b/app/scripts/notice-controller.js
@@ -0,0 +1,94 @@
+const EventEmitter = require('events').EventEmitter
+const extend = require('xtend')
+const ObservableStore = require('obs-store')
+const hardCodedNotices = require('../../notices/notices.json')
+
+module.exports = class NoticeController extends EventEmitter {
+
+ constructor (opts) {
+ super()
+ this.noticePoller = null
+ const initState = extend({
+ noticesList: [],
+ }, opts.initState)
+ this.store = new ObservableStore(initState)
+ this.memStore = new ObservableStore({})
+ this.store.subscribe(() => this._updateMemstore())
+ }
+
+ getNoticesList () {
+ return this.store.getState().noticesList
+ }
+
+ getUnreadNotices () {
+ const notices = this.getNoticesList()
+ return notices.filter((notice) => notice.read === false)
+ }
+
+ getLatestUnreadNotice () {
+ const unreadNotices = this.getUnreadNotices()
+ return unreadNotices[unreadNotices.length - 1]
+ }
+
+ setNoticesList (noticesList) {
+ this.store.updateState({ noticesList })
+ return Promise.resolve(true)
+ }
+
+ markNoticeRead (noticeToMark, cb) {
+ cb = cb || function (err) { if (err) throw err }
+ try {
+ var notices = this.getNoticesList()
+ var index = notices.findIndex((currentNotice) => currentNotice.id === noticeToMark.id)
+ notices[index].read = true
+ this.setNoticesList(notices)
+ const latestNotice = this.getLatestUnreadNotice()
+ cb(null, latestNotice)
+ } catch (err) {
+ cb(err)
+ }
+ }
+
+ updateNoticesList () {
+ return this._retrieveNoticeData().then((newNotices) => {
+ var oldNotices = this.getNoticesList()
+ var combinedNotices = this._mergeNotices(oldNotices, newNotices)
+ return Promise.resolve(this.setNoticesList(combinedNotices))
+ })
+ }
+
+ startPolling () {
+ if (this.noticePoller) {
+ clearInterval(this.noticePoller)
+ }
+ this.noticePoller = setInterval(() => {
+ this.noticeController.updateNoticesList()
+ }, 300000)
+ }
+
+ _mergeNotices (oldNotices, newNotices) {
+ var noticeMap = this._mapNoticeIds(oldNotices)
+ newNotices.forEach((notice) => {
+ if (noticeMap.indexOf(notice.id) === -1) {
+ oldNotices.push(notice)
+ }
+ })
+ return oldNotices
+ }
+
+ _mapNoticeIds (notices) {
+ return notices.map((notice) => notice.id)
+ }
+
+ _retrieveNoticeData () {
+ // Placeholder for the API.
+ return Promise.resolve(hardCodedNotices)
+ }
+
+ _updateMemstore () {
+ const lastUnreadNotice = this.getLatestUnreadNotice()
+ const noActiveNotices = !lastUnreadNotice
+ this.memStore.updateState({ lastUnreadNotice, noActiveNotices })
+ }
+
+}
diff --git a/app/scripts/popup-core.js b/app/scripts/popup-core.js
new file mode 100644
index 000000000..0c97a5d19
--- /dev/null
+++ b/app/scripts/popup-core.js
@@ -0,0 +1,63 @@
+const EventEmitter = require('events').EventEmitter
+const Dnode = require('dnode')
+const Web3 = require('web3')
+const MetaMaskUi = require('../../ui')
+const StreamProvider = require('web3-stream-provider')
+const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
+
+
+module.exports = initializePopup
+
+
+function initializePopup (connectionStream) {
+ // setup app
+ connectToAccountManager(connectionStream, setupApp)
+}
+
+function connectToAccountManager (connectionStream, cb) {
+ // setup communication with background
+ // setup multiplexing
+ var mx = setupMultiplex(connectionStream)
+ // connect features
+ setupControllerConnection(mx.createStream('controller'), cb)
+ setupWeb3Connection(mx.createStream('provider'))
+}
+
+function setupWeb3Connection (connectionStream) {
+ var providerStream = new StreamProvider()
+ providerStream.pipe(connectionStream).pipe(providerStream)
+ connectionStream.on('error', console.error.bind(console))
+ providerStream.on('error', console.error.bind(console))
+ global.web3 = new Web3(providerStream)
+}
+
+function setupControllerConnection (connectionStream, cb) {
+ // this is a really sneaky way of adding EventEmitter api
+ // to a bi-directional dnode instance
+ var eventEmitter = new EventEmitter()
+ var accountManagerDnode = Dnode({
+ sendUpdate: function (state) {
+ eventEmitter.emit('update', state)
+ },
+ })
+ connectionStream.pipe(accountManagerDnode).pipe(connectionStream)
+ accountManagerDnode.once('remote', function (accountManager) {
+ // setup push events
+ accountManager.on = eventEmitter.on.bind(eventEmitter)
+ cb(null, accountManager)
+ })
+}
+
+function setupApp (err, accountManager) {
+ if (err) {
+ alert(err.stack)
+ throw err
+ }
+
+ var container = document.getElementById('app-content')
+
+ MetaMaskUi({
+ container: container,
+ accountManager: accountManager,
+ })
+}
diff --git a/app/scripts/popup.js b/app/scripts/popup.js
index 20be15df7..62db68c10 100644
--- a/app/scripts/popup.js
+++ b/app/scripts/popup.js
@@ -1,95 +1,25 @@
-const url = require('url')
-const EventEmitter = require('events').EventEmitter
-const async = require('async')
-const Dnode = require('dnode')
-const Web3 = require('web3')
-const MetaMaskUi = require('../../ui')
-const MetaMaskUiCss = require('../../ui/css')
const injectCss = require('inject-css')
+const MetaMaskUiCss = require('../../ui/css')
+const startPopup = require('./popup-core')
const PortStream = require('./lib/port-stream.js')
-const StreamProvider = require('web3-stream-provider')
-const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
+const isPopupOrNotification = require('./lib/is-popup-or-notification')
const extension = require('./lib/extension')
+const notification = require('./lib/notifications')
-// setup app
var css = MetaMaskUiCss()
injectCss(css)
-async.parallel({
- currentDomain: getCurrentDomain,
- accountManager: connectToAccountManager,
-}, setupApp)
+var name = isPopupOrNotification()
+closePopupIfOpen(name)
+window.METAMASK_UI_TYPE = name
-function connectToAccountManager (cb) {
- // setup communication with background
- var pluginPort = extension.runtime.connect({name: 'popup'})
- var portStream = new PortStream(pluginPort)
- // setup multiplexing
- var mx = setupMultiplex(portStream)
- // connect features
- setupControllerConnection(mx.createStream('controller'), cb)
- setupWeb3Connection(mx.createStream('provider'))
-}
+var pluginPort = extension.runtime.connect({ name })
+var portStream = new PortStream(pluginPort)
-function setupWeb3Connection (stream) {
- var remoteProvider = new StreamProvider()
- remoteProvider.pipe(stream).pipe(remoteProvider)
- stream.on('error', console.error.bind(console))
- remoteProvider.on('error', console.error.bind(console))
- global.web3 = new Web3(remoteProvider)
-}
+startPopup(portStream)
-function setupControllerConnection (stream, cb) {
- var eventEmitter = new EventEmitter()
- var background = Dnode({
- sendUpdate: function (state) {
- eventEmitter.emit('update', state)
- },
- })
- stream.pipe(background).pipe(stream)
- background.once('remote', function (accountManager) {
- // setup push events
- accountManager.on = eventEmitter.on.bind(eventEmitter)
- cb(null, accountManager)
- })
-}
-
-function getCurrentDomain (cb) {
- const unknown = '<unknown>'
- if (!extension.tabs) return cb(null, unknown)
- extension.tabs.query({active: true, currentWindow: true}, function (results) {
- var activeTab = results[0]
- var currentUrl = activeTab && activeTab.url
- var currentDomain = url.parse(currentUrl).host
- if (!currentUrl) {
- return cb(null, unknown)
- }
- cb(null, currentDomain)
- })
-}
-
-function clearNotifications(){
- extension.notifications.getAll(function (object) {
- for (let notification in object){
- extension.notifications.clear(notification)
- }
- })
-}
-
-function setupApp (err, opts) {
- if (err) {
- alert(err.stack)
- throw err
+function closePopupIfOpen (name) {
+ if (name !== 'notification') {
+ notification.closePopup()
}
-
- clearNotifications()
-
- var container = document.getElementById('app-content')
-
- MetaMaskUi({
- container: container,
- accountManager: opts.accountManager,
- currentDomain: opts.currentDomain,
- networkVersion: opts.networkVersion,
- })
}
diff --git a/app/scripts/transaction-manager.js b/app/scripts/transaction-manager.js
new file mode 100644
index 000000000..6299091f2
--- /dev/null
+++ b/app/scripts/transaction-manager.js
@@ -0,0 +1,390 @@
+const EventEmitter = require('events')
+const async = require('async')
+const extend = require('xtend')
+const Semaphore = require('semaphore')
+const ObservableStore = require('obs-store')
+const ethUtil = require('ethereumjs-util')
+const BN = require('ethereumjs-util').BN
+const TxProviderUtil = require('./lib/tx-utils')
+const createId = require('./lib/random-id')
+
+module.exports = class TransactionManager extends EventEmitter {
+ constructor (opts) {
+ super()
+ this.store = new ObservableStore(extend({
+ transactions: [],
+ gasMultiplier: 1,
+ }, opts.initState))
+ this.memStore = new ObservableStore({})
+ this.networkStore = opts.networkStore || new ObservableStore({})
+ this.preferencesStore = opts.preferencesStore || new ObservableStore({})
+ this.txHistoryLimit = opts.txHistoryLimit
+ this.provider = opts.provider
+ this.blockTracker = opts.blockTracker
+ this.txProviderUtils = new TxProviderUtil(this.provider)
+ this.blockTracker.on('block', this.checkForTxInBlock.bind(this))
+ this.signEthTx = opts.signTransaction
+ this.nonceLock = Semaphore(1)
+
+ // memstore is computed from a few different stores
+ this._updateMemstore()
+ this.store.subscribe(() => this._updateMemstore() )
+ this.networkStore.subscribe(() => this._updateMemstore() )
+ this.preferencesStore.subscribe(() => this._updateMemstore() )
+ }
+
+ getState () {
+ return this.memStore.getState()
+ }
+
+ getNetwork () {
+ return this.networkStore.getState().network
+ }
+
+ getSelectedAddress () {
+ return this.preferencesStore.getState().selectedAddress
+ }
+
+ // Returns the tx list
+ getTxList () {
+ let network = this.getNetwork()
+ let fullTxList = this.store.getState().transactions
+ return fullTxList.filter(txMeta => txMeta.metamaskNetworkId === network)
+ }
+
+ getGasMultiplier () {
+ return this.store.getState().gasMultiplier
+ }
+
+ setGasMultiplier (gasMultiplier) {
+ return this.store.updateState({ gasMultiplier })
+ }
+
+ // Adds a tx to the txlist
+ addTx (txMeta) {
+ var txList = this.getTxList()
+ var txHistoryLimit = this.txHistoryLimit
+
+ // checks if the length of th tx history is
+ // longer then desired persistence limit
+ // and then if it is removes only confirmed
+ // or rejected tx's.
+ // not tx's that are pending or unapproved
+ if (txList.length > txHistoryLimit - 1) {
+ var index = txList.findIndex((metaTx) => metaTx.status === 'confirmed' || metaTx.status === 'rejected')
+ txList.splice(index, 1)
+ }
+ txList.push(txMeta)
+
+ this._saveTxList(txList)
+ this.once(`${txMeta.id}:signed`, function (txId) {
+ this.removeAllListeners(`${txMeta.id}:rejected`)
+ })
+ this.once(`${txMeta.id}:rejected`, function (txId) {
+ this.removeAllListeners(`${txMeta.id}:signed`)
+ })
+
+ this.emit('updateBadge')
+ this.emit(`${txMeta.id}:unapproved`, txMeta)
+ }
+
+ // gets tx by Id and returns it
+ getTx (txId, cb) {
+ var txList = this.getTxList()
+ var txMeta = txList.find(txData => txData.id === txId)
+ return cb ? cb(txMeta) : txMeta
+ }
+
+ //
+ updateTx (txMeta) {
+ var txId = txMeta.id
+ var txList = this.getTxList()
+ var index = txList.findIndex(txData => txData.id === txId)
+ txList[index] = txMeta
+ this._saveTxList(txList)
+ this.emit('update')
+ }
+
+ get unapprovedTxCount () {
+ return Object.keys(this.getUnapprovedTxList()).length
+ }
+
+ get pendingTxCount () {
+ return this.getTxsByMetaData('status', 'signed').length
+ }
+
+ addUnapprovedTransaction (txParams, done) {
+ let txMeta
+ async.waterfall([
+ // validate
+ (cb) => this.txProviderUtils.validateTxParams(txParams, cb),
+ // prepare txMeta
+ (cb) => {
+ // create txMeta obj with parameters and meta data
+ let time = (new Date()).getTime()
+ let txId = createId()
+ txParams.metamaskId = txId
+ txParams.metamaskNetworkId = this.getNetwork()
+ txMeta = {
+ id: txId,
+ time: time,
+ status: 'unapproved',
+ gasMultiplier: this.getGasMultiplier(),
+ metamaskNetworkId: this.getNetwork(),
+ txParams: txParams,
+ }
+ // calculate metadata for tx
+ this.txProviderUtils.analyzeGasUsage(txMeta, cb)
+ },
+ // save txMeta
+ (cb) => {
+ this.addTx(txMeta)
+ this.setMaxTxCostAndFee(txMeta)
+ cb(null, txMeta)
+ },
+ ], done)
+ }
+
+ setMaxTxCostAndFee (txMeta) {
+ var txParams = txMeta.txParams
+ var gasMultiplier = txMeta.gasMultiplier
+ var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16)
+ var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
+ gasPrice = gasPrice.mul(new BN(gasMultiplier * 100), 10).div(new BN(100, 10))
+ var txFee = gasCost.mul(gasPrice)
+ var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
+ var maxCost = txValue.add(txFee)
+ txMeta.txFee = txFee
+ txMeta.txValue = txValue
+ txMeta.maxCost = maxCost
+ this.updateTx(txMeta)
+ }
+
+ getUnapprovedTxList () {
+ var txList = this.getTxList()
+ return txList.filter((txMeta) => txMeta.status === 'unapproved')
+ .reduce((result, tx) => {
+ result[tx.id] = tx
+ return result
+ }, {})
+ }
+
+ approveTransaction (txId, cb = warn) {
+ const self = this
+ // approve
+ self.setTxStatusApproved(txId)
+ // only allow one tx at a time for atomic nonce usage
+ self.nonceLock.take(() => {
+ // begin signature process
+ async.waterfall([
+ (cb) => self.fillInTxParams(txId, cb),
+ (cb) => self.signTransaction(txId, cb),
+ (rawTx, cb) => self.publishTransaction(txId, rawTx, cb),
+ ], (err) => {
+ self.nonceLock.leave()
+ if (err) {
+ this.setTxStatusFailed(txId)
+ return cb(err)
+ }
+ cb()
+ })
+ })
+ }
+
+ cancelTransaction (txId, cb = warn) {
+ this.setTxStatusRejected(txId)
+ cb()
+ }
+
+ fillInTxParams (txId, cb) {
+ let txMeta = this.getTx(txId)
+ this.txProviderUtils.fillInTxParams(txMeta.txParams, (err) => {
+ if (err) return cb(err)
+ this.updateTx(txMeta)
+ cb()
+ })
+ }
+
+ signTransaction (txId, cb) {
+ let txMeta = this.getTx(txId)
+ let txParams = txMeta.txParams
+ let fromAddress = txParams.from
+ let ethTx = this.txProviderUtils.buildEthTxFromParams(txParams, txMeta.gasMultiplier)
+ this.signEthTx(ethTx, fromAddress).then(() => {
+ this.setTxStatusSigned(txMeta.id)
+ cb(null, ethUtil.bufferToHex(ethTx.serialize()))
+ }).catch((err) => {
+ cb(err)
+ })
+ }
+
+ publishTransaction (txId, rawTx, cb) {
+ this.txProviderUtils.publishTransaction(rawTx, (err, txHash) => {
+ if (err) return cb(err)
+ this.setTxHash(txId, txHash)
+ this.setTxStatusSubmitted(txId)
+ cb()
+ })
+ }
+
+ // receives a txHash records the tx as signed
+ setTxHash (txId, txHash) {
+ // Add the tx hash to the persisted meta-tx object
+ let txMeta = this.getTx(txId)
+ txMeta.hash = txHash
+ this.updateTx(txMeta)
+ }
+
+ /*
+ Takes an object of fields to search for eg:
+ var thingsToLookFor = {
+ to: '0x0..',
+ from: '0x0..',
+ status: 'signed',
+ }
+ and returns a list of tx with all
+ options matching
+
+ this is for things like filtering a the tx list
+ for only tx's from 1 account
+ or for filltering for all txs from one account
+ and that have been 'confirmed'
+ */
+ getFilteredTxList (opts) {
+ var filteredTxList
+ Object.keys(opts).forEach((key) => {
+ filteredTxList = this.getTxsByMetaData(key, opts[key], filteredTxList)
+ })
+ return filteredTxList
+ }
+
+ getTxsByMetaData (key, value, txList = this.getTxList()) {
+ return txList.filter((txMeta) => {
+ if (txMeta.txParams[key]) {
+ return txMeta.txParams[key] === value
+ } else {
+ return txMeta[key] === value
+ }
+ })
+ }
+
+ // STATUS METHODS
+ // get::set status
+
+ // should return the status of the tx.
+ getTxStatus (txId) {
+ const txMeta = this.getTx(txId)
+ return txMeta.status
+ }
+
+ // should update the status of the tx to 'rejected'.
+ setTxStatusRejected (txId) {
+ this._setTxStatus(txId, 'rejected')
+ }
+
+ // should update the status of the tx to 'approved'.
+ setTxStatusApproved (txId) {
+ this._setTxStatus(txId, 'approved')
+ }
+
+ // should update the status of the tx to 'signed'.
+ setTxStatusSigned (txId) {
+ this._setTxStatus(txId, 'signed')
+ }
+
+ // should update the status of the tx to 'submitted'.
+ setTxStatusSubmitted (txId) {
+ this._setTxStatus(txId, 'submitted')
+ }
+
+ // should update the status of the tx to 'confirmed'.
+ setTxStatusConfirmed (txId) {
+ this._setTxStatus(txId, 'confirmed')
+ }
+
+ setTxStatusFailed (txId) {
+ this._setTxStatus(txId, 'failed')
+ }
+
+ // merges txParams obj onto txData.txParams
+ // use extend to ensure that all fields are filled
+ updateTxParams (txId, txParams) {
+ var txMeta = this.getTx(txId)
+ txMeta.txParams = extend(txMeta.txParams, txParams)
+ this.updateTx(txMeta)
+ }
+
+ // checks if a signed tx is in a block and
+ // if included sets the tx status as 'confirmed'
+ checkForTxInBlock () {
+ var signedTxList = this.getFilteredTxList({status: 'submitted'})
+ if (!signedTxList.length) return
+ signedTxList.forEach((txMeta) => {
+ var txHash = txMeta.hash
+ var txId = txMeta.id
+ if (!txHash) {
+ txMeta.err = {
+ errCode: 'No hash was provided',
+ message: 'We had an error while submitting this transaction, please try again.',
+ }
+ this.updateTx(txMeta)
+ return this.setTxStatusFailed(txId)
+ }
+ this.txProviderUtils.query.getTransactionByHash(txHash, (err, txParams) => {
+ if (err || !txParams) {
+ if (!txParams) return
+ txMeta.err = {
+ isWarning: true,
+ errorCode: err,
+ message: 'There was a problem loading this transaction.',
+ }
+ this.updateTx(txMeta)
+ return console.error(err)
+ }
+ if (txParams.blockNumber) {
+ this.setTxStatusConfirmed(txId)
+ }
+ })
+ })
+ }
+
+ // PRIVATE METHODS
+
+ // Should find the tx in the tx list and
+ // update it.
+ // should set the status in txData
+ // - `'unapproved'` the user has not responded
+ // - `'rejected'` the user has responded no!
+ // - `'approved'` the user has approved the tx
+ // - `'signed'` the tx is signed
+ // - `'submitted'` the tx is sent to a server
+ // - `'confirmed'` the tx has been included in a block.
+ _setTxStatus (txId, status) {
+ var txMeta = this.getTx(txId)
+ txMeta.status = status
+ this.emit(`${txMeta.id}:${status}`, txId)
+ if (status === 'submitted' || status === 'rejected') {
+ this.emit(`${txMeta.id}:finished`, status)
+ }
+ this.updateTx(txMeta)
+ this.emit('updateBadge')
+ }
+
+ // Saves the new/updated txList.
+ // Function is intended only for internal use
+ _saveTxList (transactions) {
+ this.store.updateState({ transactions })
+ }
+
+ _updateMemstore () {
+ const unapprovedTxs = this.getUnapprovedTxList()
+ const selectedAddressTxList = this.getFilteredTxList({
+ from: this.getSelectedAddress(),
+ metamaskNetworkId: this.getNetwork(),
+ })
+ this.memStore.updateState({ unapprovedTxs, selectedAddressTxList })
+ }
+}
+
+
+const warn = () => console.warn('warn was used no cb provided')
diff --git a/circle.yml b/circle.yml
index ccb5fa120..c9ea787ff 100644
--- a/circle.yml
+++ b/circle.yml
@@ -4,3 +4,4 @@ machine:
dependencies:
pre:
- "npm i -g testem"
+ - "npm i -g mocha"
diff --git a/development/announcer.js b/development/announcer.js
new file mode 100644
index 000000000..110d41fd4
--- /dev/null
+++ b/development/announcer.js
@@ -0,0 +1,12 @@
+var manifest = require('../app/manifest.json')
+var version = manifest.version
+
+var fs = require('fs')
+var path = require('path')
+var changelog = fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md')).toString()
+
+var log = changelog.split(version)[1].split('##')[0].trim()
+
+let msg = `*MetaMask ${version}* now published to the Chrome Store! It should auto-update over the next hour!\n${log}`
+
+console.log(msg)
diff --git a/development/states.js b/development/states.js
deleted file mode 100644
index d172abaa3..000000000
--- a/development/states.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = {"account detail with shapeshift tx":{"metamask":{"currentFiat":"USD","conversionRate":11.06608791,"conversionDate":1470421024,"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","shapeShiftTxList":[{"depositAddress":"1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw","depositType":"BTC","key":"shapeshift","time":1471564825772,"response":{"status":"complete","outgoingCoin":"100.00","incomingCoin":"1.000","transaction":"0x3701e0ac344a12a1fc5417cf251109a7c41f3edab922310202630d9c012414c8"}},{"depositAddress":"1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw","depositType":"BTC","key":"shapeshift","time":1471566579224,"response":{"status":"no_deposits","depositAddress":"1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw"}},{"depositAddress":"1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw","depositType":"BTC","key":"shapeshift","time":1471566565378,"response":{"status":"received","depositAddress":"1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw"}}],"transactions":[{"id":1467921503489592,"txParams":{"from":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761","value":"0x66c899104aa57038000","origin":"thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com","metamaskId":1467921503489592,"metamaskNetworkId":"2"},"time":1467921503489,"status":"rejected","containsDelegateCall":false},{"id":1467923203344608,"txParams":{"from":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761","value":"0x0","origin":"thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com","metamaskId":1467923203344608,"metamaskNetworkId":"2"},"time":1467923203344,"status":"confirmed","containsDelegateCall":false,"hash":"0x957bbba51e2732a86c10c5e7e8a484a093795a06f2e2c38ad02da1b20aeca620"},{"id":1467921503489592,"txParams":{"from":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761","value":"0x66c899104aa57038000","origin":"thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com","metamaskId":1467921503489592,"metamaskNetworkId":"2"},"time":1467921503489,"status":"confirmed","containsDelegateCall":false}],"identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","network":"1","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"mainnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail","detailView":null,"context":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}},"account detail with transaction history":{"metamask":{"currentFiat":"USD","conversionRate":11.06608791,"conversionDate":1470421024,"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[{"id":1467921503489592,"txParams":{"from":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761","value":"0x66c899104aa57038000","origin":"thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com","metamaskId":1467921503489592,"metamaskNetworkId":"2"},"time":1467921503489,"status":"rejected","containsDelegateCall":false},{"id":1467923203344608,"txParams":{"from":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761","value":"0x0","origin":"thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com","metamaskId":1467923203344608,"metamaskNetworkId":"2"},"time":1467923203344,"status":"confirmed","containsDelegateCall":false,"hash":"0x957bbba51e2732a86c10c5e7e8a484a093795a06f2e2c38ad02da1b20aeca620"},{"id":1467921503489592,"txParams":{"from":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761","value":"0x66c899104aa57038000","origin":"thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com","metamaskId":1467921503489592,"metamaskNetworkId":"2"},"time":1467921503489,"status":"confirmed","containsDelegateCall":false}],"selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail","detailView":null,"context":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}},"account detail":{"metamask":{"currentFiat":"USD","conversionRate":11.06608791,"conversionDate":1470421024,"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail","detailView":null,"context":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}},"accounts":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"balance":"0x0","code":"0x","nonce":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"accounts"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}},"config":{"metamask":{"currentFiat":"USD","conversionRate":11.06608791,"conversionDate":1470421024,"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Wallet 1","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Wallet 2","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"name":"Wallet 4","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x843963b837841dad3b0f5969ff271108776616df","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"config"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"testfaucet.metamask.io","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}},"create vault password":{"metamask":{"isInitialized":false,"isUnlocked":false,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{},"accounts":{},"transactions":[],"network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"accounts","detailView":null},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":false,"isLoading":false,"warning":null},"identities":{}},"custom rpc":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false},"0x18c643c9cf21027339c8648fbaa2f348ddcbe00a":{"name":"Wallet 4","address":"0x18c643c9cf21027339c8648fbaa2f348ddcbe00a","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{},"0x18c643c9cf21027339c8648fbaa2f348ddcbe00a":{}},"transactions":[{"id":1470676748243181,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161097d38038061097d83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b50505050505061070c806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610618575b610000610002565b61064c60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156106ef5780601f106106c4576101008083540402835291602001916106ef565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561070757816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a35060016106fb565b6106ba60045460ff1681565b61064c60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156106ef5780601f106106c4576101008083540402835291602001916106ef565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61064c60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156106ef5780601f106106c4576101008083540402835291602001916106ef565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561070257604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c948235946024803595606494939101919081908382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105f05780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106f757610002565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106ac5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106d257829003601f168201915b505050505081565b5060015b9392505050565b610157565b6106fb5600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000084d536674436f696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044d53465400000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1470676748243181,"metamaskNetworkId":"2"},"time":1470676748243,"status":"confirmed","estimatedGas":"0xa9442","hash":"0xce6f9e068c780a1197117ec9e2103c33ac21bc8d5822aa2293c9c84e72229d88"},{"id":1470682363931487,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","nonce":"0x2","gasPrice":"0x4a817c800","to":"0x059f1e6f420d6fee280bb83e2702dcd06a2ce9c8","value":"0x3e2c284391c0000","data":"0xd92ed13700000000000000000000000000000000000000000000000000000000000000620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000224d6574614d61736b3a20457468657265756d20696e20796f75722042726f777365720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000052416e7920696e746572657374696e672064657461696c732061626f757420796f752c207468617420796f7520776f756c64206c696b6520746f20736861726520776974682074686520636f6d6d756e697479000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000137777772e796f7572776562736974652e636f6d00000000000000000000000000","gasLimit":"0x5ce40","origin":"www.etherfaces.com","metamaskId":1470682363931487,"metamaskNetworkId":"1"},"time":1470682363931,"status":"confirmed","estimatedGas":"0x447a0","containsDelegateCall":false,"hash":"0x4aa629bdc0710f9048c2dd8c35883338de681aff50998b0e14f1079e41e7b3bf"},{"id":1470682617417416,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","nonce":"0x3","gasPrice":"0x4a817c800","to":"0x059f1e6f420d6fee280bb83e2702dcd06a2ce9c8","value":"0x3e2c284391c0000","data":"0xd92ed137000000000000000000000000000000000000000000000000000000000000006200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e566973697420457468657265756d2044617070732077697468696e206e6f726d616c207765622062726f77736572732077697468204d6574614d61736b210000000000000000000000000000000000000000000000000000000000000000001368747470733a2f2f6d6574616d61736b2e696f00000000000000000000000000","gasLimit":"0x2fc7720","origin":"www.etherfaces.com","metamaskId":1470682617417416,"metamaskNetworkId":"1"},"time":1470682617417,"status":"confirmed","containsDelegateCall":false,"estimatedGas":"0x2faf080","hash":"0x88bd8c6276854039ea416a8c38e867546cc928670758aebb8db44b9120f7dad2"},{"id":1470682742887332,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","nonce":"0x3","gasPrice":"0x4a817c800","to":"0x059f1e6f420d6fee280bb83e2702dcd06a2ce9c8","value":"0x470de4df8200000","data":"0xd92ed137000000000000000000000000000000000000000000000000000000000000006200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029566973697420457468657265756d20446170707320696e2061206e6f726d616c2042726f777365722e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f7777772e6d6574616d61736b2e696f0000000000000000000000000000000000","gasLimit":"0x2fc7720","origin":"www.etherfaces.com","metamaskId":1470682742887332,"metamaskNetworkId":"1"},"time":1470682742887,"status":"confirmed","containsDelegateCall":false,"estimatedGas":"0x2faf080","hash":"0xc716c9ce00d48edf04ec64ab07ea7b413dd49ffd744359213fe8da69246aad10"},{"id":1470692493483229,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161097d38038061097d83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b50505050505061070c806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610618575b610000610002565b61064c60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156106ef5780601f106106c4576101008083540402835291602001916106ef565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561070757816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a35060016106fb565b6106ba60045460ff1681565b61064c60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156106ef5780601f106106c4576101008083540402835291602001916106ef565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61064c60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156106ef5780601f106106c4576101008083540402835291602001916106ef565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561070257604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c948235946024803595606494939101919081908382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105f05780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106f757610002565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106ac5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106d257829003601f168201915b505050505081565b5060015b9392505050565b610157565b6106fb5600000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000084d5346542062757800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044d53465400000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1470692493483229,"metamaskNetworkId":"1"},"time":1470692493483,"status":"confirmed","estimatedGas":"0xa9482","hash":"0xe539d7b39c2742261b9b3d1cdb22940275c6cc4d8b80a8a0222f6d2b0b549311"},{"id":1470962599348486,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x69eabf45623e5b53683924dff98309eb39af1a89","value":"0x2","origin":"localhost","metamaskId":1470962599348486,"metamaskNetworkId":"1"},"time":1470962599348,"status":"rejected","containsDelegateCall":false,"estimatedGas":"0x5208"},{"id":1470963263070463,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x69eabf45623e5b53683924dff98309eb39af1a89","value":"0x2","origin":"localhost","metamaskId":1470963263070463,"metamaskNetworkId":"1"},"time":1470963263070,"status":"rejected","containsDelegateCall":false,"estimatedGas":"0x5208"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"loading","seedWords":null,"isConfirmed":true,"isEthConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"rpc","rpcTarget":"555.203.16.244"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail","detailView":null,"context":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"accountDetail":{"subview":"transactions"},"currentDomain":"localhost:8080","transForward":true,"isLoading":false,"warning":null},"identities":{}},"empty account detail":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"code":"0x","balance":"0x01","nonce":"0x0","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"code":"0x","nonce":"0x0","balance":"0x01","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"code":"0x","nonce":"0x0","balance":"0x01","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail","detailView":null,"context":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}},"help":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"info"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}},"locked":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Wallet 1","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Wallet 2","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"name":"Wallet 4","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x843963b837841dad3b0f5969ff271108776616df"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"testfaucet.metamask.io","transForward":false,"isLoading":false,"warning":null,"scrollToBottom":false},"identities":{}},"new vault":{"metamask":{"isInitialized":false,"isUnlocked":false,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{},"accounts":{},"transactions":[],"network":"2","seedWords":null,"isConfirmed":false,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"accounts","detailView":null},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}},"pending crash":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{"1467755147235616":{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"1","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"mainnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"tokenfactory.surge.sh","transForward":true,"isLoading":false,"warning":null},"identities":{}},"pending signature":{"metamask":{"isInitialized":true,"isUnlocked":true,"isEthConfirmed":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{},"currentFiat":"USD","conversionRate":11.02269525,"conversionDate":1472076963,"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"balance":"0x056ace16d84b1c7e78","nonce":"0x17","code":"0x0","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"balance":"0x00000000000000056bc75e2d63100000","nonce":"0x0","code":"0x0","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"balance":"0x00000000000000056bc75e2d63100000","nonce":"0x0","code":"0x0","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[{"id":1471975421223082,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a","origin":"localhost","metamaskId":1471975421223082,"metamaskNetworkId":"1471904489432"},"time":1471975421223,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xd0fe393e2586ebded866c9f13b90494e902bc49047fbf25ba2ac96c805a2f5d3"},{"id":1471975427199819,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a","origin":"localhost","metamaskId":1471975427199819,"metamaskNetworkId":"1471904489432"},"time":1471975427199,"status":"rejected","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xb1f2f63f3e265f05d7c353ab38dd8b73fce8e7214489037311ee1f58a994dae3"},{"id":1471975806981442,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a","origin":"localhost","metamaskId":1471975806981442,"metamaskNetworkId":"1471904489432"},"time":1471975806981,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xeeb89e91aaeea14c4950016c45d60df8ee8874daa6f414de5cf267ea2c17bc6e"},{"id":1471975810133789,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a","origin":"localhost","metamaskId":1471975810133789,"metamaskNetworkId":"1471904489432"},"time":1471975810133,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xe54cab2e54b8495691b6d8315ca24a190cba546a9fcb056642479ce5770cec8b"},{"id":1471976546865348,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a","origin":"localhost","metamaskId":1471976546865348,"metamaskNetworkId":"1471904489432"},"time":1471976546865,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0x54e10f77e17f28f4d12751749a2ca22f9b528592d1140ef53c6430a68e731542"},{"id":1471976930101889,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005","origin":"localhost","metamaskId":1471976930101889,"metamaskNetworkId":"1471904489432"},"time":1471976930101,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0x60b5af26fad18c5549949064b67c8f965c9f20cd3e890c69512ca3acad10ed8b"},{"id":1471977268048169,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000002","origin":"localhost","metamaskId":1471977268048169,"metamaskNetworkId":"1471904489432"},"time":1471977268048,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0x1f96e29305ef11a9c993302c29e5419d87017e8222d4034daea0d86e155dc3aa"},{"id":1471977310778630,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005","origin":"localhost","metamaskId":1471977310778630,"metamaskNetworkId":"1471904489432"},"time":1471977310778,"status":"rejected","containsDelegateCall":true,"estimatedGas":"0x89ef"},{"id":1471977316241561,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005","origin":"localhost","metamaskId":1471977316241561,"metamaskNetworkId":"1471904489432"},"time":1471977316240,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xdbd610c92d77a07c76b82e14e32674d382c45c4780dd2a550888b5cc40d54bcc"},{"id":1471977344018510,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000004","origin":"localhost","metamaskId":1471977344018510,"metamaskNetworkId":"1471904489432"},"time":1471977344018,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0x709d871d9ded0108de9f7718a7490b19d45e5e7562b1ba6c5bf6cce56e767d48"},{"id":1471977403830380,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000001","origin":"localhost","metamaskId":1471977403830380,"metamaskNetworkId":"1471904489432"},"time":1471977403830,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0x94f5088a127bba181b303d6427ae93cbfa9867997bea1326f30da311e36c6aca"},{"id":1471977431563703,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000001","origin":"localhost","metamaskId":1471977431563703,"metamaskNetworkId":"1471904489432"},"time":1471977431563,"status":"rejected","containsDelegateCall":true,"estimatedGas":"0x89ef"},{"id":1471977436074587,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000001","origin":"localhost","metamaskId":1471977436074587,"metamaskNetworkId":"1471904489432"},"time":1471977436074,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0x5f514dfe3bd48f6f301c809a7a75f73f0fc93bc3a0e469368b84dce032aff9ec"},{"id":1471991826717707,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000004","origin":"localhost","metamaskId":1471991826717707,"metamaskNetworkId":"1471904489432"},"time":1471991826717,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xab1e706f8981680a6c921f9f57f8ce573392bbb4f0fe85cf45e5cbf858fa5f3e"},{"id":1471991851917592,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000004","origin":"localhost","metamaskId":1471991851917592,"metamaskNetworkId":"1471904489432"},"time":1471991851917,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xdea144f7a6f06969739f676d8702a9a11efc689e032f1981fe67afc9261dd4de"},{"id":1471992032999543,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005","origin":"localhost","metamaskId":1471992032999543,"metamaskNetworkId":"1471904489432"},"time":1471992032999,"status":"confirmed","containsDelegateCall":true,"estimatedGas":"0x89ef","hash":"0xdf31b8cc0fbd2ab6727e0b63536bd4eab51a147aa29e04691e68fae28b866fb3"},{"id":1471992043490878,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","to":"0x48ff0cbac0acefedf152281ee80e9a0a01d5da63","data":"0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005","origin":"localhost","metamaskId":1471992043490878,"metamaskNetworkId":"1471904489432"},"time":1471992043490,"status":"rejected","containsDelegateCall":true,"estimatedGas":"0x89ef"},{"id":1472068030402279,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x3782dace9d90000","gas":"0x493e0","to":"0x18a672e11d637fffadccc99b152f4895da069601","data":"0x5b7d47a900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","origin":"www.rouleth.com","metamaskId":1472068030402279,"metamaskNetworkId":"1"},"time":1472068030402,"status":"rejected","containsDelegateCall":false,"estimatedGas":"0x24704"},{"id":1472068061833258,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","gas":"0x493e0","to":"0x18a672e11d637fffadccc99b152f4895da069601","data":"0x5b7d47a900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","origin":"www.rouleth.com","metamaskId":1472068061833258,"metamaskNetworkId":"1"},"time":1472068061833,"status":"confirmed","containsDelegateCall":false,"estimatedGas":"0x24704","hash":"0xb6e6ff57e7b5f6bd7f2e6dc44c39f4e858a227c9509586634ca547179345a13e"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"1471904489432","seedWords":null,"isConfirmed":true,"unconfMsgs":{"1472076978535283":{"id":1472076978535283,"msgParams":{"origin":"localhost","from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"hello"},"time":1472076978535,"status":"unconfirmed"}},"messages":[{"id":1472076978535283,"msgParams":{"origin":"localhost","from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"hello"},"time":1472076978535,"status":"unconfirmed"}],"shapeShiftTxList":[],"provider":{"type":"rpc","rpcTarget":"http://localhost:8545"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","detailView":null,"context":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"accountDetail":{"subview":"transactions"},"currentDomain":"ebjbdknjcgcbchkagneicjfpneaghdhb","transForward":true,"isLoading":false,"warning":null},"identities":{}},"pending tx contract":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467755147235616":{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x1000000","nonce":"0x0","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","nonce":"0x0","balance":"0x1000000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","balance":"0x1000000","nonce":"0x0","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"1","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"mainnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}},"pending tx send coin":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467868023090690":{"id":1467868023090690,"txParams":{"data":"0xa9059cbb0000000000000000000000008deb4d106090c3eb8f1950f727e87c4f884fb06f0000000000000000000000000000000000000000000000000000000000000064","from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xbeb0ed3034c4155f3d16a64a5c5e7c8d4ea9e9c9","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x38326dc32cf80800","nonce":"0x10000c","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","balance":"0x15e578bd8e9c8000","nonce":"0x100000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","nonce":"0x100000","balance":"0x2386f26fc10000","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}},"pending tx value":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467868023090690":{"id":1467868023090690,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xC5b8dBAc4c1d3F152cDeb400E2313F309c410aCb","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x38326dc32cf80800","nonce":"0x10000c","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","balance":"0x15e578bd8e9c8000","nonce":"0x100000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","nonce":"0x100000","balance":"0x2386f26fc10000","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"rejected"},{"id":1467868023090690,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xC5b8dBAc4c1d3F152cDeb400E2313F309c410aCb","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}},"show seed words":{"metamask":{"isInitialized":false,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"}},"transactions":[],"network":"2","seedWords":"debris dizzy just program just float decrease vacant alarm reduce speak stadium","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"createVaultComplete","seedWords":"debris dizzy just program just float decrease vacant alarm reduce speak stadium"},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}},"terms":{"metamask":{"isInitialized":false,"isUnlocked":false,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{},"accounts":{},"transactions":[],"network":"2","seedWords":null,"isConfirmed":false,"isEthConfirmed":false,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"EthStoreWarning"},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}}} \ No newline at end of file
diff --git a/development/states.json b/development/states.json
index b2776edcc..bdd0a6b27 100644
--- a/development/states.json
+++ b/development/states.json
@@ -1 +1 @@
-module.exports = [{"account detail":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Secret Wallet!","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Main Wallet","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x843963b837841dad3b0f5969ff271108776616df"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail","detailView":null,"context":"0x843963b837841dad3b0f5969ff271108776616df"},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}}},{"accounts":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"balance":"0x0","code":"0x","nonce":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"accounts"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}}},{"config":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Wallet 1","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Wallet 2","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"name":"Wallet 4","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x843963b837841dad3b0f5969ff271108776616df","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"accounts"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"testfaucet.metamask.io","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}}},{"create vault password":{"metamask":{"isInitialized":false,"isUnlocked":false,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{},"accounts":{},"transactions":[],"network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"accounts","detailView":null},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":false,"isLoading":false,"warning":null},"identities":{}}},{"help":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"info"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}}},{"locked":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Wallet 1","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Wallet 2","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"name":"Wallet 4","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0x843963b837841dad3b0f5969ff271108776616df"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"testfaucet.metamask.io","transForward":false,"isLoading":false,"warning":null,"scrollToBottom":false},"identities":{}}},{"new vault":{"metamask":{"isInitialized":false,"isUnlocked":false,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{},"accounts":{},"transactions":[],"network":"2","seedWords":null,"isConfirmed":false,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"accounts","detailView":null},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}}},{"show seed words":{"metamask":{"isInitialized":false,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"}},"transactions":[],"network":"2","seedWords":"debris dizzy just program just float decrease vacant alarm reduce speak stadium","isConfirmed":false,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"createVaultComplete","seedWords":"debris dizzy just program just float decrease vacant alarm reduce speak stadium"},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}}},{"terms":{"metamask":{"accounts":{},"transactions":[],"identities":{},"network":"2","isInitialized":false,"isUnlocked":false,"seedWords":null,"isConfirmed":false,"unconfTxs":{},"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"currentDomain":"extensions"}}}] \ No newline at end of file
+module.exports = [{"account detail":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Secret Wallet!","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Main Wallet","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","seedWords":null,"isDisclaimerConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail","detailView":null,"context":"0x843963b837841dad3b0f5969ff271108776616df"},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}}},{"accounts":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"balance":"0x0","code":"0x","nonce":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"network":"2","isDisclaimerConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"accounts"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}}},{"config":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Wallet 1","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Wallet 2","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"name":"Wallet 4","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","isDisclaimerConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"accounts"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"testfaucet.metamask.io","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}}},{"create vault password":{"metamask":{"isInitialized":false,"isUnlocked":false,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{},"accounts":{},"transactions":[],"network":"2","seedWords":null,"isDisclaimerConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"accounts","detailView":null},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":false,"isLoading":false,"warning":null},"identities":{}}},{"help":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"name":"Wallet 4","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"code":"0x","balance":"0x0","nonce":"0x0","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"},"0x704107d04affddd9b66ab9de3dd7b095852e9b69":{"code":"0x","nonce":"0x0","balance":"0x0","address":"0x704107d04affddd9b66ab9de3dd7b095852e9b69"}},"transactions":[],"network":"2","isDisclaimerConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","selectedAddress":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","seedWords":null},"appState":{"menuOpen":false,"currentView":{"name":"info"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null,"scrollToBottom":true},"identities":{}}},{"locked":{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"name":"Wallet 1","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6","mayBeFauceting":false},"0x843963b837841dad3b0f5969ff271108776616df":{"name":"Wallet 2","address":"0x843963b837841dad3b0f5969ff271108776616df","mayBeFauceting":false},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"name":"Wallet 3","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a","mayBeFauceting":false},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"name":"Wallet 4","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"},"0x843963b837841dad3b0f5969ff271108776616df":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x843963b837841dad3b0f5969ff271108776616df"},"0x2cb215323857bec1c91e5db10fe87379a5cf129a":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x2cb215323857bec1c91e5db10fe87379a5cf129a"},"0xc5091450b7548b0dce3a76b8d325929c39e648d1":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xc5091450b7548b0dce3a76b8d325929c39e648d1"}},"transactions":[],"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df","network":"2","isDisclaimerConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAddress":"0x843963b837841dad3b0f5969ff271108776616df"},"appState":{"menuOpen":false,"currentView":{"name":"accountDetail"},"accountDetail":{"subview":"transactions","accountExport":"none","privateKey":""},"currentDomain":"testfaucet.metamask.io","transForward":false,"isLoading":false,"warning":null,"scrollToBottom":false},"identities":{}}},{"new vault":{"metamask":{"isInitialized":false,"isUnlocked":false,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{},"accounts":{},"transactions":[],"network":"2","seedWords":null,"isDisclaimerConfirmed":false,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"accounts","detailView":null},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}}},{"show seed words":{"metamask":{"isInitialized":false,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"name":"Wallet 1","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc","mayBeFauceting":false},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"name":"Wallet 2","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b","mayBeFauceting":false},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"name":"Wallet 3","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823","mayBeFauceting":false}},"unconfTxs":{},"accounts":{"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"},"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"},"0xeb9e64b93097bc15f01f13eae97015c57ab64823":{"balance":"0x0","nonce":"0x0","code":"0x","address":"0xeb9e64b93097bc15f01f13eae97015c57ab64823"}},"transactions":[],"network":"2","seedWords":"debris dizzy just program just float decrease vacant alarm reduce speak stadium","isDisclaimerConfirmed":false,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"menuOpen":false,"currentView":{"name":"createVaultComplete","seedWords":"debris dizzy just program just float decrease vacant alarm reduce speak stadium"},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}}},{"terms":{"metamask":{"accounts":{},"transactions":[],"identities":{},"network":"2","isInitialized":false,"isUnlocked":false,"seedWords":null,"isDisclaimerConfirmed":false,"unconfTxs":{},"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"}},"appState":{"currentDomain":"extensions"}}}] \ No newline at end of file
diff --git a/development/states/account-detail-with-shapeshift-tx.json b/development/states/account-detail-with-shapeshift-tx.json
index 027062979..c14062350 100644
--- a/development/states/account-detail-with-shapeshift-tx.json
+++ b/development/states/account-detail-with-shapeshift-tx.json
@@ -136,13 +136,12 @@
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"network": "1",
"seedWords": null,
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
"type": "mainnet"
},
- "selectedAccount": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/account-detail-with-transaction-history.json b/development/states/account-detail-with-transaction-history.json
index 69af85623..8cb495656 100644
--- a/development/states/account-detail-with-transaction-history.json
+++ b/development/states/account-detail-with-transaction-history.json
@@ -102,13 +102,12 @@
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"network": "2",
"seedWords": null,
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
"type": "testnet"
},
- "selectedAccount": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/account-detail.json b/development/states/account-detail.json
index a8183e3fb..644e3674d 100644
--- a/development/states/account-detail.json
+++ b/development/states/account-detail.json
@@ -33,20 +33,20 @@
"accounts": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
"code": "0x",
- "balance": "0x0",
+ "balance": "0x100000000000",
"nonce": "0x0",
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
"code": "0x",
"nonce": "0x0",
- "balance": "0x0",
+ "balance": "0x100000000000",
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
},
"0xeb9e64b93097bc15f01f13eae97015c57ab64823": {
"code": "0x",
"nonce": "0x0",
- "balance": "0x0",
+ "balance": "0x100000000000",
"address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823"
},
"0x704107d04affddd9b66ab9de3dd7b095852e9b69": {
@@ -60,13 +60,12 @@
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"network": "2",
"seedWords": null,
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
"type": "testnet"
},
- "selectedAccount": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/account-list-with-imported.json b/development/states/account-list-with-imported.json
new file mode 100644
index 000000000..b450b4fb8
--- /dev/null
+++ b/development/states/account-list-with-imported.json
@@ -0,0 +1,83 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683": {
+ "address": "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683",
+ "name": "Account 1"
+ },
+ "0x9858e7d8b79fc3e6d989636721584498926da38a": {
+ "address": "0x9858e7d8b79fc3e6d989636721584498926da38a",
+ "name": "Imported Account"
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 10.19458075,
+ "conversionDate": 1484696373,
+ "noActiveNotices": true,
+ "network": "3",
+ "accounts": {
+ "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683"
+ },
+ "0x9858e7d8b79fc3e6d989636721584498926da38a": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x9858e7d8b79fc3e6d989636721584498926da38a"
+ }
+ },
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0x9858e7d8b79fc3e6d989636721584498926da38a",
+ "selectedAccountTxList": [],
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "58bda1f9d87dc7d2bcc6f7c2513efc9d03fca683"
+ ]
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": [
+ "0x9858e7d8b79fc3e6d989636721584498926da38a"
+ ]
+ }
+ ],
+ "lostAccounts": [],
+ "seedWords": null
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accounts"
+ },
+ "accountDetail": {
+ "subview": "transactions",
+ "accountExport": "none",
+ "privateKey": ""
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null,
+ "scrollToBottom": false,
+ "forgottenPassword": false
+ },
+ "identities": {}
+}
diff --git a/development/states/accounts-loose.json b/development/states/accounts-loose.json
new file mode 100644
index 000000000..542e207d4
--- /dev/null
+++ b/development/states/accounts-loose.json
@@ -0,0 +1,125 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
+ "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "name": "Account 1"
+ },
+ "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
+ "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4",
+ "name": "Account 2"
+ },
+ "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
+ "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241",
+ "name": "Account 3"
+ },
+ "0xe15d894becb0354c501ae69429b05143679f39e0": {
+ "address": "0xe15d894becb0354c501ae69429b05143679f39e0",
+ "name": "Account 4"
+ },
+ "0x87658c15aefe7448008a28513a11b6b130ef4cd0": {
+ "address": "0x87658c15aefe7448008a28513a11b6b130ef4cd0",
+ "name": "Account 5"
+ },
+ "0xaa25854c0379e53c957ac9382e720c577fa31fd5": {
+ "address": "0xaa25854c0379e53c957ac9382e720c577fa31fd5",
+ "name": "Account 6"
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 0,
+ "conversionDate": "N/A",
+ "noActiveNotices": true,
+ "network": "3",
+ "accounts": {
+ "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
+ "code": "0x",
+ "balance": "0x11f646fe14c9c000",
+ "nonce": "0x3",
+ "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9"
+ },
+ "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4"
+ },
+ "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241"
+ },
+ "0xe15d894becb0354c501ae69429b05143679f39e0": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0xe15d894becb0354c501ae69429b05143679f39e0"
+ },
+ "0x87658c15aefe7448008a28513a11b6b130ef4cd0": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x87658c15aefe7448008a28513a11b6b130ef4cd0"
+ },
+ "0xaa25854c0379e53c957ac9382e720c577fa31fd5": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0xaa25854c0379e53c957ac9382e720c577fa31fd5"
+ }
+ },
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0x87658c15aefe7448008a28513a11b6b130ef4cd0",
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "ac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "d7c0cd9e7d2701c710d64fc492c7086679bdf7b4",
+ "1acfb961c5a8268eac8e09d6241a26cbeff42241"
+ ]
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": [
+ "e15d894becb0354c501ae69429b05143679f39e0",
+ "87658c15aefe7448008a28513a11b6b130ef4cd0",
+ "aa25854c0379e53c957ac9382e720c577fa31fd5"
+ ]
+ }
+ ],
+ "lostAccounts": []
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accounts"
+ },
+ "accountDetail": {
+ "subview": "transactions",
+ "accountExport": "none",
+ "privateKey": ""
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null,
+ "scrollToBottom": false,
+ "forgottenPassword": false
+ },
+ "identities": {}
+}
diff --git a/development/states/accounts.json b/development/states/accounts.json
index df3eb8132..4eef1145f 100644
--- a/development/states/accounts.json
+++ b/development/states/accounts.json
@@ -2,67 +2,101 @@
"metamask": {
"isInitialized": true,
"isUnlocked": true,
- "currentDomain": "example.com",
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
- "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
+ "0x0abdd95cafcabec9b3e99dcd09fc4b441037cb80": {
"name": "Wallet 1",
- "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "address": "0x0abdd95cafcabec9b3e99dcd09fc4b441037cb80",
"mayBeFauceting": false
},
- "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
+ "0xc4692812f03d96edf4ac88c9d08106222578407b": {
"name": "Wallet 2",
- "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b",
+ "address": "0xc4692812f03d96edf4ac88c9d08106222578407b",
"mayBeFauceting": false
},
- "0xeb9e64b93097bc15f01f13eae97015c57ab64823": {
+ "0x1abda824194d3583509c97e6faefd575e194844d": {
"name": "Wallet 3",
- "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823",
+ "address": "0x1abda824194d3583509c97e6faefd575e194844d",
"mayBeFauceting": false
},
- "0x704107d04affddd9b66ab9de3dd7b095852e9b69": {
+ "0xa7ea108f933abe81ddafc0b2279b1d828dd0e8d6": {
"name": "Wallet 4",
- "address": "0x704107d04affddd9b66ab9de3dd7b095852e9b69",
+ "address": "0xa7ea108f933abe81ddafc0b2279b1d828dd0e8d6",
+ "mayBeFauceting": false
+ },
+ "0xab64bed4528feed6a85ab3f3da91479954da9e46": {
+ "name": "Wallet 5",
+ "address": "0xab64bed4528feed6a85ab3f3da91479954da9e46",
+ "mayBeFauceting": false
+ },
+ "0x69e90083dabd9acc9ea6d706eb6dccf245ae4d6b": {
+ "name": "Wallet 6",
+ "address": "0x69e90083dabd9acc9ea6d706eb6dccf245ae4d6b",
+ "mayBeFauceting": false
+ },
+ "0xebcc86684ef70e0ec3ad85f996b22e1bdad2d024": {
+ "name": "Wallet 7",
+ "address": "0xebcc86684ef70e0ec3ad85f996b22e1bdad2d024",
"mayBeFauceting": false
}
},
"unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 11.84461814,
+ "conversionDate": 1476226414,
"accounts": {
- "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
- "balance": "0x0",
- "nonce": "0x0",
+ "0x0abdd95cafcabec9b3e99dcd09fc4b441037cb80": {
+ "balance": "0x0de0b6b3a7640000",
+ "nonce": "0x100000",
+ "code": "0x",
+ "address": "0x0abdd95cafcabec9b3e99dcd09fc4b441037cb80"
+ },
+ "0xc4692812f03d96edf4ac88c9d08106222578407b": {
+ "balance": "0x00",
+ "nonce": "0x100000",
+ "code": "0x",
+ "address": "0xc4692812f03d96edf4ac88c9d08106222578407b"
+ },
+ "0x1abda824194d3583509c97e6faefd575e194844d": {
+ "balance": "0x00",
+ "nonce": "0x100000",
+ "code": "0x",
+ "address": "0x1abda824194d3583509c97e6faefd575e194844d"
+ },
+ "0xa7ea108f933abe81ddafc0b2279b1d828dd0e8d6": {
+ "balance": "0x00",
+ "nonce": "0x100000",
"code": "0x",
- "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ "address": "0xa7ea108f933abe81ddafc0b2279b1d828dd0e8d6"
},
- "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
- "balance": "0x0",
- "nonce": "0x0",
+ "0xab64bed4528feed6a85ab3f3da91479954da9e46": {
+ "balance": "0x00",
+ "nonce": "0x100000",
"code": "0x",
- "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
+ "address": "0xab64bed4528feed6a85ab3f3da91479954da9e46"
},
- "0xeb9e64b93097bc15f01f13eae97015c57ab64823": {
- "balance": "0x0",
- "nonce": "0x0",
+ "0x69e90083dabd9acc9ea6d706eb6dccf245ae4d6b": {
+ "balance": "0x00",
+ "nonce": "0x100000",
"code": "0x",
- "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823"
+ "address": "0x69e90083dabd9acc9ea6d706eb6dccf245ae4d6b"
},
- "0x704107d04affddd9b66ab9de3dd7b095852e9b69": {
- "balance": "0x0",
+ "0xebcc86684ef70e0ec3ad85f996b22e1bdad2d024": {
+ "balance": "0x00",
+ "nonce": "0x100000",
"code": "0x",
- "nonce": "0x0",
- "address": "0x704107d04affddd9b66ab9de3dd7b095852e9b69"
+ "address": "0xebcc86684ef70e0ec3ad85f996b22e1bdad2d024"
}
},
"transactions": [],
"network": "2",
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
+ "shapeShiftTxList": [],
"provider": {
"type": "testnet"
},
- "selectedAccount": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
- "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "selectedAddress": "0x0abdd95cafcabec9b3e99dcd09fc4b441037cb80",
"seedWords": null
},
"appState": {
@@ -71,15 +105,12 @@
"name": "accounts"
},
"accountDetail": {
- "subview": "transactions",
- "accountExport": "none",
- "privateKey": ""
+ "subview": "transactions"
},
- "currentDomain": "extensions",
"transForward": true,
"isLoading": false,
"warning": null,
"scrollToBottom": true
},
"identities": {}
-} \ No newline at end of file
+}
diff --git a/development/states/compilation-bug.json b/development/states/compilation-bug.json
new file mode 100644
index 000000000..b33e59c5c
--- /dev/null
+++ b/development/states/compilation-bug.json
@@ -0,0 +1,123 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
+ "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "name": "Account 1"
+ },
+ "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
+ "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4",
+ "name": "Account 2"
+ },
+ "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
+ "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241",
+ "name": "Account 3"
+ },
+ "0xabc2bca51709b8615147352c62420f547a63a00c": {
+ "address": "0xabc2bca51709b8615147352c62420f547a63a00c",
+ "name": "Account 4"
+ }
+ },
+ "unconfTxs": {
+ "7992944905869041": {
+ "id": 7992944905869041,
+ "txParams": {
+ "from": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "value": "0x0",
+ "data": "0x606060405234610000575b60da806100186000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14603c575b6000565b3460005760466088565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820a99dfa6091771f518dd1ae8d1ee347bae3304dffd98fd24b1b99a8380bc60a750029",
+ "gas": "0x1af75",
+ "metamaskId": 7992944905869041,
+ "metamaskNetworkId": "3"
+ },
+ "time": 1482279685589,
+ "status": "unconfirmed",
+ "gasMultiplier": 1,
+ "metamaskNetworkId": "3",
+ "gasLimitSpecified": true,
+ "estimatedGas": "0x1af75",
+ "simulationFails": true
+ }
+ },
+ "currentFiat": "USD",
+ "conversionRate": 7.69158136,
+ "conversionDate": 1482279663,
+ "noActiveNotices": true,
+ "network": "3",
+ "accounts": {
+ "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9": {
+ "code": "0x",
+ "nonce": "0x3",
+ "balance": "0x11f646fe14c9c000",
+ "address": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9"
+ },
+ "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4": {
+ "code": "0x",
+ "nonce": "0x0",
+ "balance": "0x0",
+ "address": "0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4"
+ },
+ "0x1acfb961c5a8268eac8e09d6241a26cbeff42241": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x1acfb961c5a8268eac8e09d6241a26cbeff42241"
+ },
+ "0xabc2bca51709b8615147352c62420f547a63a00c": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0xabc2bca51709b8615147352c62420f547a63a00c"
+ }
+ },
+ "transactions": [
+ {
+ "id": 7992944905869041,
+ "txParams": {
+ "from": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "value": "0x0",
+ "data": "0x606060405234610000575b60da806100186000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14603c575b6000565b3460005760466088565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820a99dfa6091771f518dd1ae8d1ee347bae3304dffd98fd24b1b99a8380bc60a750029",
+ "gas": "0x1af75",
+ "metamaskId": 7992944905869041,
+ "metamaskNetworkId": "3"
+ },
+ "time": 1482279685589,
+ "status": "unconfirmed",
+ "gasMultiplier": 1,
+ "metamaskNetworkId": "3",
+ "gasLimitSpecified": true,
+ "estimatedGas": "0x1af75",
+ "simulationFails": true
+ }
+ ],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9",
+ "seedWords": false,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "lostAccounts": []
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "confTx",
+ "context": 0
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/states/config.json b/development/states/config.json
index 22d999158..de1df1831 100644
--- a/development/states/config.json
+++ b/development/states/config.json
@@ -57,15 +57,13 @@
}
},
"transactions": [],
- "selectedAddress": "0x843963b837841dad3b0f5969ff271108776616df",
"network": "2",
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
"type": "testnet"
},
- "selectedAccount": "0x843963b837841dad3b0f5969ff271108776616df",
+ "selectedAddress": "0x843963b837841dad3b0f5969ff271108776616df",
"seedWords": null
},
"appState": {
diff --git a/development/states/create-vault-password.json b/development/states/create-vault-password.json
index 2253ba639..1ff51b427 100644
--- a/development/states/create-vault-password.json
+++ b/development/states/create-vault-password.json
@@ -10,7 +10,6 @@
"transactions": [],
"network": "2",
"seedWords": null,
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
@@ -32,4 +31,4 @@
"warning": null
},
"identities": {}
-} \ No newline at end of file
+}
diff --git a/development/states/custom-rpc.json b/development/states/custom-rpc.json
index ea0f797e6..7917352e8 100644
--- a/development/states/custom-rpc.json
+++ b/development/states/custom-rpc.json
@@ -157,18 +157,15 @@
"estimatedGas": "0x5208"
}
],
- "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
- "network": "loading",
+ "network": "166",
"seedWords": null,
- "isConfirmed": true,
- "isEthConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
"type": "rpc",
"rpcTarget": "555.203.16.244"
},
- "selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/empty-account-detail.json b/development/states/empty-account-detail.json
index f95a4b251..60b04f03d 100644
--- a/development/states/empty-account-detail.json
+++ b/development/states/empty-account-detail.json
@@ -57,13 +57,12 @@
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"network": "2",
"seedWords": null,
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
"type": "testnet"
},
- "selectedAccount": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
},
"appState": {
"menuOpen": false,
diff --git a/development/states/first-time.json b/development/states/first-time.json
new file mode 100644
index 000000000..aeeea7be0
--- /dev/null
+++ b/development/states/first-time.json
@@ -0,0 +1,39 @@
+{
+ "metamask": {
+ "isInitialized": false,
+ "isUnlocked": false,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {},
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 11.47635827,
+ "conversionDate": 1477606503,
+ "noActiveNotices": true,
+ "network": null,
+ "accounts": {},
+ "transactions": [],
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair"
+ ],
+ "provider": {
+ "type": "testnet"
+ }
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accounts",
+ "detailView": null
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/states/help.json b/development/states/help.json
index 9c2c4f0d3..cb8b11970 100644
--- a/development/states/help.json
+++ b/development/states/help.json
@@ -55,13 +55,11 @@
},
"transactions": [],
"network": "2",
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
"type": "testnet"
},
- "selectedAccount": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"seedWords": null
},
@@ -82,4 +80,4 @@
"scrollToBottom": true
},
"identities": {}
-} \ No newline at end of file
+}
diff --git a/development/states/import-private-key-warning.json b/development/states/import-private-key-warning.json
new file mode 100644
index 000000000..a2d33ed7f
--- /dev/null
+++ b/development/states/import-private-key-warning.json
@@ -0,0 +1,91 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0x01208723ba84e15da2e71656544a2963b0c06d40": {
+ "address": "0x01208723ba84e15da2e71656544a2963b0c06d40",
+ "name": "Account 1"
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 10.1219126,
+ "conversionDate": 1484695442,
+ "noActiveNotices": true,
+ "network": "3",
+ "accounts": {
+ "0x01208723ba84e15da2e71656544a2963b0c06d40": {
+ "nonce": "0x0",
+ "balance": "0x0",
+ "code": "0x",
+ "address": "0x01208723ba84e15da2e71656544a2963b0c06d40"
+ }
+ },
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0x01208723ba84e15da2e71656544a2963b0c06d40",
+ "selectedAccountTxList": [],
+ "seedWords": false,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "Simple Key Pair",
+ "accounts": []
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": []
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": []
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": []
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": []
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": []
+ },
+ {
+ "type": "Simple Key Pair",
+ "accounts": []
+ },
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "01208723ba84e15da2e71656544a2963b0c06d40"
+ ]
+ }
+ ],
+ "lostAccounts": []
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "import-menu"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": "Invalid hex string"
+ },
+ "identities": {}
+}
diff --git a/development/states/import-private-key.json b/development/states/import-private-key.json
new file mode 100644
index 000000000..73fbd68db
--- /dev/null
+++ b/development/states/import-private-key.json
@@ -0,0 +1,63 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0x01208723ba84e15da2e71656544a2963b0c06d40": {
+ "address": "0x01208723ba84e15da2e71656544a2963b0c06d40",
+ "name": "Account 1"
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 10.10788584,
+ "conversionDate": 1484694362,
+ "noActiveNotices": true,
+ "network": "3",
+ "accounts": {
+ "0x01208723ba84e15da2e71656544a2963b0c06d40": {
+ "balance": "0x0",
+ "code": "0x",
+ "nonce": "0x0",
+ "address": "0x01208723ba84e15da2e71656544a2963b0c06d40"
+ }
+ },
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0x01208723ba84e15da2e71656544a2963b0c06d40",
+ "selectedAccountTxList": [],
+ "seedWords": null,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ],
+ "keyrings": [
+ {
+ "type": "HD Key Tree",
+ "accounts": [
+ "01208723ba84e15da2e71656544a2963b0c06d40"
+ ]
+ }
+ ],
+ "lostAccounts": []
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "import-menu"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/states/locked.json b/development/states/locked.json
index 944888798..8e6ed7860 100644
--- a/development/states/locked.json
+++ b/development/states/locked.json
@@ -1,84 +1,42 @@
{
"metamask": {
"isInitialized": true,
- "isUnlocked": true,
+ "isUnlocked": false,
"currentDomain": "example.com",
"rpcTarget": "https://rawtestrpc.metamask.io/",
- "identities": {
- "0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6": {
- "name": "Wallet 1",
- "address": "0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6",
- "mayBeFauceting": false
- },
- "0x843963b837841dad3b0f5969ff271108776616df": {
- "name": "Wallet 2",
- "address": "0x843963b837841dad3b0f5969ff271108776616df",
- "mayBeFauceting": false
- },
- "0x2cb215323857bec1c91e5db10fe87379a5cf129a": {
- "name": "Wallet 3",
- "address": "0x2cb215323857bec1c91e5db10fe87379a5cf129a",
- "mayBeFauceting": false
- },
- "0xc5091450b7548b0dce3a76b8d325929c39e648d1": {
- "name": "Wallet 4",
- "address": "0xc5091450b7548b0dce3a76b8d325929c39e648d1",
- "mayBeFauceting": false
- }
- },
+ "identities": {},
"unconfTxs": {},
- "accounts": {
- "0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6": {
- "balance": "0x0",
- "nonce": "0x0",
- "code": "0x",
- "address": "0x5f11b68b7d41633e74c6b18d8b8d147da52aedd6"
- },
- "0x843963b837841dad3b0f5969ff271108776616df": {
- "balance": "0x0",
- "nonce": "0x0",
- "code": "0x",
- "address": "0x843963b837841dad3b0f5969ff271108776616df"
- },
- "0x2cb215323857bec1c91e5db10fe87379a5cf129a": {
- "balance": "0x0",
- "nonce": "0x0",
- "code": "0x",
- "address": "0x2cb215323857bec1c91e5db10fe87379a5cf129a"
- },
- "0xc5091450b7548b0dce3a76b8d325929c39e648d1": {
- "balance": "0x0",
- "nonce": "0x0",
- "code": "0x",
- "address": "0xc5091450b7548b0dce3a76b8d325929c39e648d1"
- }
- },
+ "currentFiat": "USD",
+ "conversionRate": 11.4379398,
+ "conversionDate": 1473358355,
+ "accounts": {},
"transactions": [],
- "selectedAddress": "0x843963b837841dad3b0f5969ff271108776616df",
- "network": "2",
- "isConfirmed": true,
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "network": "1473186153102",
+ "seedWords": null,
"unconfMsgs": {},
"messages": [],
+ "shapeShiftTxList": [],
"provider": {
- "type": "testnet"
+ "type": "rpc",
+ "rpcTarget": "http://localhost:8545"
},
- "selectedAccount": "0x843963b837841dad3b0f5969ff271108776616df"
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"appState": {
"menuOpen": false,
"currentView": {
- "name": "accountDetail"
+ "name": "accountDetail",
+ "detailView": null,
+ "context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"accountDetail": {
- "subview": "transactions",
- "accountExport": "none",
- "privateKey": ""
+ "subview": "transactions"
},
- "currentDomain": "testfaucet.metamask.io",
- "transForward": false,
+ "currentDomain": "127.0.0.1:9966",
+ "transForward": true,
"isLoading": false,
- "warning": null,
- "scrollToBottom": false
+ "warning": null
},
"identities": {}
-} \ No newline at end of file
+}
diff --git a/development/states/lost-accounts.json b/development/states/lost-accounts.json
new file mode 100644
index 000000000..dcba7a764
--- /dev/null
+++ b/development/states/lost-accounts.json
@@ -0,0 +1,90 @@
+{
+ "metamask": {
+ "currentFiat": "USD",
+ "lostAccounts": [
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
+ ],
+ "conversionRate": 11.06608791,
+ "conversionDate": 1470421024,
+ "isInitialized": true,
+ "isUnlocked": true,
+ "currentDomain": "example.com",
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
+ "name": "Wallet 1",
+ "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "mayBeFauceting": false
+ },
+ "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
+ "name": "Wallet 2",
+ "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b",
+ "mayBeFauceting": false
+ },
+ "0xeb9e64b93097bc15f01f13eae97015c57ab64823": {
+ "name": "Wallet 3",
+ "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823",
+ "mayBeFauceting": false
+ },
+ "0x704107d04affddd9b66ab9de3dd7b095852e9b69": {
+ "name": "Wallet 4",
+ "address": "0x704107d04affddd9b66ab9de3dd7b095852e9b69",
+ "mayBeFauceting": false
+ }
+ },
+ "unconfTxs": {},
+ "accounts": {
+ "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
+ "code": "0x",
+ "balance": "0x100000000000",
+ "nonce": "0x0",
+ "address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ },
+ "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
+ "code": "0x",
+ "nonce": "0x0",
+ "balance": "0x100000000000",
+ "address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
+ },
+ "0xeb9e64b93097bc15f01f13eae97015c57ab64823": {
+ "code": "0x",
+ "nonce": "0x0",
+ "balance": "0x100000000000",
+ "address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823"
+ },
+ "0x704107d04affddd9b66ab9de3dd7b095852e9b69": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x704107d04affddd9b66ab9de3dd7b095852e9b69"
+ }
+ },
+ "transactions": [],
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
+ "network": "2",
+ "seedWords": null,
+ "unconfMsgs": {},
+ "messages": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accountDetail",
+ "detailView": null,
+ "context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "currentDomain": "127.0.0.1:9966",
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/states/new-account.json b/development/states/new-account.json
new file mode 100644
index 000000000..a056e14e7
--- /dev/null
+++ b/development/states/new-account.json
@@ -0,0 +1,65 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0xa6ef573d60594731178b7f85d80da13cc2af52dd": {
+ "address": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
+ "name": "Dan! 1"
+ },
+ "0xf9f52e84ad2c9122caa87478d27041ddaa215666": {
+ "address": "0xf9f52e84ad2c9122caa87478d27041ddaa215666",
+ "name": "Account 2"
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 10.92067835,
+ "conversionDate": 1478282884,
+ "network": null,
+ "accounts": {
+ "0xa6ef573d60594731178b7f85d80da13cc2af52dd": {
+ "balance": "0x00",
+ "nonce": "0x100000",
+ "code": "0x",
+ "address": "0xa6ef573d60594731178b7f85d80da13cc2af52dd"
+ },
+ "0xf9f52e84ad2c9122caa87478d27041ddaa215666": {
+ "balance": "0x00",
+ "nonce": "0x100000",
+ "code": "0x",
+ "address": "0xf9f52e84ad2c9122caa87478d27041ddaa215666"
+ }
+ },
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
+ "unconfMsgs": {},
+ "messages": [],
+ "selectedAddress": "0xa6ef573d60594731178b7f85d80da13cc2af52dd",
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ]
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "new-account"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null,
+ "forgottenPassword": null,
+ "detailView": {},
+ "scrollToBottom": false
+ },
+ "identities": {}
+}
diff --git a/development/states/new-vault.json b/development/states/new-vault.json
index 18b4e7427..8db8222fa 100644
--- a/development/states/new-vault.json
+++ b/development/states/new-vault.json
@@ -10,7 +10,6 @@
"transactions": [],
"network": "2",
"seedWords": null,
- "isConfirmed": false,
"unconfMsgs": {},
"messages": [],
"provider": {
@@ -32,4 +31,4 @@
"warning": null
},
"identities": {}
-} \ No newline at end of file
+}
diff --git a/development/states/notice.json b/development/states/notice.json
new file mode 100644
index 000000000..1cdcefcfb
--- /dev/null
+++ b/development/states/notice.json
@@ -0,0 +1,61 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "identities": {
+ "0x24a1d059462456aa332d6da9117aa7f91a46f2ac": {
+ "address": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac",
+ "name": "Account 1"
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 8.3533002,
+ "conversionDate": 1481671082,
+ "noActiveNotices": false,
+ "lastUnreadNotice": {
+ "read": false,
+ "date": "Tue Dec 13 2016",
+ "title": "MultiVault Support",
+ "body": "# Multi\n# Line\n## Support\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi tincidunt dapibus justo a auctor. Sed luctus metus non mi laoreet, sit amet placerat nibh ultricies. Cras fringilla, urna sit amet sodales porttitor, lacus risus lacinia lorem, non euismod magna felis id ex. Nam iaculis, ante nec imperdiet suscipit, nisi quam fringilla nisl, sed fringilla turpis lectus et nibh. Pellentesque sed neque pretium nulla elementum lacinia eu eget felis. Nulla facilisi. Pellentesque id mi tempor, tempus sapien id, ultricies nibh. Integer faucibus elit non orci dapibus porttitor. Pellentesque rutrum hendrerit sapien ut lacinia. Nunc elementum eget arcu eu volutpat. Integer ullamcorper aliquam metus, eu malesuada tellus vestibulum a.\n",
+ "id": 0
+ },
+ "network": "3",
+ "accounts": {
+ "0x24a1d059462456aa332d6da9117aa7f91a46f2ac": {
+ "code": "0x",
+ "nonce": "0x0",
+ "balance": "0x0",
+ "address": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac"
+ }
+ },
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac",
+ "seedWords": null,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ]
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accountDetail",
+ "detailView": null,
+ "context": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/states/pending-crash.json b/development/states/pending-crash.json
index 086238d62..cbb395359 100644
--- a/development/states/pending-crash.json
+++ b/development/states/pending-crash.json
@@ -1 +1 @@
-{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{"1467755147235616":{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"1","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"mainnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"tokenfactory.surge.sh","transForward":true,"isLoading":false,"warning":null},"identities":{}} \ No newline at end of file
+{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{},"unconfTxs":{"1467755147235616":{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"1","seedWords":null,"unconfMsgs":{},"messages":[],"provider":{"type":"mainnet"},"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"tokenfactory.surge.sh","transForward":true,"isLoading":false,"warning":null},"identities":{}}
diff --git a/development/states/pending-signature.json b/development/states/pending-signature.json
index 33f79b4c8..0d49e0229 100644
--- a/development/states/pending-signature.json
+++ b/development/states/pending-signature.json
@@ -2,7 +2,6 @@
"metamask": {
"isInitialized": true,
"isUnlocked": true,
- "isEthConfirmed": true,
"currentDomain": "example.com",
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {
@@ -355,7 +354,6 @@
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
"network": "1471904489432",
"seedWords": null,
- "isConfirmed": true,
"unconfMsgs": {
"1472076978535283": {
"id": 1472076978535283,
@@ -385,7 +383,7 @@
"type": "rpc",
"rpcTarget": "http://localhost:8545"
},
- "selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
},
"appState": {
"menuOpen": false,
@@ -403,4 +401,4 @@
"warning": null
},
"identities": {}
-} \ No newline at end of file
+}
diff --git a/development/states/pending-tx-contract.json b/development/states/pending-tx-contract.json
index 5599f3753..98fbf5890 100644
--- a/development/states/pending-tx-contract.json
+++ b/development/states/pending-tx-contract.json
@@ -1 +1 @@
-{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467755147235616":{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x1000000","nonce":"0x0","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","nonce":"0x0","balance":"0x1000000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","balance":"0x1000000","nonce":"0x0","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"1","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"mainnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}}
+{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467755147235616":{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x1000000","nonce":"0x0","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","nonce":"0x0","balance":"0x1000000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","balance":"0x1000000","nonce":"0x0","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"unconfirmed"}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"1","seedWords":null,"unconfMsgs":{},"messages":[],"provider":{"type":"mainnet"},"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"127.0.0.1:9966","transForward":true,"isLoading":false,"warning":null},"identities":{}}
diff --git a/development/states/pending-tx-send-coin.json b/development/states/pending-tx-send-coin.json
index 262d791bb..690f92b44 100644
--- a/development/states/pending-tx-send-coin.json
+++ b/development/states/pending-tx-send-coin.json
@@ -1 +1 @@
-{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467868023090690":{"id":1467868023090690,"txParams":{"data":"0xa9059cbb0000000000000000000000008deb4d106090c3eb8f1950f727e87c4f884fb06f0000000000000000000000000000000000000000000000000000000000000064","from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xbeb0ed3034c4155f3d16a64a5c5e7c8d4ea9e9c9","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x38326dc32cf80800","nonce":"0x10000c","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","balance":"0x15e578bd8e9c8000","nonce":"0x100000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","nonce":"0x100000","balance":"0x2386f26fc10000","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}}
+{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467868023090690":{"id":1467868023090690,"txParams":{"data":"0xa9059cbb0000000000000000000000008deb4d106090c3eb8f1950f727e87c4f884fb06f0000000000000000000000000000000000000000000000000000000000000064","from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xbeb0ed3034c4155f3d16a64a5c5e7c8d4ea9e9c9","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x38326dc32cf80800","nonce":"0x10000c","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","balance":"0x15e578bd8e9c8000","nonce":"0x100000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","nonce":"0x100000","balance":"0x2386f26fc10000","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"2","seedWords":null,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}}
diff --git a/development/states/pending-tx-value.json b/development/states/pending-tx-value.json
index 61fe7fa34..494fd313d 100644
--- a/development/states/pending-tx-value.json
+++ b/development/states/pending-tx-value.json
@@ -1 +1 @@
-{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467868023090690":{"id":1467868023090690,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xC5b8dBAc4c1d3F152cDeb400E2313F309c410aCb","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x38326dc32cf80800","nonce":"0x10000c","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","balance":"0x15e578bd8e9c8000","nonce":"0x100000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","nonce":"0x100000","balance":"0x2386f26fc10000","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"rejected"},{"id":1467868023090690,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xC5b8dBAc4c1d3F152cDeb400E2313F309c410aCb","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"2","seedWords":null,"isConfirmed":true,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAccount":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}} \ No newline at end of file
+{"metamask":{"isInitialized":true,"isUnlocked":true,"currentDomain":"example.com","rpcTarget":"https://rawtestrpc.metamask.io/","identities":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"name":"Wallet 1","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","mayBeFauceting":false},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"name":"Wallet 2","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb","mayBeFauceting":false},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"name":"Wallet 3","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d","mayBeFauceting":false}},"unconfTxs":{"1467868023090690":{"id":1467868023090690,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xC5b8dBAc4c1d3F152cDeb400E2313F309c410aCb","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}},"accounts":{"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825":{"code":"0x","balance":"0x38326dc32cf80800","nonce":"0x10000c","address":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb":{"code":"0x","balance":"0x15e578bd8e9c8000","nonce":"0x100000","address":"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"},"0x2f8d4a878cfa04a6e60d46362f5644deab66572d":{"code":"0x","nonce":"0x100000","balance":"0x2386f26fc10000","address":"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"}},"transactions":[{"id":1467742640796159,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742640796159,"metamaskNetworkId":"2"},"time":1467742640796,"status":"rejected"},{"id":1467742652846512,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e6275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467742652846512,"metamaskNetworkId":"2"},"time":1467742652846,"status":"confirmed","hash":"0xa991793a6918aea6d58c30934dab5ca4c0a47c2444e5b60769637491f118de26"},{"id":1467755147235616,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","data":"0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161099338038061099383398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b505050505050610722806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f257806318160ddd1461015d57806323b872dd14610166578063313ce567146102c757806354fd4d50146102d357806370a082311461033057806395d89b411461035e578063a9059cbb146103bb578063cae9ca511461044d578063dd62ed3e14610639575b610000610002565b61066d60038054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825282208590556060858152919392917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a35060015b92915050565b61034c60025481565b61034c600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101b9575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101c55750600082115b1561071d57816000600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816000600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816001600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054039250508190555082600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3506001610632565b6106db60045460ff1681565b61066d60068054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b61066d60058054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156107105780601f106106e557610100808354040283529160200191610710565b61034c60043560243533600160a060020a03166000908152602081905260408120548290108015906103ed5750600082115b1561071857604080822080548490039055600160a060020a03808516808452918320805485019055606084815233909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3506001610157565b60806020604435600481810135601f81018490049093028401604052606083815261034c94823594602480359560649493910191908190838280828437509496505050505050506000826001600050600033600160a060020a03168152602001908152602001600020600050600086600160a060020a031681526020019081526020016000206000508190555083600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a8091040260e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105d45780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f1505060408051868152905133600160a060020a031692507f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259181900360200190a35060015b9392505050565b61034c600435602435600160a060020a03808316600090815260016020908152604080832093851683529290522054610157565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106cd5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6060908152602090f35b820191906000526020600020905b8154815290600101906020018083116106f357829003601f168201915b505050505081565b610157565b6106325600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000844616e4275636b7300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034442580000000000000000000000000000000000000000000000000000000000","gasPrice":"0xba43b7400","gas":"0xf4240","origin":"tokenfactory.surge.sh","metamaskId":1467755147235616,"metamaskNetworkId":"1"},"time":1467755147235,"status":"rejected"},{"id":1467868023090690,"txParams":{"from":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","value":"0x16345785d8a0000","to":"0xC5b8dBAc4c1d3F152cDeb400E2313F309c410aCb","origin":"MetaMask","metamaskId":1467868023090690,"metamaskNetworkId":"2"},"time":1467868023090,"status":"unconfirmed","containsDelegateCall":false}],"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825","network":"2","seedWords":null,"unconfMsgs":{},"messages":[],"provider":{"type":"testnet"},"selectedAddress":"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"},"appState":{"menuOpen":false,"currentView":{"name":"confTx","context":0},"accountDetail":{"subview":"transactions"},"currentDomain":"extensions","transForward":true,"isLoading":false,"warning":null},"identities":{}}
diff --git a/development/states/private-network.json b/development/states/private-network.json
new file mode 100644
index 000000000..155aa6584
--- /dev/null
+++ b/development/states/private-network.json
@@ -0,0 +1,84 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
+ "name": "Account 1",
+ "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "mayBeFauceting": false
+ },
+ "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
+ "name": "Account 2",
+ "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
+ "mayBeFauceting": false
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 9.52855776,
+ "conversionDate": 1479756513,
+ "accounts": {
+ "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
+ "balance": "0x0",
+ "nonce": "0x0",
+ "code": "0x0",
+ "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ },
+ "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
+ "balance": "0x0",
+ "nonce": "0x0",
+ "code": "0x0",
+ "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
+ }
+ },
+ "transactions": [
+ {
+ "id": 5551995700357153,
+ "txParams": {
+ "from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
+ "data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a",
+ "metamaskId": 5551995700357153,
+ "metamaskNetworkId": "1479490588308"
+ },
+ "time": 1479498745949,
+ "status": "confirmed",
+ "gasMultiplier": 1,
+ "metamaskNetworkId": "1479490588308",
+ "containsDelegateCall": true,
+ "estimatedGas": "0x24b33",
+ "hash": "0xad609a6931f54a575ad71222ffc27cd6746017106d5b89f4ad300b37b273f8ac"
+ }
+ ],
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "network": "1479753732793",
+ "isEthConfirmed": true,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "gasMultiplier": false,
+ "provider": {
+ "type": "rpc",
+ "rpcTarget": "http://localhost:8545"
+ },
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accountDetail"
+ },
+ "accountDetail": {
+ "subview": "transactions",
+ "accountExport": "none",
+ "privateKey": ""
+ },
+ "transForward": false,
+ "isLoading": false,
+ "warning": null,
+ "scrollToBottom": false
+ },
+ "identities": {}
+}
diff --git a/development/states/terms.json b/development/states/restore-vault.json
index 3a4914841..ad136c78e 100644
--- a/development/states/terms.json
+++ b/development/states/restore-vault.json
@@ -6,30 +6,32 @@
"rpcTarget": "https://rawtestrpc.metamask.io/",
"identities": {},
"unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 0,
+ "conversionDate": "N/A",
"accounts": {},
"transactions": [],
- "network": "2",
"seedWords": null,
- "isConfirmed": false,
- "isEthConfirmed": false,
"unconfMsgs": {},
"messages": [],
+ "shapeShiftTxList": [],
"provider": {
"type": "testnet"
- }
+ },
+ "network": "2"
},
"appState": {
"menuOpen": false,
"currentView": {
- "name": "EthStoreWarning"
+ "name": "restoreVault"
},
"accountDetail": {
"subview": "transactions"
},
- "currentDomain": "127.0.0.1:9966",
+ "currentDomain": "extensions",
"transForward": true,
"isLoading": false,
"warning": null
},
"identities": {}
-} \ No newline at end of file
+}
diff --git a/development/states/send.json b/development/states/send.json
new file mode 100644
index 000000000..c054af55b
--- /dev/null
+++ b/development/states/send.json
@@ -0,0 +1,74 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "currentDomain": "example.com",
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
+ "name": "Wallet 1",
+ "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "mayBeFauceting": false
+ },
+ "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
+ "name": "Wallet 2",
+ "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
+ "mayBeFauceting": false
+ },
+ "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
+ "name": "Wallet 3",
+ "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
+ "mayBeFauceting": false
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 11.21283484,
+ "conversionDate": 1472158984,
+ "accounts": {
+ "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
+ "code": "0x",
+ "balance": "0x34693f54a1e25900",
+ "nonce": "0x100013",
+ "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ },
+ "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
+ "code": "0x",
+ "nonce": "0x100000",
+ "balance": "0x18af912cee770000",
+ "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
+ },
+ "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
+ "code": "0x",
+ "nonce": "0x100000",
+ "balance": "0x2386f26fc10000",
+ "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
+ }
+ },
+ "transactions": [],
+ "network": "2",
+ "seedWords": null,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "sendTransaction"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "currentDomain": "127.0.0.1:9966",
+ "transForward": true,
+ "isLoading": false,
+ "warning": null,
+ "detailView": {}
+ },
+ "identities": {}
+}
diff --git a/development/states/shapeshift.json b/development/states/shapeshift.json
new file mode 100644
index 000000000..bc10143d0
--- /dev/null
+++ b/development/states/shapeshift.json
@@ -0,0 +1,346 @@
+{
+ "metamask": {
+ "isInitialized": true,
+ "isUnlocked": true,
+ "currentDomain": "example.com",
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {
+ "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
+ "name": "Wallet 1",
+ "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "mayBeFauceting": false
+ },
+ "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
+ "name": "Wallet 2",
+ "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
+ "mayBeFauceting": false
+ },
+ "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
+ "name": "Wallet 3",
+ "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
+ "mayBeFauceting": false
+ }
+ },
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 11.21274318,
+ "conversionDate": 1472159644,
+ "accounts": {
+ "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
+ "code": "0x",
+ "nonce": "0x13",
+ "balance": "0x461d4a64e937d3d1",
+ "address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ },
+ "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
+ "code": "0x",
+ "nonce": "0x0",
+ "balance": "0x0",
+ "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
+ },
+ "0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
+ "code": "0x",
+ "balance": "0x0",
+ "nonce": "0x0",
+ "address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
+ }
+ },
+ "transactions": [],
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "network": "1",
+ "seedWords": null,
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "provider": {
+ "type": "mainnet"
+ },
+ "selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "buyEth",
+ "context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "currentDomain": "127.0.0.1:9966",
+ "transForward": true,
+ "isLoading": false,
+ "detailView": {},
+ "buyView": {
+ "subview": "buyForm",
+ "formView": {
+ "coinbase": false,
+ "shapeshift": true,
+ "marketinfo": {
+ "pair": "btc_eth",
+ "rate": 51.14252949,
+ "minerFee": 0.01,
+ "limit": 2.60306578,
+ "minimum": 0.00038935,
+ "maxLimit": 8.67688592
+ },
+ "coinOptions": {
+ "BTC": {
+ "name": "Bitcoin",
+ "symbol": "BTC",
+ "image": "https://shapeshift.io/images/coins/bitcoin.png",
+ "status": "available"
+ },
+ "BCY": {
+ "name": "BitCrystals",
+ "symbol": "BCY",
+ "image": "https://shapeshift.io/images/coins/bitcrystals.png",
+ "status": "available"
+ },
+ "BLK": {
+ "name": "Blackcoin",
+ "symbol": "BLK",
+ "image": "https://shapeshift.io/images/coins/blackcoin.png",
+ "status": "available"
+ },
+ "BTS": {
+ "name": "Bitshares",
+ "symbol": "BTS",
+ "specialReturn": false,
+ "specialOutgoing": true,
+ "specialIncoming": true,
+ "fieldName": "destTag",
+ "fieldKey": "destTag",
+ "image": "https://shapeshift.io/images/coins/bitshares.png",
+ "status": "available"
+ },
+ "CLAM": {
+ "name": "Clams",
+ "symbol": "CLAM",
+ "image": "https://shapeshift.io/images/coins/clams.png",
+ "status": "available"
+ },
+ "DASH": {
+ "name": "Dash",
+ "symbol": "DASH",
+ "image": "https://shapeshift.io/images/coins/dash.png",
+ "status": "available"
+ },
+ "DGB": {
+ "name": "Digibyte",
+ "symbol": "DGB",
+ "image": "https://shapeshift.io/images/coins/digibyte.png",
+ "status": "available"
+ },
+ "DAO": {
+ "name": "TheDao",
+ "symbol": "DAO",
+ "image": "https://shapeshift.io/images/coins/thedao.png",
+ "status": "available"
+ },
+ "DGD": {
+ "name": "DigixDao",
+ "symbol": "DGD",
+ "image": "https://shapeshift.io/images/coins/digixdao.png",
+ "status": "available"
+ },
+ "DOGE": {
+ "name": "Dogecoin",
+ "symbol": "DOGE",
+ "image": "https://shapeshift.io/images/coins/dogecoin.png",
+ "status": "available"
+ },
+ "EMC": {
+ "name": "Emercoin",
+ "symbol": "EMC",
+ "image": "https://shapeshift.io/images/coins/emercoin.png",
+ "status": "available"
+ },
+ "ETH": {
+ "name": "Ether",
+ "symbol": "ETH",
+ "image": "https://shapeshift.io/images/coins/ether.png",
+ "status": "available"
+ },
+ "ETC": {
+ "name": "Ether Classic",
+ "symbol": "ETC",
+ "image": "https://shapeshift.io/images/coins/etherclassic.png",
+ "status": "available"
+ },
+ "FCT": {
+ "name": "Factoids",
+ "symbol": "FCT",
+ "image": "https://shapeshift.io/images/coins/factoids.png",
+ "status": "available"
+ },
+ "LBC": {
+ "name": "LBRY Credits",
+ "symbol": "LBC",
+ "image": "https://shapeshift.io/images/coins/lbry.png",
+ "status": "available"
+ },
+ "LSK": {
+ "name": "Lisk",
+ "symbol": "LSK",
+ "image": "https://shapeshift.io/images/coins/lisk.png",
+ "status": "available"
+ },
+ "LTC": {
+ "name": "Litecoin",
+ "symbol": "LTC",
+ "image": "https://shapeshift.io/images/coins/litecoin.png",
+ "status": "available"
+ },
+ "MAID": {
+ "name": "Maidsafe",
+ "symbol": "MAID",
+ "image": "https://shapeshift.io/images/coins/maidsafe.png",
+ "status": "available"
+ },
+ "MINT": {
+ "name": "Mintcoin",
+ "symbol": "MINT",
+ "image": "https://shapeshift.io/images/coins/mintcoin.png",
+ "status": "available"
+ },
+ "MONA": {
+ "name": "Monacoin",
+ "symbol": "MONA",
+ "image": "https://shapeshift.io/images/coins/monacoin.png",
+ "status": "available"
+ },
+ "MSC": {
+ "name": "Omni",
+ "symbol": "MSC",
+ "image": "https://shapeshift.io/images/coins/mastercoin.png",
+ "status": "available"
+ },
+ "NBT": {
+ "name": "Nubits",
+ "symbol": "NBT",
+ "image": "https://shapeshift.io/images/coins/nubits.png",
+ "status": "available"
+ },
+ "NMC": {
+ "name": "Namecoin",
+ "symbol": "NMC",
+ "image": "https://shapeshift.io/images/coins/namecoin.png",
+ "status": "available"
+ },
+ "NVC": {
+ "name": "Novacoin",
+ "symbol": "NVC",
+ "image": "https://shapeshift.io/images/coins/novacoin.png",
+ "status": "available"
+ },
+ "NXT": {
+ "name": "Nxt",
+ "symbol": "NXT",
+ "specialReturn": false,
+ "specialOutgoing": true,
+ "specialIncoming": true,
+ "specialIncomingStatus": false,
+ "fieldName": "Public Key (only for unfunded accounts!)",
+ "fieldKey": "rsAddress",
+ "image": "https://shapeshift.io/images/coins/nxt.png",
+ "status": "available"
+ },
+ "PPC": {
+ "name": "Peercoin",
+ "symbol": "PPC",
+ "image": "https://shapeshift.io/images/coins/peercoin.png",
+ "status": "available"
+ },
+ "RDD": {
+ "name": "Reddcoin",
+ "symbol": "RDD",
+ "image": "https://shapeshift.io/images/coins/reddcoin.png",
+ "status": "available"
+ },
+ "SDC": {
+ "name": "Shadowcash",
+ "symbol": "SDC",
+ "image": "https://shapeshift.io/images/coins/shadowcash.png",
+ "status": "available"
+ },
+ "SC": {
+ "name": "Siacoin",
+ "symbol": "SC",
+ "image": "https://shapeshift.io/images/coins/siacoin.png",
+ "status": "available"
+ },
+ "SJCX": {
+ "name": "StorjX",
+ "symbol": "SJCX",
+ "image": "https://shapeshift.io/images/coins/storjcoinx.png",
+ "status": "available"
+ },
+ "START": {
+ "name": "Startcoin",
+ "symbol": "START",
+ "image": "https://shapeshift.io/images/coins/startcoin.png",
+ "status": "available"
+ },
+ "STEEM": {
+ "name": "Steem",
+ "symbol": "STEEM",
+ "specialReturn": false,
+ "specialOutgoing": true,
+ "specialIncoming": true,
+ "fieldName": "destTag",
+ "fieldKey": "destTag",
+ "image": "https://shapeshift.io/images/coins/steem.png",
+ "status": "available"
+ },
+ "USDT": {
+ "name": "Tether",
+ "symbol": "USDT",
+ "image": "https://shapeshift.io/images/coins/tether.png",
+ "status": "available"
+ },
+ "VOX": {
+ "name": "Voxels",
+ "symbol": "VOX",
+ "image": "https://shapeshift.io/images/coins/voxels.png",
+ "status": "available"
+ },
+ "VRC": {
+ "name": "Vericoin",
+ "symbol": "VRC",
+ "image": "https://shapeshift.io/images/coins/vericoin.png",
+ "status": "available"
+ },
+ "VTC": {
+ "name": "Vertcoin",
+ "symbol": "VTC",
+ "image": "https://shapeshift.io/images/coins/vertcoin.png",
+ "status": "available"
+ },
+ "XCP": {
+ "name": "Counterparty",
+ "symbol": "XCP",
+ "image": "https://shapeshift.io/images/coins/counterparty.png",
+ "status": "available"
+ },
+ "XMR": {
+ "name": "Monero",
+ "symbol": "XMR",
+ "specialReturn": false,
+ "specialOutgoing": true,
+ "specialIncoming": true,
+ "fieldName": "Payment Id",
+ "qrName": "tx_payment_id",
+ "fieldKey": "paymentId",
+ "image": "https://shapeshift.io/images/coins/monero.png",
+ "status": "available"
+ }
+ }
+ },
+ "buyAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
+ "amount": "5.00",
+ "warning": null
+ },
+ "isSubLoading": false
+ },
+ "identities": {}
+}
diff --git a/development/states/show-seed-words.json b/development/states/show-seed-words.json
index 19be51fbd..55127f60a 100644
--- a/development/states/show-seed-words.json
+++ b/development/states/show-seed-words.json
@@ -45,7 +45,6 @@
"transactions": [],
"network": "2",
"seedWords": "debris dizzy just program just float decrease vacant alarm reduce speak stadium",
- "isConfirmed": true,
"unconfMsgs": {},
"messages": [],
"provider": {
diff --git a/development/states/terms-and-conditions.json b/development/states/terms-and-conditions.json
new file mode 100644
index 000000000..b995e446f
--- /dev/null
+++ b/development/states/terms-and-conditions.json
@@ -0,0 +1,39 @@
+{
+ "metamask": {
+ "isInitialized": false,
+ "isUnlocked": false,
+ "rpcTarget": "https://rawtestrpc.metamask.io/",
+ "identities": {},
+ "unconfTxs": {},
+ "currentFiat": "USD",
+ "conversionRate": 8.18703468,
+ "conversionDate": 1481755832,
+ "network": "3",
+ "accounts": {},
+ "transactions": [],
+ "provider": {
+ "type": "testnet"
+ },
+ "unconfMsgs": {},
+ "messages": [],
+ "shapeShiftTxList": [],
+ "keyringTypes": [
+ "Simple Key Pair",
+ "HD Key Tree"
+ ]
+ },
+ "appState": {
+ "menuOpen": false,
+ "currentView": {
+ "name": "accounts",
+ "detailView": null
+ },
+ "accountDetail": {
+ "subview": "transactions"
+ },
+ "transForward": true,
+ "isLoading": false,
+ "warning": null
+ },
+ "identities": {}
+}
diff --git a/development/test.html b/development/test.html
new file mode 100644
index 000000000..702be7fa0
--- /dev/null
+++ b/development/test.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>MetaMask</title>
+
+ <script>
+ window.METAMASK_DEBUG = true
+ window.TEST_MODE = true
+ </script>
+ </head>
+ <body>
+
+ <!-- app content -->
+ <div id="app-content" style="height: 100%"></div>
+ <script src="./bundle.js" type="text/javascript" charset="utf-8"></script>
+
+ </body>
+
+<style>
+html, body, #app-content, .super-dev-container {
+ height: 100%;
+ width: 100%;
+ position: relative;
+ background: white;
+}
+.mock-app-root {
+ background: #F7F7F7;
+}
+</style>
+</html>
diff --git a/docs/extension_description/en.txt b/docs/extension_description/en.txt
new file mode 100644
index 000000000..53bc4780d
--- /dev/null
+++ b/docs/extension_description/en.txt
@@ -0,0 +1,8 @@
+MetaMask is an extension for accessing Ethereum enabled distributed applications, or "Dapps" in your normal browser!
+
+The extension injects the Ethereum web3 API into every website's javascript context, so that dapps can read from the blockchain.
+
+MetaMask also lets the user create and manage their own identities, so when a Dapp wants to perform a transaction and write to the blockchain, the user gets a secure interface to review the transaction, before approving or rejecting it.
+
+Because it adds functionality to the normal browser context, MetaMask requires the permission to read and write to any webpage. You can always "view the source" of MetaMask the way you do any extension, or view the source code on Github:
+https://github.com/MetaMask/metamask-plugin \ No newline at end of file
diff --git a/docs/form_persisting_architecture.md b/docs/form_persisting_architecture.md
new file mode 100644
index 000000000..e2d766ccc
--- /dev/null
+++ b/docs/form_persisting_architecture.md
@@ -0,0 +1,28 @@
+# Form Persisting Architecture
+
+Since:
+ - The popup is torn down completely on every click outside of it.
+ - We have forms with multiple fields (like passwords & seed phrases) that might encourage a user to leave our panel to refer to a password manager.
+
+ We cause user friction when we lose the contents of certain forms.
+
+ This calls for an architecture of a form component that can completely persist its values to LocalStorage on every relevant change, and restore those values on reopening.
+
+ To achieve this, we have defined a class, a subclass of `React.Component`, called `PersistentForm`, and it's stored at `ui/lib/persistent-form.js`.
+
+To use this class, simply take your form component (the component that renders `input`, `select`, or `textarea` elements), and make it subclass from `PersistentForm` instead of `React.Component`.
+
+You can see an example of this in use in `ui/app/first-time/restore-vault.js`.
+
+Additionally, any field whose value should be persisted, should have a `persistentFormId` attribute, which needs to be assigned under a `dataset` key on the main `attributes` hash. For example:
+
+```javascript
+ return h('textarea.twelve-word-phrase.letter-spacey', {
+ dataset: {
+ persistentFormId: 'wallet-seed',
+ },
+ })
+```
+
+That's it! This field should be persisted to `localStorage` on each `keyUp`, those values should be restored on view load, and the cached values should be cleared when navigating deliberately away from the form.
+
diff --git a/docs/multi_vault_planning.md b/docs/multi_vault_planning.md
new file mode 100644
index 000000000..fdde2bc50
--- /dev/null
+++ b/docs/multi_vault_planning.md
@@ -0,0 +1,188 @@
+https://hackmd.io/JwIwDMDGKQZgtAFgKZjEgbARhPAhgKxZbwAcA7LAWOQCaKEgFA==?edit
+
+Subscribablez(initState)
+ .subscribe()
+ .emitUpdate(newState)
+ //.getState()
+
+
+var initState = fromDisk()
+ReduxStore(reducer, initState)
+.reduce(action) -> .emitUpdate()
+
+ReduxStore.subscribe(toDisk)
+
+
+### KeyChainManager / idStore 2.0 (maybe just in MetaMaskController)
+ keychains: []
+ getAllAccounts(cb)
+ getAllKeychainViewStates(cb) -> returns [ KeyChainViewState]
+
+#### Old idStore external methods, for feature parity:
+
+- init(configManager)
+- setStore(ethStore)
+- getState()
+- getSelectedAddres()
+- setSelectedAddress()
+- createNewVault()
+- recoverFromSeed()
+- submitPassword()
+- approveTransaction()
+- cancelTransaction()
+- addUnconfirmedMessage(msgParams, cb)
+- signMessage()
+- cancelMessage()
+- setLocked()
+- clearSeedWordCache()
+- exportAccount()
+- revealAccount()
+- saveAccountLabel()
+- tryPassword()
+- recoverSeed()
+- getNetwork()
+
+##### Of those methods
+
+Where they should end up:
+
+##### MetaMaskController
+
+- getNetwork()
+
+##### KeyChainManager
+
+- init(configManager)
+- setStore(ethStore)
+- getState() // Deprecate for unidirectional flow
+- on('update', cb)
+- createNewVault(password)
+- getSelectedAddres()
+- setSelectedAddress()
+- submitPassword()
+- tryPassword()
+- approveTransaction()
+- cancelTransaction()
+- signMessage()
+- cancelMessage()
+- setLocked()
+- exportAccount()
+
+##### Bip44 KeyChain
+
+- getState() // Deprecate for unidirectional flow
+- on('update', cb)
+
+If we adopt a ReactStore style unidirectional action dispatching data flow, these methods will be unified under a `dispatch` method, and rather than having a cb will emit an update to the UI:
+
+- createNewKeyChain(entropy)
+- recoverFromSeed()
+- approveTransaction()
+- signMessage()
+- clearSeedWordCache()
+- exportAccount()
+- revealAccount()
+- saveAccountLabel()
+- recoverSeed()
+
+Additional methods, new to this:
+- serialize()
+ - Returns pojo with optional `secret` key whose contents will be encrypted with the users' password and salt when written to disk.
+ - The isolation of secrets is to preserve performance when decrypting user data.
+- deserialize(pojo)
+
+### KeyChain (ReduxStore?)
+ // attributes
+ @name
+
+ signTx(txParams, cb)
+ signMsg(msg, cb)
+
+ getAddressList(cb)
+
+ getViewState(cb) -> returns KeyChainViewState
+
+ serialize(cb) -> obj
+ deserialize(obj)
+
+ dispatch({ type: <str>, value: <pojo> })
+
+
+### KeyChainViewState
+ // The serialized, renderable keychain data
+ accountList: [],
+ typeName: 'uPort',
+ iconAddress: 'uport.gif',
+ internal: {} // Subclass-defined metadata
+
+### KeyChainReactComponent
+ // takes a KeyChainViewState
+
+ // Subclasses of this:
+ - KeyChainListItemComponent
+ - KeyChainInitComponent - Maybe part of the List Item
+ - KeyChainAccountHeaderComponent
+ - KeyChainConfirmationComponent
+ // Account list item, tx confirmation extra data (like a QR code),
+ // Maybe an options screen, init screen,
+
+ how to send actions?
+ emitAction(keychains.<id>.didInit)
+
+
+gimmeRemoteKeychain((err, remoteKeychain)=>
+
+)
+
+
+
+
+
+KeyChainReactComponent({
+ keychain
+})
+
+Keychain:
+ methods:{},
+ cachedAccountList: [],
+ name: '',
+
+
+CoinbaseKeychain
+ getAccountList
+
+
+CoinbaseKeychainComponent
+ isLoading = true
+ keychain.getAccountList(()=>{
+ isLoading=false
+ accountList=accounts
+ })
+
+
+
+
+
+KeyChainViewState {
+ attributes: {
+ //mandatory:
+ accountList: [],
+ typeName: 'uPort',
+ iconAddress: 'uport.gif',
+
+ internal: {
+ // keychain-specific metadata
+ proxyAddresses: {
+ 0xReal: '0xProxy'
+ }
+ },
+ },
+ methods: {
+ // arbitrary, internal
+ }
+}
+
+## A note on the security of arbitrary action dispatchers
+
+Since keychains will be dispatching actions that are then passed through the background process to be routed, we should not trust or require them to include their own keychain ID as a prefix to their action, but we should tack it on ourselves, so that no action dispatched by a KeyChainComponent ever reaches any KeyChain other than its own.
+
diff --git a/gulpfile.js b/gulpfile.js
index aeaf3e674..fe223adf1 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,5 +1,6 @@
var watchify = require('watchify')
var browserify = require('browserify')
+var disc = require('disc')
var gulp = require('gulp')
var source = require('vinyl-source-stream')
var buffer = require('vinyl-buffer')
@@ -10,19 +11,23 @@ var jsoneditor = require('gulp-json-editor')
var zip = require('gulp-zip')
var assign = require('lodash.assign')
var livereload = require('gulp-livereload')
-var brfs = require('gulp-brfs')
var del = require('del')
var eslint = require('gulp-eslint')
var fs = require('fs')
var path = require('path')
var manifest = require('./app/manifest.json')
+var gulpif = require('gulp-if')
+var replace = require('gulp-replace')
+var mkdirp = require('mkdirp')
+
+var disableLiveReload = gutil.env.disableLiveReload
+var debug = gutil.env.debug
// browser reload
gulp.task('dev:reload', function() {
livereload.listen({
port: 35729,
- // basePath: './dist/firefox/'
})
})
@@ -34,6 +39,8 @@ gulp.task('copy:locales', copyTask({
destinations: [
'./dist/firefox/_locales',
'./dist/chrome/_locales',
+ './dist/edge/_locales',
+ './dist/opera/_locales',
]
}))
gulp.task('copy:images', copyTask({
@@ -41,6 +48,8 @@ gulp.task('copy:images', copyTask({
destinations: [
'./dist/firefox/images',
'./dist/chrome/images',
+ './dist/edge/images',
+ './dist/opera/images',
],
}))
gulp.task('copy:fonts', copyTask({
@@ -48,6 +57,8 @@ gulp.task('copy:fonts', copyTask({
destinations: [
'./dist/firefox/fonts',
'./dist/chrome/fonts',
+ './dist/edge/fonts',
+ './dist/opera/fonts',
],
}))
gulp.task('copy:reload', copyTask({
@@ -55,6 +66,8 @@ gulp.task('copy:reload', copyTask({
destinations: [
'./dist/firefox/scripts',
'./dist/chrome/scripts',
+ './dist/edge/scripts',
+ './dist/opera/scripts',
],
pattern: '/chromereload.js',
}))
@@ -63,12 +76,14 @@ gulp.task('copy:root', copyTask({
destinations: [
'./dist/firefox',
'./dist/chrome',
+ './dist/edge',
+ './dist/opera',
],
pattern: '/*',
}))
-gulp.task('manifest:cleanup', function() {
- return gulp.src('./dist/firefox/manifest.json')
+gulp.task('manifest:chrome', function() {
+ return gulp.src('./dist/chrome/manifest.json')
.pipe(jsoneditor(function(json) {
delete json.applications
return json
@@ -76,7 +91,48 @@ gulp.task('manifest:cleanup', function() {
.pipe(gulp.dest('./dist/chrome', { overwrite: true }))
})
-gulp.task('copy', gulp.series(gulp.parallel('copy:locales','copy:images','copy:fonts','copy:reload','copy:root'), 'manifest:cleanup'))
+gulp.task('manifest:opera', function() {
+ return gulp.src('./dist/opera/manifest.json')
+ .pipe(jsoneditor(function(json) {
+ json.permissions = [
+ "storage",
+ "tabs",
+ "clipboardWrite",
+ "clipboardRead",
+ "http://localhost:8545/"
+ ]
+ return json
+ }))
+ .pipe(gulp.dest('./dist/opera', { overwrite: true }))
+})
+
+gulp.task('manifest:production', function() {
+ return gulp.src([
+ './dist/firefox/manifest.json',
+ './dist/chrome/manifest.json',
+ './dist/edge/manifest.json',
+ ],{base: './dist/'})
+ .pipe(gulpif(disableLiveReload,jsoneditor(function(json) {
+ json.background.scripts = ["scripts/background.js"]
+ return json
+ })))
+ .pipe(gulp.dest('./dist/', { overwrite: true }))
+})
+
+const staticFiles = [
+ 'locales',
+ 'images',
+ 'fonts',
+ 'root'
+]
+
+var copyStrings = staticFiles.map(staticFile => `copy:${staticFile}`)
+
+if (!disableLiveReload) {
+ copyStrings.push('copy:reload')
+}
+
+gulp.task('copy', gulp.series(gulp.parallel(...copyStrings), 'manifest:production', 'manifest:chrome', 'manifest:opera'))
gulp.task('copy:watch', function(){
gulp.watch(['./app/{_locales,images}/*', './app/scripts/chromereload.js', './app/*.{html,json}'], gulp.series('copy'))
})
@@ -110,14 +166,27 @@ const jsFiles = [
'popup',
]
+// bundle tasks
+
+var jsDevStrings = jsFiles.map(jsFile => `dev:js:${jsFile}`)
+var jsBuildStrings = jsFiles.map(jsFile => `build:js:${jsFile}`)
+
+jsFiles.forEach((jsFile) => {
+ gulp.task(`dev:js:${jsFile}`, bundleTask({ watch: true, label: jsFile, filename: `${jsFile}.js` }))
+ gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, label: jsFile, filename: `${jsFile}.js` }))
+})
+
+gulp.task('dev:js', gulp.parallel(...jsDevStrings))
+gulp.task('build:js', gulp.parallel(...jsBuildStrings))
+
+// disc bundle analyzer tasks
+
jsFiles.forEach((jsFile) => {
- gulp.task(`dev:js:${jsFile}`, bundleTask({ watch: true, filename: `${jsFile}.js` }))
- gulp.task(`build:js:${jsFile}`, bundleTask({ watch: false, filename: `${jsFile}.js` }))
+ gulp.task(`disc:${jsFile}`, bundleTask({ label: jsFile, filename: `${jsFile}.js` }))
})
-gulp.task('dev:js', gulp.parallel('dev:js:inpage','dev:js:contentscript','dev:js:background','dev:js:popup'))
+gulp.task('disc', gulp.parallel(jsFiles.map(jsFile => `disc:${jsFile}`)))
-gulp.task('build:js', gulp.parallel('build:js:inpage','build:js:contentscript','build:js:background','build:js:popup'))
// clean dist
@@ -127,21 +196,16 @@ gulp.task('clean', function clean() {
})
// zip tasks for distribution
-gulp.task('zip:chrome', () => {
- return gulp.src('dist/chrome/**')
- .pipe(zip(`metamask-chrome-${manifest.version}.zip`))
- .pipe(gulp.dest('builds'));
-});
-gulp.task('zip:firefox', () => {
- return gulp.src('dist/firefox/**')
- .pipe(zip(`metamask-firefox-${manifest.version}.zip`))
- .pipe(gulp.dest('builds'));
-});
-gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox'))
+gulp.task('zip:chrome', zipTask('chrome'))
+gulp.task('zip:firefox', zipTask('firefox'))
+gulp.task('zip:edge', zipTask('edge'))
+gulp.task('zip:opera', zipTask('opera'))
+gulp.task('zip', gulp.parallel('zip:chrome', 'zip:firefox', 'zip:edge', 'zip:opera'))
// high level tasks
gulp.task('dev', gulp.series('dev:js', 'copy', gulp.parallel('copy:watch', 'dev:reload')))
+
gulp.task('build', gulp.series('clean', gulp.parallel('build:js', 'copy')))
gulp.task('dist', gulp.series('build', 'zip'))
@@ -160,27 +224,71 @@ function copyTask(opts){
destinations.forEach(function(destination) {
stream = stream.pipe(gulp.dest(destination))
})
- stream.pipe(livereload())
+ stream.pipe(gulpif(!disableLiveReload,livereload()))
return stream
}
}
-function bundleTask(opts) {
+function zipTask(target) {
+ return () => {
+ return gulp.src(`dist/${target}/**`)
+ .pipe(zip(`metamask-${target}-${manifest.version}.zip`))
+ .pipe(gulp.dest('builds'));
+ }
+}
+
+function generateBundler(opts) {
var browserifyOpts = assign({}, watchify.args, {
entries: ['./app/scripts/'+opts.filename],
- debug: true,
plugin: 'browserify-derequire',
+ debug: debug,
+ fullPaths: debug,
})
- var bundler = browserify(browserifyOpts)
- bundler.transform('brfs')
+ return browserify(browserifyOpts)
+}
+
+function discTask(opts) {
+ let bundler = generateBundler(opts)
+
+ if (opts.watch) {
+ bundler = watchify(bundler)
+ // on any dep update, runs the bundler
+ bundler.on('update', performBundle)
+ }
+
+ // output build logs to terminal
+ bundler.on('log', gutil.log)
+
+ return performBundle
+
+ function performBundle(){
+ // start "disc" build
+ let discDir = path.join(__dirname, 'disc')
+ mkdirp.sync(discDir)
+ let discPath = path.join(discDir, `${opts.label}.html`)
+
+ return (
+ bundler.bundle()
+ .pipe(disc())
+ .pipe(fs.createWriteStream(discPath))
+ )
+ }
+}
+
+
+function bundleTask(opts) {
+ let bundler = generateBundler(opts)
+
if (opts.watch) {
bundler = watchify(bundler)
- bundler.on('update', performBundle) // on any dep update, runs the bundler
+ // on any file update, re-runs the bundler
+ bundler.on('update', performBundle)
}
- bundler.on('log', gutil.log) // output build logs to terminal
+ // output build logs to terminal
+ bundler.on('log', gutil.log)
return performBundle
@@ -190,17 +298,24 @@ function bundleTask(opts) {
bundler.bundle()
// log errors if they happen
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
+ // convert bundle stream to gulp vinyl stream
.pipe(source(opts.filename))
- .pipe(brfs())
- // optional, remove if you don't need to buffer file contents
+ // inject variables into bundle
+ .pipe(replace('\'GULP_METAMASK_DEBUG\'', debug))
+ // buffer file contents (?)
.pipe(buffer())
- // optional, remove if you dont want sourcemaps
- .pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
- // Add transformation tasks to the pipeline here.
- .pipe(sourcemaps.write('./')) // writes .map file
+ // sourcemaps
+ // loads map from browserify file
+ .pipe(sourcemaps.init({loadMaps: true}))
+ // writes .map file
+ .pipe(sourcemaps.write('./'))
+ // write completed bundles
.pipe(gulp.dest('./dist/firefox/scripts'))
.pipe(gulp.dest('./dist/chrome/scripts'))
- .pipe(livereload())
+ .pipe(gulp.dest('./dist/edge/scripts'))
+ .pipe(gulp.dest('./dist/opera/scripts'))
+ // finally, trigger live reload
+ .pipe(gulpif(!disableLiveReload, livereload()))
)
}
diff --git a/library/README.md b/library/README.md
new file mode 100644
index 000000000..6a6574dbd
--- /dev/null
+++ b/library/README.md
@@ -0,0 +1,24 @@
+start the dual servers (dapp + mascara)
+```
+node server.js
+```
+
+open the example dapp at `http://localhost:9002/`
+
+*You will need to build MetaMask in order for this to work*
+```
+gulp dev
+```
+to build MetaMask and have it live reload if you make changes
+
+
+## First time use:
+
+- navigate to: http://127.0.0.1:9001/popup/popup.html
+- Create an Account
+- go back to http://localhost:9002/
+- open devTools
+- click Sync Tx
+
+### Todos
+- Look into using [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
diff --git a/library/controller.js b/library/controller.js
new file mode 100644
index 000000000..5823287cc
--- /dev/null
+++ b/library/controller.js
@@ -0,0 +1,159 @@
+const urlUtil = require('url')
+const extend = require('xtend')
+const Dnode = require('dnode')
+const eos = require('end-of-stream')
+const ParentStream = require('iframe-stream').ParentStream
+const PortStream = require('../app/scripts/lib/port-stream.js')
+const notification = require('../app/scripts/lib/notifications.js')
+const messageManager = require('../app/scripts/lib/message-manager')
+const setupMultiplex = require('../app/scripts/lib/stream-utils.js').setupMultiplex
+const MetamaskController = require('../app/scripts/metamask-controller')
+const extension = require('../app/scripts/lib/extension')
+
+const STORAGE_KEY = 'metamask-config'
+
+
+initializeZeroClient()
+
+function initializeZeroClient() {
+
+ const controller = new MetamaskController({
+ // User confirmation callbacks:
+ showUnconfirmedMessage,
+ unlockAccountMessage,
+ showUnapprovedTx,
+ // Persistence Methods:
+ setData,
+ loadData,
+ })
+ const idStore = controller.idStore
+
+ function unlockAccountMessage () {
+ console.log('notif stub - unlockAccountMessage')
+ }
+
+ function showUnconfirmedMessage (msgParams, msgId) {
+ console.log('notif stub - showUnconfirmedMessage')
+ }
+
+ function showUnapprovedTx (txParams, txData, onTxDoneCb) {
+ console.log('notif stub - showUnapprovedTx')
+ }
+
+ //
+ // connect to other contexts
+ //
+
+ var connectionStream = new ParentStream()
+
+ connectRemote(connectionStream, getParentHref())
+
+ function connectRemote (connectionStream, originDomain) {
+ var isMetaMaskInternalProcess = (originDomain === '127.0.0.1:9001')
+ if (isMetaMaskInternalProcess) {
+ // communication with popup
+ setupTrustedCommunication(connectionStream, 'MetaMask')
+ } else {
+ // communication with page
+ setupUntrustedCommunication(connectionStream, originDomain)
+ }
+ }
+
+ function setupUntrustedCommunication (connectionStream, originDomain) {
+ // setup multiplexing
+ var mx = setupMultiplex(connectionStream)
+ // connect features
+ controller.setupProviderConnection(mx.createStream('provider'), originDomain)
+ controller.setupPublicConfig(mx.createStream('publicConfig'))
+ }
+
+ function setupTrustedCommunication (connectionStream, originDomain) {
+ // setup multiplexing
+ var mx = setupMultiplex(connectionStream)
+ // connect features
+ setupControllerConnection(mx.createStream('controller'))
+ controller.setupProviderConnection(mx.createStream('provider'), originDomain)
+ }
+
+ //
+ // remote features
+ //
+
+ function setupControllerConnection (stream) {
+ controller.stream = stream
+ var api = controller.getApi()
+ var dnode = Dnode(api)
+ stream.pipe(dnode).pipe(stream)
+ dnode.on('remote', (remote) => {
+ // push updates to popup
+ controller.ethStore.on('update', controller.sendUpdate.bind(controller))
+ controller.listeners.push(remote)
+ idStore.on('update', controller.sendUpdate.bind(controller))
+
+ // teardown on disconnect
+ eos(stream, () => {
+ controller.ethStore.removeListener('update', controller.sendUpdate.bind(controller))
+ })
+ })
+ }
+
+ function loadData () {
+ var oldData = getOldStyleData()
+ var newData
+ try {
+ newData = JSON.parse(window.localStorage[STORAGE_KEY])
+ } catch (e) {}
+
+ var data = extend({
+ meta: {
+ version: 0,
+ },
+ data: {
+ config: {
+ provider: {
+ type: 'testnet',
+ },
+ },
+ },
+ }, oldData || null, newData || null)
+ return data
+ }
+
+ function getOldStyleData () {
+ var config, wallet, seedWords
+
+ var result = {
+ meta: { version: 0 },
+ data: {},
+ }
+
+ try {
+ config = JSON.parse(window.localStorage['config'])
+ result.data.config = config
+ } catch (e) {}
+ try {
+ wallet = JSON.parse(window.localStorage['lightwallet'])
+ result.data.wallet = wallet
+ } catch (e) {}
+ try {
+ seedWords = window.localStorage['seedWords']
+ result.data.seedWords = seedWords
+ } catch (e) {}
+
+ return result
+ }
+
+ function setData (data) {
+ window.localStorage[STORAGE_KEY] = JSON.stringify(data)
+ }
+
+ function getParentHref(){
+ try {
+ var parentLocation = window.parent.location
+ return parentLocation.hostname + ':' + parentLocation.port
+ } catch (err) {
+ return 'unknown'
+ }
+ }
+
+}
diff --git a/library/example/index.html b/library/example/index.html
new file mode 100644
index 000000000..47d6da34f
--- /dev/null
+++ b/library/example/index.html
@@ -0,0 +1,17 @@
+<!doctype html>
+
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+
+ <title>MetaMask ZeroClient Example</title>
+
+</head>
+
+<body>
+ <button class="action-button-1">SYNC TX</button>
+ <button class="action-button-2">ASYNC TX</button>
+ <script src="./zero.js"></script>
+ <script src="./app.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/library/example/index.js b/library/example/index.js
new file mode 100644
index 000000000..4a107df6a
--- /dev/null
+++ b/library/example/index.js
@@ -0,0 +1,56 @@
+
+window.addEventListener('load', web3Detect)
+
+function web3Detect() {
+ if (global.web3) {
+ logToDom('web3 detected!')
+ startApp()
+ } else {
+ logToDom('no web3 detected!')
+ }
+}
+
+function startApp(){
+ console.log('app started')
+
+ var primaryAccount = null
+ console.log('getting main account...')
+ web3.eth.getAccounts(function(err, addresses){
+ if (err) throw err
+ console.log('set address', addresses[0])
+ primaryAccount = addresses[0]
+ })
+
+ document.querySelector('.action-button-1').addEventListener('click', function(){
+ console.log('saw click')
+ console.log('sending tx')
+ web3.eth.sendTransaction({
+ from: primaryAccount,
+ to: primaryAccount,
+ value: 0,
+ }, function(err, txHash){
+ if (err) throw err
+ console.log('sendTransaction result:', err || txHash)
+ })
+ })
+ document.querySelector('.action-button-2').addEventListener('click', function(){
+ console.log('saw click')
+ setTimeout(function(){
+ console.log('sending tx')
+ web3.eth.sendTransaction({
+ from: primaryAccount,
+ to: primaryAccount,
+ value: 0,
+ }, function(err, txHash){
+ if (err) throw err
+ console.log('sendTransaction result:', err || txHash)
+ })
+ })
+ })
+
+}
+
+function logToDom(message){
+ document.body.appendChild(document.createTextNode(message))
+ console.log(message)
+} \ No newline at end of file
diff --git a/library/index.js b/library/index.js
new file mode 100644
index 000000000..b5f4f6637
--- /dev/null
+++ b/library/index.js
@@ -0,0 +1,43 @@
+const Web3 = require('web3')
+const setupProvider = require('./lib/setup-provider.js')
+
+//
+// setup web3
+//
+var provider = setupProvider()
+hijackProvider(provider)
+var web3 = new Web3(provider)
+web3.setProvider = function(){
+ console.log('MetaMask - overrode web3.setProvider')
+}
+console.log('metamask lib hijacked provider')
+
+//
+// export web3
+//
+
+global.web3 = web3
+
+//
+// ui stuff
+//
+
+var shouldPop = false
+window.addEventListener('click', function(){
+ if (!shouldPop) return
+ shouldPop = false
+ window.open('http://127.0.0.1:9001/popup/popup.html', '', 'width=360 height=500')
+ console.log('opening window...')
+})
+
+
+function hijackProvider(provider){
+ var _super = provider.sendAsync.bind(provider)
+ provider.sendAsync = function(payload, cb){
+ if (payload.method === 'eth_sendTransaction') {
+ console.log('saw send')
+ shouldPop = true
+ }
+ _super(payload, cb)
+ }
+}
diff --git a/library/lib/setup-iframe.js b/library/lib/setup-iframe.js
new file mode 100644
index 000000000..db67163df
--- /dev/null
+++ b/library/lib/setup-iframe.js
@@ -0,0 +1,19 @@
+const Iframe = require('iframe')
+const IframeStream = require('iframe-stream').IframeStream
+
+module.exports = setupIframe
+
+
+function setupIframe(opts) {
+ opts = opts || {}
+ var frame = Iframe({
+ src: opts.zeroClientProvider || 'https://zero.metamask.io/',
+ container: opts.container || document.head,
+ sandboxAttributes: opts.sandboxAttributes || ['allow-scripts', 'allow-popups'],
+ })
+ var iframe = frame.iframe
+ iframe.style.setProperty('display', 'none')
+ var iframeStream = new IframeStream(iframe)
+
+ return iframeStream
+}
diff --git a/library/lib/setup-provider.js b/library/lib/setup-provider.js
new file mode 100644
index 000000000..9efd209cb
--- /dev/null
+++ b/library/lib/setup-provider.js
@@ -0,0 +1,25 @@
+const setupIframe = require('./setup-iframe.js')
+const MetamaskInpageProvider = require('../../app/scripts/lib/inpage-provider.js')
+
+module.exports = getProvider
+
+
+function getProvider(){
+
+ if (global.web3) {
+ console.log('MetaMask ZeroClient - using environmental web3 provider')
+ return global.web3.currentProvider
+ }
+
+ console.log('MetaMask ZeroClient - injecting zero-client iframe!')
+ var iframeStream = setupIframe({
+ zeroClientProvider: 'http://127.0.0.1:9001',
+ sandboxAttributes: ['allow-scripts', 'allow-popups', 'allow-same-origin'],
+ container: document.body,
+ })
+
+ var inpageProvider = new MetamaskInpageProvider(iframeStream)
+ return inpageProvider
+
+}
+
diff --git a/library/popup.js b/library/popup.js
new file mode 100644
index 000000000..667b13371
--- /dev/null
+++ b/library/popup.js
@@ -0,0 +1,19 @@
+const injectCss = require('inject-css')
+const MetaMaskUiCss = require('../ui/css')
+const startPopup = require('../app/scripts/popup-core')
+const setupIframe = require('./lib/setup-iframe.js')
+
+
+var css = MetaMaskUiCss()
+injectCss(css)
+
+var name = 'popup'
+window.METAMASK_UI_TYPE = name
+
+var iframeStream = setupIframe({
+ zeroClientProvider: 'http://127.0.0.1:9001',
+ sandboxAttributes: ['allow-scripts', 'allow-popups', 'allow-same-origin'],
+ container: document.body,
+})
+
+startPopup(iframeStream)
diff --git a/library/server.js b/library/server.js
new file mode 100644
index 000000000..797ad8a77
--- /dev/null
+++ b/library/server.js
@@ -0,0 +1,96 @@
+const express = require('express')
+const browserify = require('browserify')
+const watchify = require('watchify')
+const babelify = require('babelify')
+
+const zeroBundle = createBundle('./index.js')
+const controllerBundle = createBundle('./controller.js')
+const popupBundle = createBundle('./popup.js')
+const appBundle = createBundle('./example/index.js')
+
+//
+// Iframe Server
+//
+
+const iframeServer = express()
+
+// serve popup window
+iframeServer.get('/popup/scripts/popup.js', function(req, res){
+ res.send(popupBundle.latest)
+})
+iframeServer.use('/popup', express.static('../dist/chrome'))
+
+// serve controller bundle
+iframeServer.get('/controller.js', function(req, res){
+ res.send(controllerBundle.latest)
+})
+
+// serve background controller
+iframeServer.use(express.static('./server'))
+
+// start the server
+const mascaraPort = 9001
+iframeServer.listen(mascaraPort)
+console.log(`Mascara service listening on port ${mascaraPort}`)
+
+
+//
+// Dapp Server
+//
+
+const dappServer = express()
+
+// serve metamask-lib bundle
+dappServer.get('/zero.js', function(req, res){
+ res.send(zeroBundle.latest)
+})
+
+// serve dapp bundle
+dappServer.get('/app.js', function(req, res){
+ res.send(appBundle.latest)
+})
+
+// serve static
+dappServer.use(express.static('./example'))
+
+// start the server
+const dappPort = '9002'
+dappServer.listen(dappPort)
+console.log(`Dapp listening on port ${dappPort}`)
+
+//
+// util
+//
+
+function serveBundle(entryPoint){
+ const bundle = createBundle(entryPoint)
+ return function(req, res){
+ res.send(bundle.latest)
+ }
+}
+
+function createBundle(entryPoint){
+
+ var bundleContainer = {}
+
+ var bundler = browserify({
+ entries: [entryPoint],
+ cache: {},
+ packageCache: {},
+ plugin: [watchify],
+ })
+
+ bundler.on('update', bundle)
+ bundle()
+
+ return bundleContainer
+
+ function bundle() {
+ bundler.bundle(function(err, result){
+ if (err) throw err
+ console.log(`Bundle updated! (${entryPoint})`)
+ bundleContainer.latest = result.toString()
+ })
+ }
+
+}
diff --git a/library/server/index.html b/library/server/index.html
new file mode 100644
index 000000000..2308dd98b
--- /dev/null
+++ b/library/server/index.html
@@ -0,0 +1,20 @@
+<!doctype html>
+
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+
+ <title>MetaMask ZeroClient Iframe</title>
+ <meta name="description" content="MetaMask ZeroClient">
+ <meta name="author" content="MetaMask">
+
+ <!--[if lt IE 9]>
+ <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+</head>
+
+<body>
+ Hello! I am the MetaMask iframe.
+ <script src="/controller.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/mock-dev.js b/mock-dev.js
index 99b846e51..6563964c3 100644
--- a/mock-dev.js
+++ b/mock-dev.js
@@ -15,99 +15,60 @@
const extend = require('xtend')
const render = require('react-dom').render
const h = require('react-hyperscript')
+const pipe = require('mississippi').pipe
const Root = require('./ui/app/root')
const configureStore = require('./ui/app/store')
const actions = require('./ui/app/actions')
const states = require('./development/states')
const Selector = require('./development/selector')
const MetamaskController = require('./app/scripts/metamask-controller')
+const firstTimeState = require('./app/scripts/first-time-state')
const extension = require('./development/mockExtension')
+const noop = function () {}
+
+//
// Query String
+//
+
const qs = require('qs')
let queryString = qs.parse(window.location.href.split('#')[1])
-let selectedView = queryString.view || 'terms'
+let selectedView = queryString.view || 'first time'
const firstState = states[selectedView]
updateQueryParams(selectedView)
-// CSS
-const MetaMaskUiCss = require('./ui/css')
-const injectCss = require('inject-css')
-
-
function updateQueryParams(newView) {
queryString.view = newView
const params = qs.stringify(queryString)
window.location.href = window.location.href.split('#')[0] + `#${params}`
}
-const noop = function () {}
+//
+// CSS
+//
+
+const MetaMaskUiCss = require('./ui/css')
+const injectCss = require('inject-css')
+
+//
+// MetaMask Controller
+//
+
const controller = new MetamaskController({
// User confirmation callbacks:
showUnconfirmedMessage: noop,
unlockAccountMessage: noop,
- showUnconfirmedTx: noop,
- // Persistence Methods:
- setData,
- loadData,
+ showUnapprovedTx: noop,
+ // initial state
+ initState: firstTimeState,
})
+global.metamaskController = controller
-// Stub out localStorage for non-browser environments
-if (!window.localStorage) {
- window.localStorage = {}
-}
-const STORAGE_KEY = 'metamask-config'
-function loadData () {
- var oldData = getOldStyleData()
- var newData
- try {
- newData = JSON.parse(window.localStorage[STORAGE_KEY])
- } catch (e) {}
-
- var data = extend({
- meta: {
- version: 0,
- },
- data: {
- config: {
- provider: {
- type: 'testnet',
- },
- },
- },
- }, oldData || null, newData || null)
- return data
-}
-
-function setData (data) {
- window.localStorage[STORAGE_KEY] = JSON.stringify(data)
-}
-
-function getOldStyleData () {
- var config, wallet, seedWords
-
- var result = {
- meta: { version: 0 },
- data: {},
- }
-
- try {
- config = JSON.parse(window.localStorage['config'])
- result.data.config = config
- } catch (e) {}
- try {
- wallet = JSON.parse(window.localStorage['lightwallet'])
- result.data.wallet = wallet
- } catch (e) {}
- try {
- seedWords = window.localStorage['seedWords']
- result.data.seedWords = seedWords
- } catch (e) {}
-
- return result
-}
+//
+// User Interface
+//
-actions._setAccountManager(controller.getApi())
+actions._setBackgroundConnection(controller.getApi())
actions.update = function(stateName) {
selectedView = stateName
updateQueryParams(stateName)
diff --git a/notices/archive/notice_0.md b/notices/archive/notice_0.md
new file mode 100644
index 000000000..40a338016
--- /dev/null
+++ b/notices/archive/notice_0.md
@@ -0,0 +1,180 @@
+# Terms of Use #
+
+**THIS AGREEMENT IS SUBJECT TO BINDING ARBITRATION AND A WAIVER OF CLASS ACTION RIGHTS AS DETAILED IN SECTION 13. PLEASE READ THE AGREEMENT CAREFULLY.**
+
+_Our Terms of Use have been updated as of September 5, 2016_
+
+## 1. Acceptance of Terms ##
+
+MetaMask provides a platform for managing Ethereum (or "ETH") accounts, and allowing ordinary websites to interact with the Ethereum blockchain, while keeping the user in control over what transactions they approve, through our website located at[ ](http://metamask.io)[https://metamask.io/](https://metamask.io/) and browser plugin (the "Site") — which includes text, images, audio, code and other materials (collectively, the “Content”) and all of the features, and services provided. The Site, and any other features, tools, materials, or other services offered from time to time by MetaMask are referred to here as the “Service.” Please read these Terms of Use (the “Terms” or “Terms of Use”) carefully before using the Service. By using or otherwise accessing the Services, or clicking to accept or agree to these Terms where that option is made available, you (1) accept and agree to these Terms (2) consent to the collection, use, disclosure and other handling of information as described in our Privacy Policy and (3) any additional terms, rules and conditions of participation issued by MetaMask from time to time. If you do not agree to the Terms, then you may not access or use the Content or Services.
+
+## 2. Modification of Terms of Use ##
+
+Except for Section 13, providing for binding arbitration and waiver of class action rights, MetaMask reserves the right, at its sole discretion, to modify or replace the Terms of Use at any time. The most current version of these Terms will be posted on our Site. You shall be responsible for reviewing and becoming familiar with any such modifications. Use of the Services by you after any modification to the Terms constitutes your acceptance of the Terms of Use as modified.
+
+
+
+## 3. Eligibility ##
+
+You hereby represent and warrant that you are fully able and competent to enter into the terms, conditions, obligations, affirmations, representations and warranties set forth in these Terms and to abide by and comply with these Terms.
+
+MetaMask is a global platform and by accessing the Content or Services, you are representing and warranting that, you are of the legal age of majority in your jurisdiction as is required to access such Services and Content and enter into arrangements as provided by the Service. You further represent that you are otherwise legally permitted to use the service in your jurisdiction including owning cryptographic tokens of value, and interacting with the Services or Content in any way. You further represent you are responsible for ensuring compliance with the laws of your jurisdiction and acknowledge that MetaMask is not liable for your compliance with such laws.
+
+## 4 Account Password and Security ##
+
+When setting up an account within MetaMask, you will be responsible for keeping your own account secrets, which may be a twelve-word seed phrase, an account file, or other locally stored secret information. MetaMask encrypts this information locally with a password you provide, that we never send to our servers. You agree to (a) never use the same password for MetaMask that you have ever used outside of this service; (b) keep your secret information and password confidential and do not share them with anyone else; (c) immediately notify MetaMask of any unauthorized use of your account or breach of security. MetaMask cannot and will not be liable for any loss or damage arising from your failure to comply with this section.
+
+## 5. Representations, Warranties, and Risks ##
+
+### 5.1. Warranty Disclaimer ###
+
+You expressly understand and agree that your use of the Service is at your sole risk. The Service (including the Service and the Content) are provided on an "AS IS" and "as available" basis, without warranties of any kind, either express or implied, including, without limitation, implied warranties of merchantability, fitness for a particular purpose or non-infringement. You acknowledge that MetaMask has no control over, and no duty to take any action regarding: which users gain access to or use the Service; what effects the Content may have on you; how you may interpret or use the Content; or what actions you may take as a result of having been exposed to the Content. You release MetaMask from all liability for you having acquired or not acquired Content through the Service. MetaMask makes no representations concerning any Content contained in or accessed through the Service, and MetaMask will not be responsible or liable for the accuracy, copyright compliance, legality or decency of material contained in or accessed through the Service.
+
+### 5.2 Sophistication and Risk of Cryptographic Systems ###
+
+By utilizing the Service or interacting with the Content or platform in any way, you represent that you understand the inherent risks associated with cryptographic systems; and warrant that you have an understanding of the usage and intricacies of native cryptographic tokens, like Ether (ETH) and Bitcoin (BTC), smart contract based tokens such as those that follow the Ethereum Token Standard (https://github.com/ethereum/EIPs/issues/20), and blockchain-based software systems.
+
+### 5.3 Risk of Regulatory Actions in One or More Jurisdictions ###
+
+MetaMask and ETH could be impacted by one or more regulatory inquiries or regulatory action, which could impede or limit the ability of MetaMask to continue to develop, or which could impede or limit your ability to access or use the Service or Ethereum blockchain.
+
+### 5.4 Risk of Weaknesses or Exploits in the Field of Cryptography ###
+
+You acknowledge and understand that Cryptography is a progressing field. Advances in code cracking or technical advances such as the development of quantum computers may present risks to cryptocurrencies and Services of Content, which could result in the theft or loss of your cryptographic tokens or property. To the extent possible, MetaMask intends to update the protocol underlying Services to account for any advances in cryptography and to incorporate additional security measures, but does not guarantee or otherwise represent full security of the system. By using the Service or accessing Content, you acknowledge these inherent risks.
+
+### 5.5 Volatility of Crypto Currencies ###
+
+You understand that Ethereum and other blockchain technologies and associated currencies or tokens are highly volatile due to many factors including but not limited to adoption, speculation, technology and security risks. You also acknowledge that the cost of transacting on such technologies is variable and may increase at any time causing impact to any activities taking place on the Ethereum blockchain. You acknowledge these risks and represent that MetaMask cannot be held liable for such fluctuations or increased costs.
+
+### 5.6 Application Security ###
+
+You acknowledge that Ethereum applications are code subject to flaws and acknowledge that you are solely responsible for evaluating any code provided by the Services or Content and the trustworthiness of any third-party websites, products, smart-contracts, or Content you access or use through the Service. You further expressly acknowledge and represent that Ethereum applications can be written maliciously or negligently, that MetaMask cannot be held liable for your interaction with such applications and that such applications may cause the loss of property or even identity. This warning and others later provided by MetaMask in no way evidence or represent an on-going duty to alert you to all of the potential risks of utilizing the Service or Content.
+
+## 6. Indemnity ##
+
+You agree to release and to indemnify, defend and hold harmless MetaMask and its parents, subsidiaries, affiliates and agencies, as well as the officers, directors, employees, shareholders and representatives of any of the foregoing entities, from and against any and all losses, liabilities, expenses, damages, costs (including attorneys’ fees and court costs) claims or actions of any kind whatsoever arising or resulting from your use of the Service, your violation of these Terms of Use, and any of your acts or omissions that implicate publicity rights, defamation or invasion of privacy. MetaMask reserves the right, at its own expense, to assume exclusive defense and control of any matter otherwise subject to indemnification by you and, in such case, you agree to cooperate with MetaMask in the defense of such matter.
+
+## 7. Limitation on liability ##
+
+YOU ACKNOWLEDGE AND AGREE THAT YOU ASSUME FULL RESPONSIBILITY FOR YOUR USE OF THE SITE AND SERVICE. YOU ACKNOWLEDGE AND AGREE THAT ANY INFORMATION YOU SEND OR RECEIVE DURING YOUR USE OF THE SITE AND SERVICE MAY NOT BE SECURE AND MAY BE INTERCEPTED OR LATER ACQUIRED BY UNAUTHORIZED PARTIES. YOU ACKNOWLEDGE AND AGREE THAT YOUR USE OF THE SITE AND SERVICE IS AT YOUR OWN RISK. RECOGNIZING SUCH, YOU UNDERSTAND AND AGREE THAT, TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, NEITHER METAMASK NOR ITS SUPPLIERS OR LICENSORS WILL BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY OR OTHER DAMAGES OF ANY KIND, INCLUDING WITHOUT LIMITATION DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER TANGIBLE OR INTANGIBLE LOSSES OR ANY OTHER DAMAGES BASED ON CONTRACT, TORT, STRICT LIABILITY OR ANY OTHER THEORY (EVEN IF METAMASK HAD BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES), RESULTING FROM THE SITE OR SERVICE; THE USE OR THE INABILITY TO USE THE SITE OR SERVICE; UNAUTHORIZED ACCESS TO OR ALTERATION OF YOUR TRANSMISSIONS OR DATA; STATEMENTS OR CONDUCT OF ANY THIRD PARTY ON THE SITE OR SERVICE; ANY ACTIONS WE TAKE OR FAIL TO TAKE AS A RESULT OF COMMUNICATIONS YOU SEND TO US; HUMAN ERRORS; TECHNICAL MALFUNCTIONS; FAILURES, INCLUDING PUBLIC UTILITY OR TELEPHONE OUTAGES; OMISSIONS, INTERRUPTIONS, LATENCY, DELETIONS OR DEFECTS OF ANY DEVICE OR NETWORK, PROVIDERS, OR SOFTWARE (INCLUDING, BUT NOT LIMITED TO, THOSE THAT DO NOT PERMIT PARTICIPATION IN THE SERVICE); ANY INJURY OR DAMAGE TO COMPUTER EQUIPMENT; INABILITY TO FULLY ACCESS THE SITE OR SERVICE OR ANY OTHER WEBSITE; THEFT, TAMPERING, DESTRUCTION, OR UNAUTHORIZED ACCESS TO, IMAGES OR OTHER CONTENT OF ANY KIND; DATA THAT IS PROCESSED LATE OR INCORRECTLY OR IS INCOMPLETE OR LOST; TYPOGRAPHICAL, PRINTING OR OTHER ERRORS, OR ANY COMBINATION THEREOF; OR ANY OTHER MATTER RELATING TO THE SITE OR SERVICE.
+
+SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.
+
+## 8. Our Proprietary Rights ##
+
+All title, ownership and intellectual property rights in and to the Service are owned by MetaMask or its licensors. You acknowledge and agree that the Service contains proprietary and confidential information that is protected by applicable intellectual property and other laws. Except as expressly authorized by MetaMask, you agree not to copy, modify, rent, lease, loan, sell, distribute, perform, display or create derivative works based on the Service, in whole or in part. MetaMask issues a license for MetaMask, found [here](https://github.com/MetaMask/metamask-plugin/blob/master/LICENSE). For information on other licenses utilized in the development of MetaMask, please see our attribution page at: [https://metamask.io/attributions.html](https://metamask.io/attributions.html)
+
+## 9. Links ##
+
+The Service provides, or third parties may provide, links to other World Wide Web or accessible sites, applications or resources. Because MetaMask has no control over such sites, applications and resources, you acknowledge and agree that MetaMask is not responsible for the availability of such external sites, applications or resources, and does not endorse and is not responsible or liable for any content, advertising, products or other materials on or available from such sites or resources. You further acknowledge and agree that MetaMask shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such site or resource.
+
+## 10. Termination and Suspension ##
+
+MetaMask may terminate or suspend all or part of the Service and your MetaMask access immediately, without prior notice or liability, if you breach any of the terms or conditions of the Terms. Upon termination of your access, your right to use the Service will immediately cease.
+
+The following provisions of the Terms survive any termination of these Terms: INDEMNITY; WARRANTY DISCLAIMERS; LIMITATION ON LIABILITY; OUR PROPRIETARY RIGHTS; LINKS; TERMINATION; NO THIRD PARTY BENEFICIARIES; BINDING ARBITRATION AND CLASS ACTION WAIVER; GENERAL INFORMATION.
+
+## 11. No Third Party Beneficiaries ##
+
+You agree that, except as otherwise expressly provided in these Terms, there shall be no third party beneficiaries to the Terms.
+
+## 12. Notice and Procedure For Making Claims of Copyright Infringement ##
+
+If you believe that your copyright or the copyright of a person on whose behalf you are authorized to act has been infringed, please provide MetaMask’s Copyright Agent a written Notice containing the following information:
+
+· an electronic or physical signature of the person authorized to act on behalf of the owner of the copyright or other intellectual property interest;
+
+· a description of the copyrighted work or other intellectual property that you claim has been infringed;
+
+· a description of where the material that you claim is infringing is located on the Service;
+
+· your address, telephone number, and email address;
+
+· a statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agent, or the law;
+
+· a statement by you, made under penalty of perjury, that the above information in your Notice is accurate and that you are the copyright or intellectual property owner or authorized to act on the copyright or intellectual property owner's behalf.
+
+MetaMask’s Copyright Agent can be reached at:
+
+Email: copyright [at] metamask [dot] io
+
+Mail:
+
+Attention:
+
+MetaMask Copyright ℅ ConsenSys
+
+49 Bogart Street
+
+Brooklyn, NY 11206
+
+## 13. Binding Arbitration and Class Action Waiver ##
+
+PLEASE READ THIS SECTION CAREFULLY – IT MAY SIGNIFICANTLY AFFECT YOUR LEGAL RIGHTS, INCLUDING YOUR RIGHT TO FILE A LAWSUIT IN COURT
+
+### 13.1 Initial Dispute Resolution ###
+
+The parties shall use their best efforts to engage directly to settle any dispute, claim, question, or disagreement and engage in good faith negotiations which shall be a condition to either party initiating a lawsuit or arbitration.
+
+### 13.2 Binding Arbitration ###
+
+If the parties do not reach an agreed upon solution within a period of 30 days from the time informal dispute resolution under the Initial Dispute Resolution provision begins, then either party may initiate binding arbitration as the sole means to resolve claims, subject to the terms set forth below. Specifically, all claims arising out of or relating to these Terms (including their formation, performance and breach), the parties’ relationship with each other and/or your use of the Service shall be finally settled by binding arbitration administered by the American Arbitration Association in accordance with the provisions of its Commercial Arbitration Rules and the supplementary procedures for consumer related disputes of the American Arbitration Association (the "AAA"), excluding any rules or procedures governing or permitting class actions.
+
+The arbitrator, and not any federal, state or local court or agency, shall have exclusive authority to resolve all disputes arising out of or relating to the interpretation, applicability, enforceability or formation of these Terms, including, but not limited to any claim that all or any part of these Terms are void or voidable, or whether a claim is subject to arbitration. The arbitrator shall be empowered to grant whatever relief would be available in a court under law or in equity. The arbitrator’s award shall be written, and binding on the parties and may be entered as a judgment in any court of competent jurisdiction.
+
+The parties understand that, absent this mandatory provision, they would have the right to sue in court and have a jury trial. They further understand that, in some instances, the costs of arbitration could exceed the costs of litigation and the right to discovery may be more limited in arbitration than in court.
+
+### 13.3 Location ###
+
+Binding arbitration shall take place in New York. You agree to submit to the personal jurisdiction of any federal or state court in New York County, New York, in order to compel arbitration, to stay proceedings pending arbitration, or to confirm, modify, vacate or enter judgment on the award entered by the arbitrator.
+
+### 13.4 Class Action Waiver ###
+
+The parties further agree that any arbitration shall be conducted in their individual capacities only and not as a class action or other representative action, and the parties expressly waive their right to file a class action or seek relief on a class basis. YOU AND METAMASK AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. If any court or arbitrator determines that the class action waiver set forth in this paragraph is void or unenforceable for any reason or that an arbitration can proceed on a class basis, then the arbitration provision set forth above shall be deemed null and void in its entirety and the parties shall be deemed to have not agreed to arbitrate disputes.
+
+### 13.5 Exception - Litigation of Intellectual Property and Small Claims Court Claims ###
+
+Notwithstanding the parties' decision to resolve all disputes through arbitration, either party may bring an action in state or federal court to protect its intellectual property rights ("intellectual property rights" means patents, copyrights, moral rights, trademarks, and trade secrets, but not privacy or publicity rights). Either party may also seek relief in a small claims court for disputes or claims within the scope of that court’s jurisdiction.
+
+### 13.6 30-Day Right to Opt Out ###
+
+You have the right to opt-out and not be bound by the arbitration and class action waiver provisions set forth above by sending written notice of your decision to opt-out to the following address: MetaMask ℅ ConsenSys, 49 Bogart Street, Brooklyn NY 11206 and via email at legal-opt@metamask.io. The notice must be sent within 30 days of September 6, 2016 or your first use of the Service, whichever is later, otherwise you shall be bound to arbitrate disputes in accordance with the terms of those paragraphs. If you opt-out of these arbitration provisions, MetaMask also will not be bound by them.
+
+### 13.7 Changes to This Section ###
+
+MetaMask will provide 60-days’ notice of any changes to this section. Changes will become effective on the 60th day, and will apply prospectively only to any claims arising after the 60th day.
+
+For any dispute not subject to arbitration you and MetaMask agree to submit to the personal and exclusive jurisdiction of and venue in the federal and state courts located in New York, New York. You further agree to accept service of process by mail, and hereby waive any and all jurisdictional and venue defenses otherwise available.
+
+The Terms and the relationship between you and MetaMask shall be governed by the laws of the State of New York without regard to conflict of law provisions.
+
+## 14. General Information ##
+
+### 14.1 Entire Agreement ###
+
+These Terms (and any additional terms, rules and conditions of participation that MetaMask may post on the Service) constitute the entire agreement between you and MetaMask with respect to the Service and supersedes any prior agreements, oral or written, between you and MetaMask. In the event of a conflict between these Terms and the additional terms, rules and conditions of participation, the latter will prevail over the Terms to the extent of the conflict.
+
+### 14.2 Waiver and Severability of Terms ###
+
+The failure of MetaMask to exercise or enforce any right or provision of the Terms shall not constitute a waiver of such right or provision. If any provision of the Terms is found by an arbitrator or court of competent jurisdiction to be invalid, the parties nevertheless agree that the arbitrator or court should endeavor to give effect to the parties' intentions as reflected in the provision, and the other provisions of the Terms remain in full force and effect.
+
+### 14.3 Statute of Limitations ###
+
+You agree that regardless of any statute or law to the contrary, any claim or cause of action arising out of or related to the use of the Service or the Terms must be filed within one (1) year after such claim or cause of action arose or be forever barred.
+
+### 14.4 Section Titles ###
+
+The section titles in the Terms are for convenience only and have no legal or contractual effect.
+
+### 14.5 Communications ###
+
+Users with questions, complaints or claims with respect to the Service may contact us using the relevant contact information set forth above and at communications@metamask.io.
+
+## 15 Related Links ##
+
+**[Terms of Use](https://metamask.io/terms.html)**
+
+**[Privacy](https://metamask.io/privacy.html)**
+
+**[Attributions](https://metamask.io/attributions.html)**
+
diff --git a/notices/notice-delete.js b/notices/notice-delete.js
new file mode 100644
index 000000000..652f96159
--- /dev/null
+++ b/notices/notice-delete.js
@@ -0,0 +1,27 @@
+var fs = require('fs')
+var path = require('path')
+var prompt = require('prompt')
+var open = require('open')
+var extend = require('extend')
+var notices = require('./notices.json')
+
+
+console.log('List of Notices')
+console.log(`ID \t DATE \t\t\t TITLE`)
+notices.forEach((notice) => {
+ console.log(`${(' ' + notice.id).slice(-2)} \t ${notice.date} \t ${notice.title}`)
+})
+prompt.get(['id'], (error, res) => {
+prompt.start()
+ if (error) {
+ console.log("Exiting...")
+ process.exit()
+ }
+ var index = notices.findIndex((notice) => { return notice.id == res.id})
+ if (index === -1) {
+ console.log('Notice not found. Exiting...')
+ }
+ notices.splice(index, 1)
+ fs.unlink(`notices/archive/notice_${res.id}.md`)
+ fs.writeFile(`notices/notices.json`, JSON.stringify(notices))
+})
diff --git a/notices/notice-generator.js b/notices/notice-generator.js
new file mode 100644
index 000000000..7c41c5676
--- /dev/null
+++ b/notices/notice-generator.js
@@ -0,0 +1,33 @@
+var fsp = require('fs-promise')
+var path = require('path')
+var prompt = require('prompt')
+var open = require('open')
+var extend = require('extend')
+var notices = require('./notices.json')
+var id = Number(require('./notice-nonce.json'))
+
+var date = new Date().toDateString()
+
+var notice = {
+ read: false,
+ date: date,
+}
+
+fsp.writeFile(`notices/archive/notice_${id}.md`,'Message goes here. Please write out your notice and save before proceeding at the command line.')
+ .then(() => {
+ open(`notices/archive/notice_${id}.md`)
+ prompt.start()
+ prompt.get(['title'], (err, result) => {
+ notice.title = result.title
+ fsp.readFile(`notices/archive/notice_${id}.md`)
+ .then((body) => {
+ notice.body = body.toString()
+ notice.id = id
+ notices.push(notice)
+ return fsp.writeFile(`notices/notices.json`, JSON.stringify(notices))
+ }).then((completion) => {
+ id += 1
+ return fsp.writeFile(`notices/notice-nonce.json`, id)
+ })
+ })
+ })
diff --git a/notices/notice-nonce.json b/notices/notice-nonce.json
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/notices/notice-nonce.json
@@ -0,0 +1 @@
+1
diff --git a/notices/notices.json b/notices/notices.json
new file mode 100644
index 000000000..9f28b32a6
--- /dev/null
+++ b/notices/notices.json
@@ -0,0 +1 @@
+[{"read":false,"date":"Thu Feb 09 2017","title":"Terms of Use","body":"# Terms of Use #\n\n**THIS AGREEMENT IS SUBJECT TO BINDING ARBITRATION AND A WAIVER OF CLASS ACTION RIGHTS AS DETAILED IN SECTION 13. PLEASE READ THE AGREEMENT CAREFULLY.**\n\n_Our Terms of Use have been updated as of September 5, 2016_\n\n## 1. Acceptance of Terms ##\n\nMetaMask provides a platform for managing Ethereum (or \"ETH\") accounts, and allowing ordinary websites to interact with the Ethereum blockchain, while keeping the user in control over what transactions they approve, through our website located at[ ](http://metamask.io)[https://metamask.io/](https://metamask.io/) and browser plugin (the \"Site\") — which includes text, images, audio, code and other materials (collectively, the “Content”) and all of the features, and services provided. The Site, and any other features, tools, materials, or other services offered from time to time by MetaMask are referred to here as the “Service.” Please read these Terms of Use (the “Terms” or “Terms of Use”) carefully before using the Service. By using or otherwise accessing the Services, or clicking to accept or agree to these Terms where that option is made available, you (1) accept and agree to these Terms (2) consent to the collection, use, disclosure and other handling of information as described in our Privacy Policy and (3) any additional terms, rules and conditions of participation issued by MetaMask from time to time. If you do not agree to the Terms, then you may not access or use the Content or Services.\n\n## 2. Modification of Terms of Use ##\n\nExcept for Section 13, providing for binding arbitration and waiver of class action rights, MetaMask reserves the right, at its sole discretion, to modify or replace the Terms of Use at any time. The most current version of these Terms will be posted on our Site. You shall be responsible for reviewing and becoming familiar with any such modifications. Use of the Services by you after any modification to the Terms constitutes your acceptance of the Terms of Use as modified.\n\n\n\n## 3. Eligibility ##\n\nYou hereby represent and warrant that you are fully able and competent to enter into the terms, conditions, obligations, affirmations, representations and warranties set forth in these Terms and to abide by and comply with these Terms.\n\nMetaMask is a global platform and by accessing the Content or Services, you are representing and warranting that, you are of the legal age of majority in your jurisdiction as is required to access such Services and Content and enter into arrangements as provided by the Service. You further represent that you are otherwise legally permitted to use the service in your jurisdiction including owning cryptographic tokens of value, and interacting with the Services or Content in any way. You further represent you are responsible for ensuring compliance with the laws of your jurisdiction and acknowledge that MetaMask is not liable for your compliance with such laws.\n\n## 4 Account Password and Security ##\n\nWhen setting up an account within MetaMask, you will be responsible for keeping your own account secrets, which may be a twelve-word seed phrase, an account file, or other locally stored secret information. MetaMask encrypts this information locally with a password you provide, that we never send to our servers. You agree to (a) never use the same password for MetaMask that you have ever used outside of this service; (b) keep your secret information and password confidential and do not share them with anyone else; (c) immediately notify MetaMask of any unauthorized use of your account or breach of security. MetaMask cannot and will not be liable for any loss or damage arising from your failure to comply with this section.\n\n## 5. Representations, Warranties, and Risks ##\n\n### 5.1. Warranty Disclaimer ###\n\nYou expressly understand and agree that your use of the Service is at your sole risk. The Service (including the Service and the Content) are provided on an \"AS IS\" and \"as available\" basis, without warranties of any kind, either express or implied, including, without limitation, implied warranties of merchantability, fitness for a particular purpose or non-infringement. You acknowledge that MetaMask has no control over, and no duty to take any action regarding: which users gain access to or use the Service; what effects the Content may have on you; how you may interpret or use the Content; or what actions you may take as a result of having been exposed to the Content. You release MetaMask from all liability for you having acquired or not acquired Content through the Service. MetaMask makes no representations concerning any Content contained in or accessed through the Service, and MetaMask will not be responsible or liable for the accuracy, copyright compliance, legality or decency of material contained in or accessed through the Service.\n\n### 5.2 Sophistication and Risk of Cryptographic Systems ###\n\nBy utilizing the Service or interacting with the Content or platform in any way, you represent that you understand the inherent risks associated with cryptographic systems; and warrant that you have an understanding of the usage and intricacies of native cryptographic tokens, like Ether (ETH) and Bitcoin (BTC), smart contract based tokens such as those that follow the Ethereum Token Standard (https://github.com/ethereum/EIPs/issues/20), and blockchain-based software systems.\n\n### 5.3 Risk of Regulatory Actions in One or More Jurisdictions ###\n\nMetaMask and ETH could be impacted by one or more regulatory inquiries or regulatory action, which could impede or limit the ability of MetaMask to continue to develop, or which could impede or limit your ability to access or use the Service or Ethereum blockchain.\n\n### 5.4 Risk of Weaknesses or Exploits in the Field of Cryptography ###\n\nYou acknowledge and understand that Cryptography is a progressing field. Advances in code cracking or technical advances such as the development of quantum computers may present risks to cryptocurrencies and Services of Content, which could result in the theft or loss of your cryptographic tokens or property. To the extent possible, MetaMask intends to update the protocol underlying Services to account for any advances in cryptography and to incorporate additional security measures, but does not guarantee or otherwise represent full security of the system. By using the Service or accessing Content, you acknowledge these inherent risks.\n\n### 5.5 Volatility of Crypto Currencies ###\n\nYou understand that Ethereum and other blockchain technologies and associated currencies or tokens are highly volatile due to many factors including but not limited to adoption, speculation, technology and security risks. You also acknowledge that the cost of transacting on such technologies is variable and may increase at any time causing impact to any activities taking place on the Ethereum blockchain. You acknowledge these risks and represent that MetaMask cannot be held liable for such fluctuations or increased costs.\n\n### 5.6 Application Security ###\n\nYou acknowledge that Ethereum applications are code subject to flaws and acknowledge that you are solely responsible for evaluating any code provided by the Services or Content and the trustworthiness of any third-party websites, products, smart-contracts, or Content you access or use through the Service. You further expressly acknowledge and represent that Ethereum applications can be written maliciously or negligently, that MetaMask cannot be held liable for your interaction with such applications and that such applications may cause the loss of property or even identity. This warning and others later provided by MetaMask in no way evidence or represent an on-going duty to alert you to all of the potential risks of utilizing the Service or Content.\n\n## 6. Indemnity ##\n\nYou agree to release and to indemnify, defend and hold harmless MetaMask and its parents, subsidiaries, affiliates and agencies, as well as the officers, directors, employees, shareholders and representatives of any of the foregoing entities, from and against any and all losses, liabilities, expenses, damages, costs (including attorneys’ fees and court costs) claims or actions of any kind whatsoever arising or resulting from your use of the Service, your violation of these Terms of Use, and any of your acts or omissions that implicate publicity rights, defamation or invasion of privacy. MetaMask reserves the right, at its own expense, to assume exclusive defense and control of any matter otherwise subject to indemnification by you and, in such case, you agree to cooperate with MetaMask in the defense of such matter.\n\n## 7. Limitation on liability ##\n\nYOU ACKNOWLEDGE AND AGREE THAT YOU ASSUME FULL RESPONSIBILITY FOR YOUR USE OF THE SITE AND SERVICE. YOU ACKNOWLEDGE AND AGREE THAT ANY INFORMATION YOU SEND OR RECEIVE DURING YOUR USE OF THE SITE AND SERVICE MAY NOT BE SECURE AND MAY BE INTERCEPTED OR LATER ACQUIRED BY UNAUTHORIZED PARTIES. YOU ACKNOWLEDGE AND AGREE THAT YOUR USE OF THE SITE AND SERVICE IS AT YOUR OWN RISK. RECOGNIZING SUCH, YOU UNDERSTAND AND AGREE THAT, TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, NEITHER METAMASK NOR ITS SUPPLIERS OR LICENSORS WILL BE LIABLE TO YOU FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY OR OTHER DAMAGES OF ANY KIND, INCLUDING WITHOUT LIMITATION DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER TANGIBLE OR INTANGIBLE LOSSES OR ANY OTHER DAMAGES BASED ON CONTRACT, TORT, STRICT LIABILITY OR ANY OTHER THEORY (EVEN IF METAMASK HAD BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES), RESULTING FROM THE SITE OR SERVICE; THE USE OR THE INABILITY TO USE THE SITE OR SERVICE; UNAUTHORIZED ACCESS TO OR ALTERATION OF YOUR TRANSMISSIONS OR DATA; STATEMENTS OR CONDUCT OF ANY THIRD PARTY ON THE SITE OR SERVICE; ANY ACTIONS WE TAKE OR FAIL TO TAKE AS A RESULT OF COMMUNICATIONS YOU SEND TO US; HUMAN ERRORS; TECHNICAL MALFUNCTIONS; FAILURES, INCLUDING PUBLIC UTILITY OR TELEPHONE OUTAGES; OMISSIONS, INTERRUPTIONS, LATENCY, DELETIONS OR DEFECTS OF ANY DEVICE OR NETWORK, PROVIDERS, OR SOFTWARE (INCLUDING, BUT NOT LIMITED TO, THOSE THAT DO NOT PERMIT PARTICIPATION IN THE SERVICE); ANY INJURY OR DAMAGE TO COMPUTER EQUIPMENT; INABILITY TO FULLY ACCESS THE SITE OR SERVICE OR ANY OTHER WEBSITE; THEFT, TAMPERING, DESTRUCTION, OR UNAUTHORIZED ACCESS TO, IMAGES OR OTHER CONTENT OF ANY KIND; DATA THAT IS PROCESSED LATE OR INCORRECTLY OR IS INCOMPLETE OR LOST; TYPOGRAPHICAL, PRINTING OR OTHER ERRORS, OR ANY COMBINATION THEREOF; OR ANY OTHER MATTER RELATING TO THE SITE OR SERVICE.\n\nSOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU.\n\n## 8. Our Proprietary Rights ##\n\nAll title, ownership and intellectual property rights in and to the Service are owned by MetaMask or its licensors. You acknowledge and agree that the Service contains proprietary and confidential information that is protected by applicable intellectual property and other laws. Except as expressly authorized by MetaMask, you agree not to copy, modify, rent, lease, loan, sell, distribute, perform, display or create derivative works based on the Service, in whole or in part. MetaMask issues a license for MetaMask, found [here](https://github.com/MetaMask/metamask-plugin/blob/master/LICENSE). For information on other licenses utilized in the development of MetaMask, please see our attribution page at: [https://metamask.io/attributions.html](https://metamask.io/attributions.html)\n\n## 9. Links ##\n\nThe Service provides, or third parties may provide, links to other World Wide Web or accessible sites, applications or resources. Because MetaMask has no control over such sites, applications and resources, you acknowledge and agree that MetaMask is not responsible for the availability of such external sites, applications or resources, and does not endorse and is not responsible or liable for any content, advertising, products or other materials on or available from such sites or resources. You further acknowledge and agree that MetaMask shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such site or resource.\n\n## 10. Termination and Suspension ##\n\nMetaMask may terminate or suspend all or part of the Service and your MetaMask access immediately, without prior notice or liability, if you breach any of the terms or conditions of the Terms. Upon termination of your access, your right to use the Service will immediately cease.\n\nThe following provisions of the Terms survive any termination of these Terms: INDEMNITY; WARRANTY DISCLAIMERS; LIMITATION ON LIABILITY; OUR PROPRIETARY RIGHTS; LINKS; TERMINATION; NO THIRD PARTY BENEFICIARIES; BINDING ARBITRATION AND CLASS ACTION WAIVER; GENERAL INFORMATION.\n\n## 11. No Third Party Beneficiaries ##\n\nYou agree that, except as otherwise expressly provided in these Terms, there shall be no third party beneficiaries to the Terms.\n\n## 12. Notice and Procedure For Making Claims of Copyright Infringement ##\n\nIf you believe that your copyright or the copyright of a person on whose behalf you are authorized to act has been infringed, please provide MetaMask’s Copyright Agent a written Notice containing the following information:\n\n· an electronic or physical signature of the person authorized to act on behalf of the owner of the copyright or other intellectual property interest;\n\n· a description of the copyrighted work or other intellectual property that you claim has been infringed;\n\n· a description of where the material that you claim is infringing is located on the Service;\n\n· your address, telephone number, and email address;\n\n· a statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agent, or the law;\n\n· a statement by you, made under penalty of perjury, that the above information in your Notice is accurate and that you are the copyright or intellectual property owner or authorized to act on the copyright or intellectual property owner's behalf.\n\nMetaMask’s Copyright Agent can be reached at:\n\nEmail: copyright [at] metamask [dot] io\n\nMail:\n\nAttention:\n\nMetaMask Copyright ℅ ConsenSys\n\n49 Bogart Street\n\nBrooklyn, NY 11206\n\n## 13. Binding Arbitration and Class Action Waiver ##\n\nPLEASE READ THIS SECTION CAREFULLY – IT MAY SIGNIFICANTLY AFFECT YOUR LEGAL RIGHTS, INCLUDING YOUR RIGHT TO FILE A LAWSUIT IN COURT\n\n### 13.1 Initial Dispute Resolution ###\n\nThe parties shall use their best efforts to engage directly to settle any dispute, claim, question, or disagreement and engage in good faith negotiations which shall be a condition to either party initiating a lawsuit or arbitration.\n\n### 13.2 Binding Arbitration ###\n\nIf the parties do not reach an agreed upon solution within a period of 30 days from the time informal dispute resolution under the Initial Dispute Resolution provision begins, then either party may initiate binding arbitration as the sole means to resolve claims, subject to the terms set forth below. Specifically, all claims arising out of or relating to these Terms (including their formation, performance and breach), the parties’ relationship with each other and/or your use of the Service shall be finally settled by binding arbitration administered by the American Arbitration Association in accordance with the provisions of its Commercial Arbitration Rules and the supplementary procedures for consumer related disputes of the American Arbitration Association (the \"AAA\"), excluding any rules or procedures governing or permitting class actions.\n\nThe arbitrator, and not any federal, state or local court or agency, shall have exclusive authority to resolve all disputes arising out of or relating to the interpretation, applicability, enforceability or formation of these Terms, including, but not limited to any claim that all or any part of these Terms are void or voidable, or whether a claim is subject to arbitration. The arbitrator shall be empowered to grant whatever relief would be available in a court under law or in equity. The arbitrator’s award shall be written, and binding on the parties and may be entered as a judgment in any court of competent jurisdiction.\n\nThe parties understand that, absent this mandatory provision, they would have the right to sue in court and have a jury trial. They further understand that, in some instances, the costs of arbitration could exceed the costs of litigation and the right to discovery may be more limited in arbitration than in court.\n\n### 13.3 Location ###\n\nBinding arbitration shall take place in New York. You agree to submit to the personal jurisdiction of any federal or state court in New York County, New York, in order to compel arbitration, to stay proceedings pending arbitration, or to confirm, modify, vacate or enter judgment on the award entered by the arbitrator.\n\n### 13.4 Class Action Waiver ###\n\nThe parties further agree that any arbitration shall be conducted in their individual capacities only and not as a class action or other representative action, and the parties expressly waive their right to file a class action or seek relief on a class basis. YOU AND METAMASK AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. If any court or arbitrator determines that the class action waiver set forth in this paragraph is void or unenforceable for any reason or that an arbitration can proceed on a class basis, then the arbitration provision set forth above shall be deemed null and void in its entirety and the parties shall be deemed to have not agreed to arbitrate disputes.\n\n### 13.5 Exception - Litigation of Intellectual Property and Small Claims Court Claims ###\n\nNotwithstanding the parties' decision to resolve all disputes through arbitration, either party may bring an action in state or federal court to protect its intellectual property rights (\"intellectual property rights\" means patents, copyrights, moral rights, trademarks, and trade secrets, but not privacy or publicity rights). Either party may also seek relief in a small claims court for disputes or claims within the scope of that court’s jurisdiction.\n\n### 13.6 30-Day Right to Opt Out ###\n\nYou have the right to opt-out and not be bound by the arbitration and class action waiver provisions set forth above by sending written notice of your decision to opt-out to the following address: MetaMask ℅ ConsenSys, 49 Bogart Street, Brooklyn NY 11206 and via email at legal-opt@metamask.io. The notice must be sent within 30 days of September 6, 2016 or your first use of the Service, whichever is later, otherwise you shall be bound to arbitrate disputes in accordance with the terms of those paragraphs. If you opt-out of these arbitration provisions, MetaMask also will not be bound by them.\n\n### 13.7 Changes to This Section ###\n\nMetaMask will provide 60-days’ notice of any changes to this section. Changes will become effective on the 60th day, and will apply prospectively only to any claims arising after the 60th day.\n\nFor any dispute not subject to arbitration you and MetaMask agree to submit to the personal and exclusive jurisdiction of and venue in the federal and state courts located in New York, New York. You further agree to accept service of process by mail, and hereby waive any and all jurisdictional and venue defenses otherwise available.\n\nThe Terms and the relationship between you and MetaMask shall be governed by the laws of the State of New York without regard to conflict of law provisions.\n\n## 14. General Information ##\n\n### 14.1 Entire Agreement ###\n\nThese Terms (and any additional terms, rules and conditions of participation that MetaMask may post on the Service) constitute the entire agreement between you and MetaMask with respect to the Service and supersedes any prior agreements, oral or written, between you and MetaMask. In the event of a conflict between these Terms and the additional terms, rules and conditions of participation, the latter will prevail over the Terms to the extent of the conflict.\n\n### 14.2 Waiver and Severability of Terms ###\n\nThe failure of MetaMask to exercise or enforce any right or provision of the Terms shall not constitute a waiver of such right or provision. If any provision of the Terms is found by an arbitrator or court of competent jurisdiction to be invalid, the parties nevertheless agree that the arbitrator or court should endeavor to give effect to the parties' intentions as reflected in the provision, and the other provisions of the Terms remain in full force and effect.\n\n### 14.3 Statute of Limitations ###\n\nYou agree that regardless of any statute or law to the contrary, any claim or cause of action arising out of or related to the use of the Service or the Terms must be filed within one (1) year after such claim or cause of action arose or be forever barred.\n\n### 14.4 Section Titles ###\n\nThe section titles in the Terms are for convenience only and have no legal or contractual effect.\n\n### 14.5 Communications ###\n\nUsers with questions, complaints or claims with respect to the Service may contact us using the relevant contact information set forth above and at communications@metamask.io.\n\n## 15 Related Links ##\n\n**[Terms of Use](https://metamask.io/terms.html)**\n\n**[Privacy](https://metamask.io/privacy.html)**\n\n**[Attributions](https://metamask.io/attributions.html)**\n\n","id":0}] \ No newline at end of file
diff --git a/package.json b/package.json
index fdf6c822e..11cb353ea 100644
--- a/package.json
+++ b/package.json
@@ -4,18 +4,24 @@
"public": false,
"private": true,
"scripts": {
- "start": "gulp dev",
+ "start": "npm run dev",
"lint": "gulp lint",
- "dev": "gulp dev",
- "dist": "gulp dist",
- "test": "npm run fastTest && npm run ci",
- "fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
- "watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
- "ui": "node development/genStates.js && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
+ "buildCiUnits": "node test/integration/index.js",
+ "dev": "gulp dev --debug",
+ "disc": "gulp disc --debug",
+ "dist": "gulp dist --disableLiveReload",
+ "test": "npm run lint && npm run fastTest && npm run ci",
+ "fastTest": "METAMASK_ENV=test mocha --require test/helper.js --recursive \"test/unit/**/*.js\"",
+ "watch": "mocha watch --recursive \"test/unit/**/*.js\"",
+ "genStates": "node development/genStates.js",
+ "ui": "npm run genStates && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
- "buildMock": "browserify ./mock-dev.js -o ./development/bundle.js",
+ "buildMock": "npm run genStates && browserify ./mock-dev.js -o ./development/bundle.js",
"testem": "npm run buildMock && testem",
- "ci": "npm run buildMock && testem ci -P 2"
+ "ci": "npm run buildMock && npm run buildCiUnits && testem ci -P 2",
+ "announce": "node development/announcer.js",
+ "generateNotice": "node notices/notice-generator.js",
+ "deleteNotice": "node notices/notice-delete.js"
},
"browserify": {
"transform": [
@@ -32,39 +38,59 @@
},
"dependencies": {
"async": "^1.5.2",
+ "async-q": "^0.3.1",
+ "bip39": "^2.2.0",
+ "browser-passworder": "^2.0.3",
"browserify-derequire": "^0.9.4",
"clone": "^1.0.2",
"copy-to-clipboard": "^2.0.0",
"debounce": "^1.0.0",
+ "deep-extend": "^0.4.1",
+ "denodeify": "^1.2.1",
+ "disc": "^1.3.2",
"dnode": "^1.2.2",
"end-of-stream": "^1.1.0",
"ensnare": "^1.0.0",
- "eth-bin-to-ops": "^1.0.0",
+ "eth-bin-to-ops": "^1.0.1",
"eth-lightwallet": "^2.3.3",
"eth-query": "^1.0.3",
- "eth-store": "^1.1.0",
"ethereumjs-tx": "^1.0.0",
- "ethereumjs-util": "^4.4.0",
+ "ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
+ "ethereumjs-wallet": "^0.6.0",
+ "express": "^4.14.0",
+ "extension-link-enabler": "^1.0.0",
+ "extensionizer": "^1.0.0",
"gulp-eslint": "^2.0.0",
"hat": "0.0.3",
"identicon.js": "^1.2.1",
+ "iframe": "^1.0.0",
+ "iframe-stream": "^1.0.2",
"inject-css": "^0.1.1",
- "jazzicon": "^1.1.3",
+ "jazzicon": "^1.2.0",
"menu-droppo": "^1.1.0",
- "metamask-logo": "^1.3.1",
+ "metamask-logo": "^2.1.2",
"mississippi": "^1.2.0",
+ "mkdirp": "^0.5.1",
"multiplex": "^6.7.0",
+ "obs-store": "^2.3.1",
"once": "^1.3.3",
+ "ping-pong-stream": "^1.0.0",
"pojo-migrator": "^2.1.0",
"polyfill-crypto.getrandomvalues": "^1.0.0",
"post-message-stream": "^1.0.0",
+ "promise-filter": "^1.1.0",
+ "promise-to-callback": "^1.0.0",
+ "pump": "^1.0.2",
"pumpify": "^1.3.4",
+ "qrcode-npm": "0.0.3",
"react": "^15.0.2",
"react-addons-css-transition-group": "^15.0.2",
"react-dom": "^15.0.2",
"react-hyperscript": "^2.2.2",
"react-markdown": "^2.3.0",
"react-redux": "^4.4.5",
+ "react-select": "^1.0.0-rc.2",
+ "react-simple-file-input": "^1.0.0",
"react-tooltip-component": "^0.3.0",
"readable-stream": "^2.1.2",
"redux": "^3.0.5",
@@ -72,34 +98,39 @@
"redux-thunk": "^1.0.2",
"request-promise": "^4.1.1",
"sandwich-expando": "^1.0.5",
+ "semaphore": "^1.0.5",
"textarea-caret": "^3.0.1",
"three.js": "^0.73.2",
"through2": "^2.0.1",
+ "valid-url": "^1.0.9",
"vreme": "^3.0.2",
- "web3": "^0.17.0-alpha",
- "web3-provider-engine": "^8.0.2",
+ "web3": "0.18.2",
+ "web3-provider-engine": "^8.5.0",
"web3-stream-provider": "^2.0.6",
"xtend": "^4.0.1"
},
"devDependencies": {
"babel-eslint": "^6.0.5",
- "babel-preset-es2015": "^6.6.0",
"babel-register": "^6.7.2",
"babelify": "^7.2.0",
"beefy": "^2.1.5",
"brfs": "^1.4.3",
"browserify": "^13.0.0",
"chai": "^3.5.0",
+ "clone": "^1.0.2",
"deep-freeze-strict": "^1.1.1",
"del": "^2.2.0",
+ "fs-promise": "^1.0.0",
"gulp": "github:gulpjs/gulp#4.0",
- "gulp-brfs": "^0.1.0",
+ "gulp-if": "^2.0.1",
"gulp-json-editor": "^2.2.1",
"gulp-livereload": "^3.8.1",
+ "gulp-replace": "^0.5.4",
"gulp-sourcemaps": "^1.6.0",
"gulp-util": "^3.0.7",
"gulp-watch": "^4.3.5",
"gulp-zip": "^3.2.0",
+ "isomorphic-fetch": "^2.2.1",
"jsdom": "^8.1.0",
"jsdom-global": "^1.7.0",
"jshint-stylish": "~0.1.5",
@@ -109,6 +140,8 @@
"mocha-jsdom": "^1.1.0",
"mocha-sinon": "^1.1.5",
"nock": "^8.0.0",
+ "open": "0.0.5",
+ "prompt": "^1.0.0",
"qs": "^6.2.0",
"qunit": "^0.9.1",
"sinon": "^1.17.3",
diff --git a/test/integration/helpers.js b/test/integration/helpers.js
index 95c36017a..eede103b4 100644
--- a/test/integration/helpers.js
+++ b/test/integration/helpers.js
@@ -1,7 +1,7 @@
-function wait() {
+function wait(time) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve()
- }, 500)
+ }, time * 3 || 1500)
})
}
diff --git a/test/integration/index.html b/test/integration/index.html
index 6de40b046..430814a8a 100644
--- a/test/integration/index.html
+++ b/test/integration/index.html
@@ -12,10 +12,10 @@
<script src="https://code.jquery.com/qunit/qunit-2.0.0.js"></script>
<script src="./jquery-3.1.0.min.js"></script>
<script src="helpers.js"></script>
- <script src="tests.js"></script>
+ <script src="bundle.js"></script>
<script src="/testem.js"></script>
- <iframe src="/development/index.html" height="500px" width="360px">
+ <iframe src="/development/test.html" height="800px" width="500px">
<p>Your browser does not support iframes</p>
</iframe>
</body>
diff --git a/test/integration/index.js b/test/integration/index.js
new file mode 100644
index 000000000..ff6d1baf8
--- /dev/null
+++ b/test/integration/index.js
@@ -0,0 +1,21 @@
+var fs = require('fs')
+var path = require('path')
+var browserify = require('browserify');
+var tests = fs.readdirSync(path.join(__dirname, 'lib'))
+var bundlePath = path.join(__dirname, 'bundle.js')
+
+var b = browserify();
+
+// Remove old bundle
+try {
+ fs.unlinkSync(bundlePath)
+} catch (e) {}
+
+var writeStream = fs.createWriteStream(bundlePath)
+
+tests.forEach(function(fileName) {
+ b.add(path.join(__dirname, 'lib', fileName))
+})
+
+b.bundle().pipe(writeStream);
+
diff --git a/test/integration/lib/first-time.js b/test/integration/lib/first-time.js
new file mode 100644
index 000000000..55a16ef38
--- /dev/null
+++ b/test/integration/lib/first-time.js
@@ -0,0 +1,103 @@
+const PASSWORD = 'password123'
+
+QUnit.module('first time usage')
+
+QUnit.test('render init screen', function (assert) {
+ var done = assert.async()
+ let app
+
+ wait().then(function() {
+ app = $('iframe').contents().find('#app-content .mock-app-root')
+
+ // Scroll through terms
+ var title = app.find('h1').text()
+ assert.equal(title, 'MetaMask', 'title screen')
+
+ // enter password
+ var pwBox = app.find('#password-box')[0]
+ var confBox = app.find('#password-box-confirm')[0]
+ pwBox.value = PASSWORD
+ confBox.value = PASSWORD
+
+ return wait()
+ }).then(function() {
+
+ // create vault
+ var createButton = app.find('button.primary')[0]
+ createButton.click()
+
+ return wait(1500)
+ }).then(function() {
+
+ var created = app.find('h3')[0]
+ assert.equal(created.textContent, 'Vault Created', 'Vault created screen')
+
+ // Agree button
+ var button = app.find('button')[0]
+ assert.ok(button, 'button present')
+ button.click()
+
+ return wait(1000)
+ }).then(function() {
+
+ var detail = app.find('.account-detail-section')[0]
+ assert.ok(detail, 'Account detail section loaded.')
+
+ var sandwich = app.find('.sandwich-expando')[0]
+ sandwich.click()
+
+ return wait()
+ }).then(function() {
+
+ var sandwich = app.find('.menu-droppo')[0]
+ var children = sandwich.children
+ var lock = children[children.length - 2]
+ assert.ok(lock, 'Lock menu item found')
+ lock.click()
+
+ return wait(1000)
+ }).then(function() {
+
+ var pwBox = app.find('#password-box')[0]
+ pwBox.value = PASSWORD
+
+ var createButton = app.find('button.primary')[0]
+ createButton.click()
+
+ return wait(1000)
+ }).then(function() {
+
+ var detail = app.find('.account-detail-section')[0]
+ assert.ok(detail, 'Account detail section loaded again.')
+
+ return wait()
+ }).then(function (){
+
+ var qrButton = app.find('.fa.fa-qrcode')[0]
+ qrButton.click()
+
+ return wait(1000)
+ }).then(function (){
+
+ var qrHeader = app.find('.qr-header')[0]
+ var qrContainer = app.find('#qr-container')[0]
+ assert.equal(qrHeader.textContent, 'Account 1', 'Should show account label.')
+ assert.ok(qrContainer, 'QR Container found')
+
+ return wait()
+ }).then(function (){
+
+ var networkMenu = app.find('.network-indicator')[0]
+ networkMenu.click()
+
+ return wait()
+ }).then(function (){
+
+ var networkMenu = app.find('.network-indicator')[0]
+ var children = networkMenu.children
+ children.length[3]
+ assert.ok(children, 'All network options present')
+
+ done()
+ })
+})
diff --git a/test/integration/lib/idStore-migrator-test.js b/test/integration/lib/idStore-migrator-test.js
new file mode 100644
index 000000000..f2a437a7c
--- /dev/null
+++ b/test/integration/lib/idStore-migrator-test.js
@@ -0,0 +1,92 @@
+const ObservableStore = require('obs-store')
+const ConfigManager = require('../../../app/scripts/lib/config-manager')
+const IdStoreMigrator = require('../../../app/scripts/lib/idStore-migrator')
+const SimpleKeyring = require('../../../app/scripts/keyrings/simple')
+const normalize = require('../../../app/scripts/lib/sig-util').normalize
+
+const oldStyleVault = require('../mocks/oldVault.json').data
+const badStyleVault = require('../mocks/badVault.json').data
+
+const PASSWORD = '12345678'
+const FIRST_ADDRESS = '0x4dd5d356c5A016A220bCD69e82e5AF680a430d00'.toLowerCase()
+const BAD_STYLE_FIRST_ADDRESS = '0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9'
+const SEED = 'fringe damage bounce extend tunnel afraid alert sound all soldier all dinner'
+
+QUnit.module('Old Style Vaults', {
+ beforeEach: function () {
+ let managers = managersFromInitState(oldStyleVault)
+
+ this.configManager = managers.configManager
+ this.migrator = managers.migrator
+ }
+})
+
+QUnit.test('migrator:isInitialized', function (assert) {
+ assert.ok(this.migrator)
+})
+
+QUnit.test('migrator:migratedVaultForPassword', function (assert) {
+ var done = assert.async()
+
+ this.migrator.migratedVaultForPassword(PASSWORD)
+ .then((result) => {
+ assert.ok(result, 'migratedVaultForPassword returned result')
+ const { serialized, lostAccounts } = result
+ assert.equal(serialized.data.mnemonic, SEED, 'seed phrase recovered')
+ assert.equal(lostAccounts.length, 0, 'no lost accounts')
+ done()
+ })
+})
+
+QUnit.module('Old Style Vaults with bad HD seed', {
+ beforeEach: function () {
+ let managers = managersFromInitState(badStyleVault)
+
+ this.configManager = managers.configManager
+ this.migrator = managers.migrator
+ }
+})
+
+QUnit.test('migrator:migratedVaultForPassword', function (assert) {
+ var done = assert.async()
+
+ this.migrator.migratedVaultForPassword(PASSWORD)
+ .then((result) => {
+ assert.ok(result, 'migratedVaultForPassword returned result')
+ const { serialized, lostAccounts } = result
+
+ assert.equal(lostAccounts.length, 1, 'one lost account')
+ assert.equal(lostAccounts[0].address, '0xe15D894BeCB0354c501AE69429B05143679F39e0'.toLowerCase())
+ assert.ok(lostAccounts[0].privateKey, 'private key exported')
+
+ var lostAccount = lostAccounts[0]
+ var privateKey = lostAccount.privateKey
+
+ var simple = new SimpleKeyring()
+ simple.deserialize([privateKey])
+ .then(() => {
+ return simple.getAccounts()
+ })
+ .then((accounts) => {
+ assert.equal(normalize(accounts[0]), lostAccount.address, 'recovered address.')
+ done()
+ })
+ .catch((reason) => {
+ assert.ifError(reason)
+ done(reason)
+ })
+ })
+})
+
+function managersFromInitState(initState){
+
+ let configManager = new ConfigManager({
+ store: new ObservableStore(initState),
+ })
+
+ let migrator = new IdStoreMigrator({
+ configManager: configManager,
+ })
+
+ return { configManager, migrator }
+} \ No newline at end of file
diff --git a/test/integration/mocks/badVault.json b/test/integration/mocks/badVault.json
new file mode 100644
index 000000000..83b4f6298
--- /dev/null
+++ b/test/integration/mocks/badVault.json
@@ -0,0 +1 @@
+{"meta":{"version":4},"data":{"fiatCurrency":"USD","conversionRate":8.34908448,"conversionDate":1481227505,"wallet":"{\"encSeed\":{\"encStr\":\"Te2KyAGY3S01bgUJ+7d4y3BOvr/8TKrXrkRZ29cGI6dgyedtN+YgTQxElC2td/pzuoXm7KeSfr+yAoFCvMgqFAJwRcX3arHOsMFQie8kp8mL5I65zwdg/HB2QecB4OJHytrxgApv2zZiKEo0kbu2cs8zYIn5wNlCBIHwgylYmHpUDIJcO1B4zg==\",\"nonce\":\"xnxqk4iy70bjt721F+KPLV4PNfBFNyct\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"vNrSjekRKLmaGFf77Uca9+aAebmDlvrBwtAV8YthpQ4OX/mXtLSycmnLsYdk4schaByfJvrm6/Mf9fxzOSaScJk+XvKw5XqNXedkDHtbWrmNnxFpuT+9tuB8Nupr3D9GZK9PgXhJD99/7Bn6Wk7/ne+PIDmbtdmx/SWmrdo3pg==\",\"nonce\":\"zqWq/gtJ5zfUVRWQQJkP/zoYjer6Rozj\"},\"hdIndex\":1,\"encPrivKeys\":{\"e15d894becb0354c501ae69429b05143679f39e0\":{\"key\":\"jBLQ9v1l5LOEY1C3kI8z7LpbJKHP1vpVfPAlz90MNSfa8Oe+XlxKQAGYs8Zb4fWm\",\"nonce\":\"fJyrSRo1t0RMNqp2MsneoJnYJWHQnSVY\"}},\"addresses\":[\"e15d894becb0354c501ae69429b05143679f39e0\"]}},\"encHdRootPriv\":{\"encStr\":\"mbvwiFBQGbjj4BJLmdeYzfYi8jb7gtFtwiCQOPfvmyz4h2/KMbHNGzumM16qRKpifioQXkhnBulMIQHaYg0Jwv1MoFsqHxHmuIAT+QP5XvJjz0MRl6708pHowmIVG+R8CZNTLqzE7XS8YkZ4ElRpTvLEM8Wngi5Sg287mQMP9w==\",\"nonce\":\"i5Tp2lQe92rXQzNhjZcu9fNNhfux6Wf4\"},\"salt\":\"FQpA8D9R/5qSp9WtQ94FILyfWZHMI6YZw6RmBYqK0N0=\",\"version\":2}","config":{"provider":{"type":"testnet"},"selectedAccount":"0xe15d894becb0354c501ae69429b05143679f39e0"},"isEthConfirmed":true,"transactions":[],"gasMultiplier":1}}
diff --git a/test/integration/mocks/badVault2.json b/test/integration/mocks/badVault2.json
new file mode 100644
index 000000000..e849ca62a
--- /dev/null
+++ b/test/integration/mocks/badVault2.json
@@ -0,0 +1 @@
+{"meta":{"version":4},"data":{"fiatCurrency":"USD","noticesList":[{"read":true,"date":"Fri Dec 16 2016","title":"Ending Morden Support","body":"Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.\n\nUsers will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).\n\nPlease use the new Ropsten Network as your new default test network.\n\nYou can fund your Ropsten account using the buy button on your account page.\n\nBest wishes!\nThe MetaMask Team\n\n","id":0}],"conversionRate":7.07341909,"conversionDate":1482539284,"wallet":"{\"encSeed\":{\"encStr\":\"LZsdN8lJzYkUe1UpmAalnERdgkBFt25gWDdK8kfQUwMAk/27XR+dc+8n5swgoF5qgwhc9LBgliEGNDs1Q/lnuld3aQLabkOeAW4BHS1vS7FxqKrzDS3iyzSuQO6wDQmGno/buuknVgDsKiyjW22hpt7vtVVWA+ZL1P3x6M0+AxGJjeGVrG+E8Q==\",\"nonce\":\"T6O9BmwmTj214XUK3KF0s3iCKo3OlrUD\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"GNNfZevCMlgMVh9y21y1UwrC9qcmH6XYq7v+9UoqbHnzPQJFlxidN5+x/Sldo72a6+5zJpQkkdZ+Q0lePrzvXfuSd3D/RO7WKFIKo9nAQI5+JWwz4INuCmVcmqCv2J4BTLGjrG8fp5pDJ62Bn0XHqkJo3gx3fpvs3cS66+ZKwg==\",\"nonce\":\"HRTlGj44khQs2veYHEF/GqTI1t0yYvyd\"},\"hdIndex\":3,\"encPrivKeys\":{\"e15d894becb0354c501ae69429b05143679f39e0\":{\"key\":\"ZAeZL9VcRUtiiO4VXOQKBFg787PR5R3iymjUeU5vpDRIqOXbjWN6N4ZNR8YpSXl+\",\"nonce\":\"xLsADagS8uqDYae6cImyhxF7o1kBDbPe\"},\"87658c15aefe7448008a28513a11b6b130ef4cd0\":{\"key\":\"ku0mm5s1agRJNAMYIJO0qeoDe+FqcbqdQI6azXF3GL1OLo6uMlt6I4qS+eeravFi\",\"nonce\":\"xdGfSUPKtkW8ge0SWIbbpahs/NyEMzn5\"},\"aa25854c0379e53c957ac9382e720c577fa31fd5\":{\"key\":\"NjpYC9FbiC95CTx/1kwgOHk5LSN9vl4RULEBbvwfVOjqSH8WixNoP3R6I/QyNIs2\",\"nonce\":\"M/HWpXXA9QvuZxEykkGQPJKKdz33ovQr\"}},\"addresses\":[\"e15d894becb0354c501ae69429b05143679f39e0\",\"87658c15aefe7448008a28513a11b6b130ef4cd0\",\"aa25854c0379e53c957ac9382e720c577fa31fd5\"]}},\"encHdRootPriv\":{\"encStr\":\"f+3prUOzl+95aNAV+ad6lZdsYZz120ZsL67ucjj3tiMXf/CC4X8XB9N2QguhoMy6fW+fATUsTdJe8+CbAAyb79V9HY0Pitzq9Yw/g1g0/Ii2JzsdGBriuMsPdwZSVqz+rvQFw/6Qms1xjW6cqa8S7kM2WA5l8RB1Ck6r5zaqbA==\",\"nonce\":\"oGahxNFekVxH9sg6PUCCHIByvo4WFSqm\"},\"salt\":\"N7xYoEA53yhSweOsEphku1UKkIEuZtX2MwLBhVM6RR8=\",\"version\":2}","config":{"provider":{"type":"testnet"},"selectedAccount":"0xe15d894becb0354c501ae69429b05143679f39e0"},"walletNicknames":{"0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9":"Account 1","0xd7c0cd9e7d2701c710d64fc492c7086679bdf7b4":"Account 2","0x1acfb961c5a8268eac8e09d6241a26cbeff42241":"Account 3"},"lostAccounts":["0xe15d894becb0354c501ae69429b05143679f39e0","0x87658c15aefe7448008a28513a11b6b130ef4cd0","0xaa25854c0379e53c957ac9382e720c577fa31fd5"]}}
diff --git a/test/integration/mocks/oldVault.json b/test/integration/mocks/oldVault.json
new file mode 100644
index 000000000..dbcd91575
--- /dev/null
+++ b/test/integration/mocks/oldVault.json
@@ -0,0 +1,19 @@
+{
+ "meta": {
+ "version": 4
+ },
+ "data": {
+ "fiatCurrency": "USD",
+ "conversionRate": 9.47316629,
+ "conversionDate": 1479510994,
+ "wallet": "{\"encSeed\":{\"encStr\":\"a5tjKtDGlHkua+6Ta5s3wMFWPmsBqaPdMKGmqeI2z1kMbNs3V03HBaCptU7NtMra1DjHKbSNsUToxFUrmrvWBmUejamN16+l1CviwqASsv7kKzpot00/dfyyJgtZwwFP5Je+TAB1V231nRbPidOfeE1cDec5V8KTF8epl6qzsbA25pjeW76Dfw==\",\"nonce\":\"RzID6bAhWfGTSR74xdIh3RaT1+1sLk6F\"},\"ksData\":{\"m/44'/60'/0'/0\":{\"info\":{\"curve\":\"secp256k1\",\"purpose\":\"sign\"},\"encHdPathPriv\":{\"encStr\":\"6nlYAopRbmGcqerRZO08XwgeYaCJg9XRhh4oiYiVVdQtyNPdxvOI9TcE/mqvBiatMwBwA+TmsqTV6eZZe/VDZKYIGajKulQbScd0xQ71JhYfqqmzSG6EH2Pnzwa+aSAsfARgN1JJSaff2+p6wV6Zg5BUDtl72OGEIEfXhcUGwg==\",\"nonce\":\"Ee1KiDqtx7NvYToQUFvjEhKNinNQcXlK\"},\"hdIndex\":1,\"encPrivKeys\":{\"4dd5d356c5a016a220bcd69e82e5af680a430d00\":{\"key\":\"htGRGAH10lGF4M+fvioznmYVIUSWAzwp/yWSIo85psgZZwmCdJY72oyGanYsrFO8\",\"nonce\":\"PkP8XeZ+ok215rzEorvJu9nYTWzkOVr0\"}},\"addresses\":[\"4dd5d356c5a016a220bcd69e82e5af680a430d00\"]}},\"encHdRootPriv\":{\"encStr\":\"TAZAo71a+4IlAaoA66f0w4ts2f+V7ArTSUHRIrMltfAPXz7GfJBmKXNtHPORUYAjRiKqWK6FZnhKLf7Vcng2LG7VnDQwC4xPxzSRZzSEilnoY3V+zRY0HD7Wb/pndb4FliA/buZQmjohO4vezeX0hl70rJlPJEZTyYoWgxbxFA==\",\"nonce\":\"FlJOaLyBEHMaH5fEnYjdHc6nn18+WkRj\"},\"salt\":\"CmuCcWpbqpKUUv+1aE2ZwvQl7EIQ731uFibSq++vwtY=\",\"version\":2}",
+ "config": {
+ "provider": {
+ "type": "testnet"
+ },
+ "selectedAddress": "0x4dd5d356c5a016a220bcd69e82e5af680a430d00"
+ },
+ "showSeedWords": false,
+ "isEthConfirmed": true
+ }
+}
diff --git a/test/integration/tests.js b/test/integration/tests.js
deleted file mode 100644
index a3f3cd294..000000000
--- a/test/integration/tests.js
+++ /dev/null
@@ -1,21 +0,0 @@
-QUnit.test('agree to terms', function (assert) {
- var done = assert.async()
-
- // Select the mock app root
- var app = $('iframe').contents().find('#app-content .mock-app-root')
-
- // Agree to terms
- app.find('button').click()
-
- // Wait for view to transition:
- wait().then(function() {
-
- var title = app.find('h1').text()
- assert.equal(title, 'MetaMask', 'title screen')
-
- var buttons = app.find('button')
- assert.equal(buttons.length, 2, 'two buttons: create and restore')
-
- done()
- })
-})
diff --git a/test/lib/example-code.json b/test/lib/example-code.json
new file mode 100644
index 000000000..b76d37a4c
--- /dev/null
+++ b/test/lib/example-code.json
@@ -0,0 +1,3 @@
+{
+ "delegateCallCode": "0x606060405260e060020a60003504637bd703e8811461003157806390b98a111461005c578063f8b2cb4f1461008e575b005b6100b4600435600073f28c53067227848f8145355c455da5cfdd20e3136396e4ee3d6100da84610095565b6100c660043560243533600160a060020a03166000908152602081905260408120548290101561011f57506000610189565b6100b46004355b600160a060020a0381166000908152602081905260409020545b919050565b60408051918252519081900360200190f35b604080519115158252519081900360200190f35b60026040518360e060020a02815260040180838152602001828152602001925050506020604051808303818660325a03f4156100025750506040515191506100af9050565b33600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060015b9291505056"
+}
diff --git a/test/lib/migrations/004.json b/test/lib/migrations/004.json
new file mode 100644
index 000000000..0e2075c46
--- /dev/null
+++ b/test/lib/migrations/004.json
@@ -0,0 +1,138 @@
+{
+ "meta":{
+ "version":4
+ },
+ "data":{
+ "seedWords":null,
+ "fiatCurrency":"USD",
+ "isDisclaimerConfirmed":true,
+ "TOSHash":"a4f4e23f823a7ac51783e7ffba7914a911b09acdb97263296b7e14b527f80c5b",
+ "shapeShiftTxList":[
+ {
+ "depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw",
+ "depositType": "BTC",
+ "key": "shapeshift",
+ "time": 1471564825772,
+ "response": {
+ "status": "complete",
+ "outgoingCoin": "100.00",
+ "incomingCoin": "1.000",
+ "transaction": "0x3701e0ac344a12a1fc5417cf251109a7c41f3edab922310202630d9c012414c8"
+ }
+ },
+ {
+ "depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw",
+ "depositType": "BTC",
+ "key": "shapeshift",
+ "time": 1471566579224,
+ "response": {
+ "status": "no_deposits",
+ "depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw"
+ }
+ },
+ {
+ "depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw",
+ "depositType": "BTC",
+ "key": "shapeshift",
+ "time": 1471566565378,
+ "response": {
+ "status": "received",
+ "depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw"
+ }
+ }
+ ],
+ "noticesList":[
+ {
+ "read":true,
+ "date":"Fri Dec 16 2016",
+ "title":"Ending Morden Support",
+ "body":"Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.\n\nUsers will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).\n\nPlease use the new Ropsten Network as your new default test network.\n\nYou can fund your Ropsten account using the buy button on your account page.\n\nBest wishes!\nThe MetaMask Team\n\n",
+ "id":0
+ }
+ ],
+ "conversionRate":12.66441492,
+ "conversionDate":1487184182,
+ "vault":"{\"data\":\"Z5UFCeI/Tg9F9No0dC7eIhe4evCG91m6qeXhGpSeX48HHCQ/BepyNONKrh05YjB9hXCAd3Jy93judD+pcXNy7WS9zLujjmMI6sI90ToSrzThnMrOE6ixcH7HGS+TCcqvwBhZEsAQqUcQeHhT9CcdCQAxkKwBk8CK8W290MVeZoQVGK88hB2R8kL3mo/uayS5AnHPwWOS0rocgSfd/ioiucClpw==\",\"iv\":\"AYufeEPwp9f2Rdrfq7yS8g==\",\"salt\":\"g7BQIEx8tosH3IxWhPnrgZFu1XRkQn1Pp7l1ehTQQCo=\"}",
+ "config":{
+ "provider":{
+ "type":"testnet"
+ },
+ "selectedAccount":"0x0beb674745816b125fbc07285d39fd373e64895c"
+ },
+ "walletNicknames":{
+ "0x0beb674745816b125fbc07285d39fd373e64895c":"Account 1",
+ "0x433eb37d2e4895815b90f555425dfa123ddaed40":"Account 2"
+ },
+ "transactions":[
+ {
+ "id":3922064325443430,
+ "time":1487184358262,
+ "status":"confirmed",
+ "gasMultiplier":1,
+ "metamaskNetworkId":"3",
+ "txParams":{
+ "from":"0x0beb674745816b125fbc07285d39fd373e64895c",
+ "to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
+ "value":"0xde0b6b3a7640000",
+ "metamaskId":3922064325443430,
+ "metamaskNetworkId":"3",
+ "gas":"0x5209",
+ "gasPrice":"0x04a817c800",
+ "nonce":"0x0",
+ "gasLimit":"0x5209"
+ },
+ "gasLimitSpecified":false,
+ "estimatedGas":"0x5209",
+ "txFee":"17e0186e60800",
+ "txValue":"de0b6b3a7640000",
+ "maxCost":"de234b52e4a0800",
+ "hash":"0x0b36c5bb31528044e6a71e45a64e9872f5f365a14ac42ee1bea49e7766216c12"
+ },
+ {
+ "id":3922064325443431,
+ "time":1487184373172,
+ "status":"confirmed",
+ "gasMultiplier":1,
+ "metamaskNetworkId":"3",
+ "txParams":{
+ "from":"0x0beb674745816b125fbc07285d39fd373e64895c",
+ "to":"0x433eb37d2e4895815b90f555425dfa123ddaed40",
+ "value":"0xde0b6b3a7640000",
+ "metamaskId":3922064325443431,
+ "metamaskNetworkId":"3",
+ "gas":"0x5209",
+ "nonce":"0x01",
+ "gasPrice":"0x04a817c800",
+ "gasLimit":"0x5209"
+ },
+ "gasLimitSpecified":false,
+ "estimatedGas":"0x5209",
+ "txFee":"17e0186e60800",
+ "txValue":"de0b6b3a7640000",
+ "maxCost":"de234b52e4a0800",
+ "hash":"0x305548a8b8bb72de0ca8cf77df45e4fe2b29383e58c4da6b7eac7e9bd59e85e9"
+ },
+ {
+ "id":3922064325443432,
+ "time":1487184391226,
+ "status":"unapproved",
+ "gasMultiplier":1,
+ "metamaskNetworkId":"3",
+ "txParams":{
+ "from":"0x0beb674745816b125fbc07285d39fd373e64895c",
+ "to":"0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
+ "value":"0xde0b6b3a7640000",
+ "metamaskId":3922064325443432,
+ "metamaskNetworkId":"3",
+ "gas":"0x5209"
+ },
+ "gasLimitSpecified":false,
+ "estimatedGas":"0x5209",
+ "txFee":"17e0186e60800",
+ "txValue":"de0b6b3a7640000",
+ "maxCost":"de234b52e4a0800"
+ }
+ ],
+ "gasMultiplier":1
+ }
+}
diff --git a/test/lib/mock-config-manager.js b/test/lib/mock-config-manager.js
index fe841f455..72be86ed1 100644
--- a/test/lib/mock-config-manager.js
+++ b/test/lib/mock-config-manager.js
@@ -1,57 +1,10 @@
-var ConfigManager = require('../../app/scripts/lib/config-manager')
-const STORAGE_KEY = 'metamask-persistance-key'
-const extend = require('xtend')
+const ObservableStore = require('obs-store')
+const clone = require('clone')
+const ConfigManager = require('../../app/scripts/lib/config-manager')
+const firstTimeState = require('../../app/scripts/first-time-state')
+const STORAGE_KEY = 'metamask-config'
module.exports = function() {
- return new ConfigManager({ loadData, setData })
-}
-
-function loadData () {
- var oldData = getOldStyleData()
- var newData
- try {
- newData = JSON.parse(window.localStorage[STORAGE_KEY])
- } catch (e) {}
-
- var data = extend({
- meta: {
- version: 0,
- },
- data: {
- config: {
- provider: {
- type: 'testnet',
- },
- },
- },
- }, oldData || null, newData || null)
- return data
-}
-
-function getOldStyleData () {
- var config, wallet, seedWords
-
- var result = {
- meta: { version: 0 },
- data: {},
- }
-
- try {
- config = JSON.parse(window.localStorage['config'])
- result.data.config = config
- } catch (e) {}
- try {
- wallet = JSON.parse(window.localStorage['lightwallet'])
- result.data.wallet = wallet
- } catch (e) {}
- try {
- seedWords = window.localStorage['seedWords']
- result.data.seedWords = seedWords
- } catch (e) {}
-
- return result
-}
-
-function setData (data) {
- window.localStorage[STORAGE_KEY] = JSON.stringify(data)
-}
+ let store = new ObservableStore(clone(firstTimeState))
+ return new ConfigManager({ store })
+} \ No newline at end of file
diff --git a/test/lib/mock-encryptor.js b/test/lib/mock-encryptor.js
new file mode 100644
index 000000000..09bbf7ad5
--- /dev/null
+++ b/test/lib/mock-encryptor.js
@@ -0,0 +1,32 @@
+var mockHex = '0xabcdef0123456789'
+var mockKey = new Buffer(32)
+let cacheVal
+
+module.exports = {
+
+ encrypt(password, dataObj) {
+ cacheVal = dataObj
+ return Promise.resolve(mockHex)
+ },
+
+ decrypt(password, text) {
+ return Promise.resolve(cacheVal || {})
+ },
+
+ encryptWithKey(key, dataObj) {
+ return this.encrypt(key, dataObj)
+ },
+
+ decryptWithKey(key, text) {
+ return this.decrypt(key, text)
+ },
+
+ keyFromPassword(password) {
+ return Promise.resolve(mockKey)
+ },
+
+ generateSalt() {
+ return 'WHADDASALT!'
+ },
+
+}
diff --git a/test/lib/mock-simple-keychain.js b/test/lib/mock-simple-keychain.js
new file mode 100644
index 000000000..615b3e537
--- /dev/null
+++ b/test/lib/mock-simple-keychain.js
@@ -0,0 +1,38 @@
+var fakeWallet = {
+ privKey: '0x123456788890abcdef',
+ address: '0xfedcba0987654321',
+}
+const type = 'Simple Key Pair'
+
+module.exports = class MockSimpleKeychain {
+
+ static type() { return type }
+
+ constructor(opts) {
+ this.type = type
+ this.opts = opts || {}
+ this.wallets = []
+ }
+
+ serialize() {
+ return [ fakeWallet.privKey ]
+ }
+
+ deserialize(data) {
+ if (!Array.isArray(data)) {
+ throw new Error('Simple keychain deserialize requires a privKey array.')
+ }
+ this.wallets = [ fakeWallet ]
+ }
+
+ addAccounts(n = 1) {
+ for(var i = 0; i < n; i++) {
+ this.wallets.push(fakeWallet)
+ }
+ }
+
+ getAccounts() {
+ return this.wallets.map(w => w.address)
+ }
+
+}
diff --git a/test/unit/account-link-test.js b/test/unit/account-link-test.js
index 39889b6be..4ea12e002 100644
--- a/test/unit/account-link-test.js
+++ b/test/unit/account-link-test.js
@@ -3,9 +3,15 @@ var linkGen = require('../../ui/lib/account-link')
describe('account-link', function() {
- it('adds testnet prefix to morden test network', function() {
+ it('adds morden prefix to morden test network', function() {
var result = linkGen('account', '2')
- assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
+ assert.notEqual(result.indexOf('morden'), -1, 'testnet included')
+ assert.notEqual(result.indexOf('account'), -1, 'account included')
+ })
+
+ it('adds testnet prefix to ropsten test network', function() {
+ var result = linkGen('account', '3')
+ assert.notEqual(result.indexOf('testnet'), -1, 'testnet included')
assert.notEqual(result.indexOf('account'), -1, 'account included')
})
diff --git a/test/unit/actions/restore_vault_test.js b/test/unit/actions/restore_vault_test.js
deleted file mode 100644
index 609f5429e..000000000
--- a/test/unit/actions/restore_vault_test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-var jsdom = require('mocha-jsdom')
-var assert = require('assert')
-var freeze = require('deep-freeze-strict')
-var path = require('path')
-var sinon = require('sinon')
-
-var actions = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'actions.js'))
-var reducers = require(path.join(__dirname, '..', '..', '..', 'ui', 'app', 'reducers.js'))
-
-describe('#recoverFromSeed(password, seed)', function() {
-
- beforeEach(function() {
- // sinon allows stubbing methods that are easily verified
- this.sinon = sinon.sandbox.create()
- })
-
- afterEach(function() {
- // sinon requires cleanup otherwise it will overwrite context
- this.sinon.restore()
- })
-
- // stub out account manager
- actions._setAccountManager({
- recoverFromSeed(pw, seed, cb) {
- cb(null, {
- identities: {
- foo: 'bar'
- }
- })
- },
- })
-
- it('sets metamask.isUnlocked to true', function() {
- var initialState = {
- metamask: {
- isUnlocked: false,
- isInitialized: false,
- }
- }
- freeze(initialState)
-
- const restorePhrase = 'invite heavy among daring outdoor dice jelly coil stable note seat vicious'
- const password = 'foo'
- const dispatchFunc = actions.recoverFromSeed(password, restorePhrase)
-
- var dispatchStub = this.sinon.stub()
- dispatchStub.withArgs({ TYPE: actions.unlockMetamask() }).onCall(0)
- dispatchStub.withArgs({ TYPE: actions.showAccountsPage() }).onCall(1)
-
- var action
- var resultingState = initialState
- dispatchFunc((newAction) => {
- action = newAction
- resultingState = reducers(resultingState, action)
- })
-
- assert.equal(resultingState.metamask.isUnlocked, true, 'was unlocked')
- assert.equal(resultingState.metamask.isInitialized, true, 'was initialized')
- });
-});
diff --git a/test/unit/actions/set_selected_account_test.js b/test/unit/actions/set_selected_account_test.js
index 69eb11e47..2dc42d2ec 100644
--- a/test/unit/actions/set_selected_account_test.js
+++ b/test/unit/actions/set_selected_account_test.js
@@ -31,7 +31,7 @@ describe('SHOW_ACCOUNT_DETAIL', function() {
it('updates metamask state', function() {
var initialState = {
metamask: {
- selectedAccount: 'foo'
+ selectedAddress: 'foo'
}
}
freeze(initialState)
@@ -43,7 +43,6 @@ describe('SHOW_ACCOUNT_DETAIL', function() {
freeze(action)
var resultingState = reducers(initialState, action)
- assert.equal(resultingState.metamask.selectedAccount, action.value)
assert.equal(resultingState.metamask.selectedAddress, action.value)
})
})
diff --git a/test/unit/actions/tx_test.js b/test/unit/actions/tx_test.js
index c08a8aa26..7ded5b1ef 100644
--- a/test/unit/actions/tx_test.js
+++ b/test/unit/actions/tx_test.js
@@ -31,7 +31,7 @@ describe('tx confirmation screen', function() {
},
},
metamask: {
- unconfTxs: {
+ unapprovedTxs: {
'1457634084250832': {
id: 1457634084250832,
status: "unconfirmed",
@@ -46,7 +46,7 @@ describe('tx confirmation screen', function() {
describe('cancelTx', function() {
before(function(done) {
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb('An error!') },
cancelTransaction(txId) { /* noop */ },
clearSeedWordCache(cb) { cb() },
@@ -75,7 +75,7 @@ describe('tx confirmation screen', function() {
before(function(done) {
alert = () => {/* noop */}
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb({message: 'An error!'}) },
})
@@ -96,7 +96,7 @@ describe('tx confirmation screen', function() {
describe('when there is success', function() {
it('should complete tx and go home', function() {
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb() },
})
@@ -119,7 +119,7 @@ describe('tx confirmation screen', function() {
},
},
metamask: {
- unconfTxs: {
+ unapprovedTxs: {
'1457634084250832': {
id: 1457634084250832,
status: "unconfirmed",
@@ -135,7 +135,7 @@ describe('tx confirmation screen', function() {
}
freeze(initialState)
- actions._setAccountManager({
+ actions._setBackgroundConnection({
approveTransaction(txId, cb) { cb() },
})
@@ -162,7 +162,7 @@ describe('tx confirmation screen', function() {
});
function getUnconfirmedTxCount(state) {
- var txs = state.metamask.unconfTxs
+ var txs = state.metamask.unapprovedTxs
var count = Object.keys(txs).length
return count
}
diff --git a/test/unit/config-manager-test.js b/test/unit/config-manager-test.js
index 6aa7146f0..05324e741 100644
--- a/test/unit/config-manager-test.js
+++ b/test/unit/config-manager-test.js
@@ -1,136 +1,20 @@
+// polyfill fetch
+global.fetch = global.fetch || require('isomorphic-fetch')
+
const assert = require('assert')
const extend = require('xtend')
-const STORAGE_KEY = 'metamask-persistance-key'
-var configManagerGen = require('../lib/mock-config-manager')
-var configManager
const rp = require('request-promise')
const nock = require('nock')
+const configManagerGen = require('../lib/mock-config-manager')
describe('config-manager', function() {
+ var configManager
beforeEach(function() {
- window.localStorage = {} // Hacking localStorage support into JSDom
configManager = configManagerGen()
})
- describe('currency conversions', function() {
-
- describe('#getCurrentFiat', function() {
- it('should return false if no previous key exists', function() {
- var result = configManager.getCurrentFiat()
- assert.ok(!result)
- })
- })
-
- describe('#setCurrentFiat', function() {
- it('should make getCurrentFiat return true once set', function() {
- assert.equal(configManager.getCurrentFiat(), false)
- configManager.setCurrentFiat('USD')
- var result = configManager.getCurrentFiat()
- assert.equal(result, 'USD')
- })
-
- it('should work with other currencies as well', function() {
- assert.equal(configManager.getCurrentFiat(), false)
- configManager.setCurrentFiat('JPY')
- var result = configManager.getCurrentFiat()
- assert.equal(result, 'JPY')
- })
- })
-
- describe('#getConversionRate', function() {
- it('should return false if non-existent', function() {
- var result = configManager.getConversionRate()
- assert.ok(!result)
- })
- })
-
- describe('#updateConversionRate', function() {
- it('should retrieve an update for ETH to USD and set it in memory', function(done) {
- this.timeout(15000)
- var usdMock = nock('https://www.cryptonator.com')
- .get('/api/ticker/eth-USD')
- .reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
-
- assert.equal(configManager.getConversionRate(), false)
- var promise = new Promise(
- function (resolve, reject) {
- configManager.setCurrentFiat('USD')
- configManager.updateConversionRate().then(function() {
- resolve()
- })
- })
-
- promise.then(function() {
- var result = configManager.getConversionRate()
- assert.equal(typeof result, 'number')
- done()
- }).catch(function(err) {
- console.log(err)
- })
-
- })
-
- it('should work for JPY as well.', function() {
- this.timeout(15000)
- assert.equal(configManager.getConversionRate(), false)
-
- var jpyMock = nock('https://www.cryptonator.com')
- .get('/api/ticker/eth-JPY')
- .reply(200, '{"ticker":{"base":"ETH","target":"JPY","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
-
-
- var promise = new Promise(
- function (resolve, reject) {
- configManager.setCurrentFiat('JPY')
- configManager.updateConversionRate().then(function() {
- resolve()
- })
- })
-
- promise.then(function() {
- var result = configManager.getConversionRate()
- assert.equal(typeof result, 'number')
- }).catch(function(err) {
- console.log(err)
- })
- })
- })
- })
-
- describe('confirmation', function() {
-
- describe('#getConfirmed', function() {
- it('should return false if no previous key exists', function() {
- var result = configManager.getConfirmed()
- assert.ok(!result)
- })
- })
-
- describe('#setConfirmed', function() {
- it('should make getConfirmed return true once set', function() {
- assert.equal(configManager.getConfirmed(), false)
- configManager.setConfirmed(true)
- var result = configManager.getConfirmed()
- assert.equal(result, true)
- })
-
- it('should be able to set false', function() {
- configManager.setConfirmed(false)
- var result = configManager.getConfirmed()
- assert.equal(result, false)
- })
-
- it('should persist to local storage', function() {
- configManager.setConfirmed(true)
- var data = configManager.getData()
- assert.equal(data.isConfirmed, true)
- })
- })
- })
-
describe('#setConfig', function() {
- window.localStorage = {} // Hacking localStorage support into JSDom
it('should set the config key', function () {
var testConfig = {
@@ -153,7 +37,6 @@ describe('config-manager', function() {
rpcTarget: 'foobar'
},
}
- configManager.setConfirmed(true)
configManager.setConfig(testConfig)
var testWallet = {
@@ -164,7 +47,6 @@ describe('config-manager', function() {
var result = configManager.getData()
assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
- assert.equal(configManager.getConfirmed(), true)
testConfig.provider.type = 'something else!'
configManager.setConfig(testConfig)
@@ -173,7 +55,6 @@ describe('config-manager', function() {
assert.equal(result.wallet.name, testWallet.name, 'wallet name is set')
assert.equal(result.config.provider.rpcTarget, testConfig.provider.rpcTarget)
assert.equal(result.config.provider.type, testConfig.provider.type)
- assert.equal(configManager.getConfirmed(), true)
})
})
@@ -215,7 +96,7 @@ describe('config-manager', function() {
describe('transactions', function() {
beforeEach(function() {
- configManager._saveTxList([])
+ configManager.setTxList([])
})
describe('#getTxList', function() {
@@ -226,90 +107,13 @@ describe('config-manager', function() {
})
})
- describe('#_saveTxList', function() {
+ describe('#setTxList', function() {
it('saves the submitted data to the tx list', function() {
var target = [{ foo: 'bar' }]
- configManager._saveTxList(target)
+ configManager.setTxList(target)
var result = configManager.getTxList()
assert.equal(result[0].foo, 'bar')
})
})
-
- describe('#addTx', function() {
- it('adds a tx returned in getTxList', function() {
- var tx = { id: 1 }
- configManager.addTx(tx)
- var result = configManager.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].id, 1)
- })
-
- it('cuts off early txs beyond a limit', function() {
- const limit = configManager.txLimit
- for (let i = 0; i < limit + 1; i++) {
- let tx = { id: i }
- configManager.addTx(tx)
- }
- var result = configManager.getTxList()
- assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
- assert.equal(result[0].id, 1, 'early txs truncted')
- })
- })
-
- describe('#confirmTx', function() {
- it('sets the tx status to confirmed', function() {
- var tx = { id: 1, status: 'unconfirmed' }
- configManager.addTx(tx)
- configManager.confirmTx(1)
- var result = configManager.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].status, 'confirmed')
- })
- })
-
- describe('#rejectTx', function() {
- it('sets the tx status to rejected', function() {
- var tx = { id: 1, status: 'unconfirmed' }
- configManager.addTx(tx)
- configManager.rejectTx(1)
- var result = configManager.getTxList()
- assert.ok(Array.isArray(result))
- assert.equal(result.length, 1)
- assert.equal(result[0].status, 'rejected')
- })
- })
-
- describe('#updateTx', function() {
- it('replaces the tx with the same id', function() {
- configManager.addTx({ id: '1', status: 'unconfirmed' })
- configManager.addTx({ id: '2', status: 'confirmed' })
- configManager.updateTx({ id: '1', status: 'blah', hash: 'foo' })
- var result = configManager.getTx('1')
- assert.equal(result.hash, 'foo')
- })
- })
-
- describe('#unconfirmedTxs', function() {
- it('returns unconfirmed txs in a hash', function() {
- configManager.addTx({ id: '1', status: 'unconfirmed' })
- configManager.addTx({ id: '2', status: 'confirmed' })
- let result = configManager.unconfirmedTxs()
- assert.equal(typeof result, 'object')
- assert.equal(result['1'].status, 'unconfirmed')
- assert.equal(result['0'], undefined)
- assert.equal(result['2'], undefined)
- })
- })
-
- describe('#getTx', function() {
- it('returns a tx with the requested id', function() {
- configManager.addTx({ id: '1', status: 'unconfirmed' })
- configManager.addTx({ id: '2', status: 'confirmed' })
- assert.equal(configManager.getTx('1').status, 'unconfirmed')
- assert.equal(configManager.getTx('2').status, 'confirmed')
- })
- })
})
})
diff --git a/test/unit/currency-controller-test.js b/test/unit/currency-controller-test.js
new file mode 100644
index 000000000..c57b522c7
--- /dev/null
+++ b/test/unit/currency-controller-test.js
@@ -0,0 +1,87 @@
+// polyfill fetch
+global.fetch = global.fetch || require('isomorphic-fetch')
+
+const assert = require('assert')
+const extend = require('xtend')
+const rp = require('request-promise')
+const nock = require('nock')
+const CurrencyController = require('../../app/scripts/lib/controllers/currency')
+
+describe('config-manager', function() {
+ var currencyController
+
+ beforeEach(function() {
+ currencyController = new CurrencyController()
+ })
+
+ describe('currency conversions', function() {
+
+ describe('#setCurrentCurrency', function() {
+ it('should return USD as default', function() {
+ assert.equal(currencyController.getCurrentCurrency(), 'USD')
+ })
+
+ it('should be able to set to other currency', function() {
+ assert.equal(currencyController.getCurrentCurrency(), 'USD')
+ currencyController.setCurrentCurrency('JPY')
+ var result = currencyController.getCurrentCurrency()
+ assert.equal(result, 'JPY')
+ })
+ })
+
+ describe('#getConversionRate', function() {
+ it('should return undefined if non-existent', function() {
+ var result = currencyController.getConversionRate()
+ assert.ok(!result)
+ })
+ })
+
+ describe('#updateConversionRate', function() {
+ it('should retrieve an update for ETH to USD and set it in memory', function(done) {
+ this.timeout(15000)
+ var usdMock = nock('https://www.cryptonator.com')
+ .get('/api/ticker/eth-USD')
+ .reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
+
+ assert.equal(currencyController.getConversionRate(), 0)
+ currencyController.setCurrentCurrency('USD')
+ currencyController.updateConversionRate()
+ .then(function() {
+ var result = currencyController.getConversionRate()
+ console.log('currencyController.getConversionRate:', result)
+ assert.equal(typeof result, 'number')
+ done()
+ }).catch(function(err) {
+ done(err)
+ })
+
+ })
+
+ it('should work for JPY as well.', function() {
+ this.timeout(15000)
+ assert.equal(currencyController.getConversionRate(), 0)
+
+ var jpyMock = nock('https://www.cryptonator.com')
+ .get('/api/ticker/eth-JPY')
+ .reply(200, '{"ticker":{"base":"ETH","target":"JPY","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
+
+
+ var promise = new Promise(
+ function (resolve, reject) {
+ currencyController.setCurrentCurrency('JPY')
+ currencyController.updateConversionRate().then(function() {
+ resolve()
+ })
+ })
+
+ promise.then(function() {
+ var result = currencyController.getConversionRate()
+ assert.equal(typeof result, 'number')
+ }).catch(function(err) {
+ done(err)
+ })
+ })
+ })
+ })
+
+})
diff --git a/test/unit/explorer-link-test.js b/test/unit/explorer-link-test.js
index 961b400fd..8aa58bff9 100644
--- a/test/unit/explorer-link-test.js
+++ b/test/unit/explorer-link-test.js
@@ -4,7 +4,7 @@ var linkGen = require('../../ui/lib/explorer-link')
describe('explorer-link', function() {
it('adds testnet prefix to morden test network', function() {
- var result = linkGen('hash', '2')
+ var result = linkGen('hash', '3')
assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
})
diff --git a/test/unit/extension-test.js b/test/unit/extension-test.js
index 6b695e835..8f259f05c 100644
--- a/test/unit/extension-test.js
+++ b/test/unit/extension-test.js
@@ -1,19 +1,47 @@
var assert = require('assert')
var sinon = require('sinon')
const ethUtil = require('ethereumjs-util')
-GLOBAL.chrome = {}
-GLOBAL.browser = {}
+global.chrome = {}
+global.browser = {}
var path = require('path')
var Extension = require(path.join(__dirname, '..', '..', 'app', 'scripts', 'lib', 'extension-instance.js'))
describe('extension', function() {
+ describe('extension.getURL', function() {
+ const desiredResult = 'http://the-desired-result.io'
+
+ describe('in Chrome or Firefox', function() {
+ global.chrome.extension = {
+ getURL: () => desiredResult
+ }
+
+ it('returns the desired result', function() {
+ const extension = new Extension()
+ const result = extension.extension.getURL()
+ assert.equal(result, desiredResult)
+ })
+ })
+
+ describe('in Microsoft Edge', function() {
+ global.browser.extension = {
+ getURL: () => desiredResult
+ }
+
+ it('returns the desired result', function() {
+ const extension = new Extension()
+ const result = extension.extension.getURL()
+ assert.equal(result, desiredResult)
+ })
+ })
+ })
+
describe('with chrome global', function() {
let extension
beforeEach(function() {
- GLOBAL.chrome = {
+ global.chrome = {
alarms: 'foo'
}
extension = new Extension()
@@ -30,9 +58,9 @@ describe('extension', function() {
beforeEach(function() {
realWindow = window
- window = GLOBAL
- GLOBAL.chrome = undefined
- GLOBAL.alarms = 'foo'
+ window = global
+ global.chrome = undefined
+ global.alarms = 'foo'
extension = new Extension()
})
@@ -45,4 +73,5 @@ describe('extension', function() {
})
})
+
})
diff --git a/test/unit/idStore-migration-test.js b/test/unit/idStore-migration-test.js
new file mode 100644
index 000000000..81a99ef63
--- /dev/null
+++ b/test/unit/idStore-migration-test.js
@@ -0,0 +1,83 @@
+const async = require('async')
+const assert = require('assert')
+const ObservableStore = require('obs-store')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const ConfigManager = require('../../app/scripts/lib/config-manager')
+const firstTimeState = require('../../app/scripts/first-time-state')
+const delegateCallCode = require('../lib/example-code.json').delegateCallCode
+const clone = require('clone')
+
+// The old way:
+const IdentityStore = require('../../app/scripts/lib/idStore')
+const STORAGE_KEY = 'metamask-config'
+
+// The new ways:
+var KeyringController = require('../../app/scripts/keyring-controller')
+const mockEncryptor = require('../lib/mock-encryptor')
+const MockSimpleKeychain = require('../lib/mock-simple-keychain')
+const sinon = require('sinon')
+
+const mockVault = {
+ seed: 'picnic injury awful upper eagle junk alert toss flower renew silly vague',
+ account: '0x5d8de92c205279c10e5669f797b853ccef4f739a',
+}
+
+const badVault = {
+ seed: 'radar blur cabbage chef fix engine embark joy scheme fiction master release',
+}
+
+describe('IdentityStore to KeyringController migration', function() {
+
+ // The stars of the show:
+ let idStore, keyringController, seedWords, configManager
+
+ let password = 'password123'
+ let entropy = 'entripppppyy duuude'
+ let accounts = []
+ let newAccounts = []
+ let originalKeystore
+
+ // This is a lot of setup, I know!
+ // We have to create an old style vault, populate it,
+ // and THEN create a new one, before we can run tests on it.
+ beforeEach(function(done) {
+ this.sinon = sinon.sandbox.create()
+ let store = new ObservableStore(clone(firstTimeState))
+ configManager = new ConfigManager({ store })
+
+ idStore = new IdentityStore({
+ configManager: configManager,
+ ethStore: {
+ addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
+ del(acct) { delete accounts[acct] },
+ },
+ })
+
+ idStore._createVault(password, mockVault.seed, (err) => {
+ assert.ifError(err, 'createNewVault threw error')
+ originalKeystore = idStore._idmgmt.keyStore
+
+ idStore.setLocked((err) => {
+ assert.ifError(err, 'createNewVault threw error')
+ keyringController = new KeyringController({
+ configManager,
+ ethStore: {
+ addAccount(acct) { newAccounts.push(ethUtil.addHexPrefix(acct)) },
+ del(acct) { delete newAccounts[acct] },
+ },
+ txManager: {
+ getTxList: () => [],
+ getUnapprovedTxList: () => []
+ },
+ })
+
+ // Stub out the browser crypto for a mock encryptor.
+ // Browser crypto is tested in the integration test suite.
+ keyringController.encryptor = mockEncryptor
+ done()
+ })
+ })
+ })
+
+})
diff --git a/test/unit/idStore-test.js b/test/unit/idStore-test.js
index ee4613236..000c58a82 100644
--- a/test/unit/idStore-test.js
+++ b/test/unit/idStore-test.js
@@ -1,13 +1,16 @@
-var assert = require('assert')
-var IdentityStore = require('../../app/scripts/lib/idStore')
-var configManagerGen = require('../lib/mock-config-manager')
+const async = require('async')
+const assert = require('assert')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const configManagerGen = require('../lib/mock-config-manager')
+const delegateCallCode = require('../lib/example-code.json').delegateCallCode
+const IdentityStore = require('../../app/scripts/lib/idStore')
describe('IdentityStore', function() {
describe('#createNewVault', function () {
let idStore
let password = 'password123'
- let entropy = 'entripppppyy duuude'
let seedWords
let accounts = []
let originalKeystore
@@ -18,11 +21,12 @@ describe('IdentityStore', function() {
idStore = new IdentityStore({
configManager: configManagerGen(),
ethStore: {
- addAccount(acct) { accounts.push(acct) },
+ addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
},
})
- idStore.createNewVault(password, entropy, (err, seeds) => {
+ idStore.createNewVault(password, (err, seeds) => {
+ assert.ifError(err, 'createNewVault threw error')
seedWords = seeds
originalKeystore = idStore._idmgmt.keyStore
done()
@@ -38,7 +42,7 @@ describe('IdentityStore', function() {
idStore = new IdentityStore({
configManager: configManagerGen(),
ethStore: {
- addAccount(acct) { newAccounts.push(acct) },
+ addAccount(acct) { newAccounts.push(ethUtil.addHexPrefix(acct)) },
},
})
})
@@ -57,31 +61,80 @@ describe('IdentityStore', function() {
})
describe('#recoverFromSeed BIP44 compliance', function() {
- let seedWords = 'picnic injury awful upper eagle junk alert toss flower renew silly vague'
- let firstAccount = '0x5d8de92c205279c10e5669f797b853ccef4f739a'
+ const salt = 'lightwalletSalt'
let password = 'secret!'
- let accounts = []
+ let accounts = {}
let idStore
+ var assertions = [
+ {
+ seed: 'picnic injury awful upper eagle junk alert toss flower renew silly vague',
+ account: '0x5d8de92c205279c10e5669f797b853ccef4f739a',
+ },
+ {
+ seed: 'radar blur cabbage chef fix engine embark joy scheme fiction master release',
+ account: '0xe15d894becb0354c501ae69429b05143679f39e0',
+ },
+ {
+ seed: 'phone coyote caught pattern found table wedding list tumble broccoli chief swing',
+ account: '0xb0e868f24bc7fec2bce2efc2b1c344d7569cd9d2',
+ },
+ {
+ seed: 'recycle tag bird palace blue village anxiety census cook soldier example music',
+ account: '0xab34a45920afe4af212b96ec51232aaa6a33f663',
+ },
+ {
+ seed: 'half glimpse tape cute harvest sweet bike voyage actual floor poet lazy',
+ account: '0x28e9044597b625ac4beda7250011670223de43b2',
+ },
+ {
+ seed: 'flavor tiger carpet motor angry hungry document inquiry large critic usage liar',
+ account: '0xb571be96558940c4e9292e1999461aa7499fb6cd',
+ },
+ ]
+
before(function() {
window.localStorage = {} // Hacking localStorage support into JSDom
idStore = new IdentityStore({
configManager: configManagerGen(),
ethStore: {
- addAccount(acct) { accounts.push(acct) },
+ addAccount(acct) { accounts[acct] = acct},
+ del(acct) { delete accounts[acct] },
},
})
})
- it('should return the expected first account', function (done) {
+ it('should enforce seed compliance with TestRPC', function (done) {
+ this.timeout(10000)
+ const tests = assertions.map((assertion) => {
+ return function (cb) {
- idStore.recoverFromSeed(password, seedWords, (err) => {
- assert.ifError(err)
+ idStore.recoverFromSeed(password, assertion.seed, (err) => {
+ assert.ifError(err)
+
+ var expected = assertion.account.toLowerCase()
+ var received = accounts[expected].toLowerCase()
+ assert.equal(received, expected)
+
+ idStore.tryPassword(password, function (err) {
- let newKeystore = idStore._idmgmt.keyStore
- assert.equal(accounts[0], firstAccount)
+ assert.ok(idStore._isUnlocked(), 'should unlock the id store')
+
+ idStore.submitPassword(password, function(err, account) {
+ assert.ifError(err)
+ assert.equal(account, expected)
+ assert.equal(Object.keys(idStore._getAddresses()).length, 1, 'only one account on restore')
+ cb()
+ })
+ })
+ })
+ }
+ })
+
+ async.series(tests, function(err, results) {
+ assert.ifError(err)
done()
})
})
diff --git a/test/unit/keyring-controller-test.js b/test/unit/keyring-controller-test.js
new file mode 100644
index 000000000..aae4cdfd6
--- /dev/null
+++ b/test/unit/keyring-controller-test.js
@@ -0,0 +1,172 @@
+const assert = require('assert')
+const KeyringController = require('../../app/scripts/keyring-controller')
+const configManagerGen = require('../lib/mock-config-manager')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
+const async = require('async')
+const mockEncryptor = require('../lib/mock-encryptor')
+const MockSimpleKeychain = require('../lib/mock-simple-keychain')
+const sinon = require('sinon')
+
+describe('KeyringController', function() {
+
+ let keyringController, state
+ let password = 'password123'
+ let seedWords = 'puzzle seed penalty soldier say clay field arctic metal hen cage runway'
+ let addresses = ['eF35cA8EbB9669A35c31b5F6f249A9941a812AC1'.toLowerCase()]
+ let accounts = []
+ let originalKeystore
+
+ beforeEach(function(done) {
+ this.sinon = sinon.sandbox.create()
+ window.localStorage = {} // Hacking localStorage support into JSDom
+
+ keyringController = new KeyringController({
+ configManager: configManagerGen(),
+ txManager: {
+ getTxList: () => [],
+ getUnapprovedTxList: () => []
+ },
+ ethStore: {
+ addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
+ },
+ })
+
+ // Stub out the browser crypto for a mock encryptor.
+ // Browser crypto is tested in the integration test suite.
+ keyringController.encryptor = mockEncryptor
+
+ keyringController.createNewVaultAndKeychain(password)
+ .then(function (newState) {
+ state = newState
+ done()
+ })
+ .catch((err) => {
+ done(err)
+ })
+ })
+
+ afterEach(function() {
+ // Cleanup mocks
+ this.sinon.restore()
+ })
+
+ describe('#createNewVaultAndKeychain', function () {
+ this.timeout(10000)
+
+ it('should set a vault on the configManager', function(done) {
+ keyringController.store.updateState({ vault: null })
+ assert(!keyringController.store.getState().vault, 'no previous vault')
+ keyringController.createNewVaultAndKeychain(password)
+ .then(() => {
+ const vault = keyringController.store.getState().vault
+ assert(vault, 'vault created')
+ done()
+ })
+ .catch((reason) => {
+ done(reason)
+ })
+ })
+ })
+
+ describe('#restoreKeyring', function() {
+
+ it(`should pass a keyring's serialized data back to the correct type.`, function(done) {
+ const mockSerialized = {
+ type: 'HD Key Tree',
+ data: {
+ mnemonic: seedWords,
+ numberOfAccounts: 1,
+ }
+ }
+ const mock = this.sinon.mock(keyringController)
+
+ mock.expects('getBalanceAndNickname')
+ .exactly(1)
+
+ keyringController.restoreKeyring(mockSerialized)
+ .then((keyring) => {
+ assert.equal(keyring.wallets.length, 1, 'one wallet restored')
+ return keyring.getAccounts()
+ })
+ .then((accounts) => {
+ assert.equal(accounts[0], addresses[0])
+ mock.verify()
+ done()
+ })
+ .catch((reason) => {
+ done(reason)
+ })
+ })
+ })
+
+ describe('#createNickname', function() {
+ it('should add the address to the identities hash', function() {
+ const fakeAddress = '0x12345678'
+ keyringController.createNickname(fakeAddress)
+ const identities = keyringController.memStore.getState().identities
+ const identity = identities[fakeAddress]
+ assert.equal(identity.address, fakeAddress)
+ })
+ })
+
+ describe('#saveAccountLabel', function() {
+ it ('sets the nickname', function(done) {
+ const account = addresses[0]
+ var nick = 'Test nickname'
+ const identities = keyringController.memStore.getState().identities
+ identities[ethUtil.addHexPrefix(account)] = {}
+ keyringController.memStore.updateState({ identities })
+ keyringController.saveAccountLabel(account, nick)
+ .then((label) => {
+ try {
+ assert.equal(label, nick)
+ const persisted = keyringController.store.getState().walletNicknames[account]
+ assert.equal(persisted, nick)
+ done()
+ } catch (err) {
+ done()
+ }
+ })
+ .catch((reason) => {
+ done(reason)
+ })
+ })
+ })
+
+ describe('#getAccounts', function() {
+ it('returns the result of getAccounts for each keyring', function() {
+ keyringController.keyrings = [
+ { getAccounts() { return Promise.resolve([1,2,3]) } },
+ { getAccounts() { return Promise.resolve([4,5,6]) } },
+ ]
+
+ keyringController.getAccounts()
+ .then((result) => {
+ assert.deepEqual(result, [1,2,3,4,5,6])
+ done()
+ })
+ })
+ })
+
+ describe('#addGasBuffer', function() {
+ it('adds 100k gas buffer to estimates', function() {
+
+ const gas = '0x04ee59' // Actual estimated gas example
+ const tooBigOutput = '0x80674f9' // Actual bad output
+ const bnGas = new BN(ethUtil.stripHexPrefix(gas), 16)
+ const correctBuffer = new BN('100000', 10)
+ const correct = bnGas.add(correctBuffer)
+
+ const tooBig = new BN(tooBigOutput, 16)
+ const result = keyringController.addGasBuffer(gas)
+ const bnResult = new BN(ethUtil.stripHexPrefix(result), 16)
+
+ assert.equal(result.indexOf('0x'), 0, 'included hex prefix')
+ assert(bnResult.gt(bnGas), 'Estimate increased in value.')
+ assert.equal(bnResult.sub(bnGas).toString(10), '100000', 'added 100k gas')
+ assert.equal(result, '0x' + correct.toString(16), 'Added the right amount')
+ assert.notEqual(result, tooBigOutput, 'not that bad estimate')
+ })
+ })
+})
diff --git a/test/unit/keyrings/hd-test.js b/test/unit/keyrings/hd-test.js
new file mode 100644
index 000000000..dfc0ec908
--- /dev/null
+++ b/test/unit/keyrings/hd-test.js
@@ -0,0 +1,127 @@
+const assert = require('assert')
+const extend = require('xtend')
+const HdKeyring = require('../../../app/scripts/keyrings/hd')
+
+// Sample account:
+const privKeyHex = 'b8a9c05beeedb25df85f8d641538cbffedf67216048de9c678ee26260eb91952'
+
+const sampleMnemonic = 'finish oppose decorate face calm tragic certain desk hour urge dinosaur mango'
+const firstAcct = '1c96099350f13d558464ec79b9be4445aa0ef579'
+const secondAcct = '1b00aed43a693f3a957f9feb5cc08afa031e37a0'
+
+describe('hd-keyring', function() {
+
+ let keyring
+ beforeEach(function() {
+ keyring = new HdKeyring()
+ })
+
+ describe('constructor', function(done) {
+ keyring = new HdKeyring({
+ mnemonic: sampleMnemonic,
+ numberOfAccounts: 2,
+ })
+
+ const accounts = keyring.getAccounts()
+ .then((accounts) => {
+ assert.equal(accounts[0], firstAcct)
+ assert.equal(accounts[1], secondAcct)
+ done()
+ })
+ })
+
+ describe('Keyring.type', function() {
+ it('is a class property that returns the type string.', function() {
+ const type = HdKeyring.type
+ assert.equal(typeof type, 'string')
+ })
+ })
+
+ describe('#type', function() {
+ it('returns the correct value', function() {
+ const type = keyring.type
+ const correct = HdKeyring.type
+ assert.equal(type, correct)
+ })
+ })
+
+ describe('#serialize empty wallets.', function() {
+ it('serializes a new mnemonic', function() {
+ keyring.serialize()
+ .then((output) => {
+ assert.equal(output.numberOfAccounts, 0)
+ assert.equal(output.mnemonic, null)
+ })
+ })
+ })
+
+ describe('#deserialize a private key', function() {
+ it('serializes what it deserializes', function(done) {
+ keyring.deserialize({
+ mnemonic: sampleMnemonic,
+ numberOfAccounts: 1
+ })
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1, 'restores two accounts')
+ return keyring.addAccounts(1)
+ }).then(() => {
+ return keyring.getAccounts()
+ }).then((accounts) => {
+ assert.equal(accounts[0], firstAcct)
+ assert.equal(accounts[1], secondAcct)
+ assert.equal(accounts.length, 2)
+
+ return keyring.serialize()
+ }).then((serialized) => {
+ assert.equal(serialized.mnemonic, sampleMnemonic)
+ done()
+ })
+ })
+ })
+
+ describe('#addAccounts', function() {
+ describe('with no arguments', function() {
+ it('creates a single wallet', function(done) {
+ keyring.addAccounts()
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1)
+ done()
+ })
+ })
+ })
+
+ describe('with a numeric argument', function() {
+ it('creates that number of wallets', function(done) {
+ keyring.addAccounts(3)
+ .then(() => {
+ assert.equal(keyring.wallets.length, 3)
+ done()
+ })
+ })
+ })
+ })
+
+ describe('#getAccounts', function() {
+ it('calls getAddress on each wallet', function(done) {
+
+ // Push a mock wallet
+ const desiredOutput = 'foo'
+ keyring.wallets.push({
+ getAddress() {
+ return {
+ toString() {
+ return desiredOutput
+ }
+ }
+ }
+ })
+
+ const output = keyring.getAccounts()
+ .then((output) => {
+ assert.equal(output[0], desiredOutput)
+ assert.equal(output.length, 1)
+ done()
+ })
+ })
+ })
+})
diff --git a/test/unit/keyrings/simple-test.js b/test/unit/keyrings/simple-test.js
new file mode 100644
index 000000000..ba7dd448a
--- /dev/null
+++ b/test/unit/keyrings/simple-test.js
@@ -0,0 +1,149 @@
+const assert = require('assert')
+const extend = require('xtend')
+const Web3 = require('web3')
+const web3 = new Web3()
+const ethUtil = require('ethereumjs-util')
+const SimpleKeyring = require('../../../app/scripts/keyrings/simple')
+const TYPE_STR = 'Simple Key Pair'
+
+// Sample account:
+const privKeyHex = 'b8a9c05beeedb25df85f8d641538cbffedf67216048de9c678ee26260eb91952'
+
+describe('simple-keyring', function() {
+
+ let keyring
+ beforeEach(function() {
+ keyring = new SimpleKeyring()
+ })
+
+ describe('Keyring.type', function() {
+ it('is a class property that returns the type string.', function() {
+ const type = SimpleKeyring.type
+ assert.equal(type, TYPE_STR)
+ })
+ })
+
+ describe('#type', function() {
+ it('returns the correct value', function() {
+ const type = keyring.type
+ assert.equal(type, TYPE_STR)
+ })
+ })
+
+ describe('#serialize empty wallets.', function() {
+ it('serializes an empty array', function(done) {
+ keyring.serialize()
+ .then((output) => {
+ assert.deepEqual(output, [])
+ done()
+ })
+ })
+ })
+
+ describe('#deserialize a private key', function() {
+ it('serializes what it deserializes', function() {
+ keyring.deserialize([privKeyHex])
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1, 'has one wallet')
+ const serialized = keyring.serialize()
+ assert.equal(serialized[0], privKeyHex)
+ })
+ })
+ })
+
+ describe('#signMessage', function() {
+ const address = '0x9858e7d8b79fc3e6d989636721584498926da38a'
+ const message = '0x879a053d4800c6354e76c7985a865d2922c82fb5b3f4577b2fe08b998954f2e0'
+ const privateKey = '0x7dd98753d7b4394095de7d176c58128e2ed6ee600abe97c9f6d9fd65015d9b18'
+ const expectedResult = '0x28fcb6768e5110144a55b2e6ce9d1ea5a58103033632d272d2b5cf506906f7941a00b539383fd872109633d8c71c404e13dba87bc84166ee31b0e36061a69e161c'
+
+ it('passes the dennis test', function(done) {
+ keyring.deserialize([ privateKey ])
+ .then(() => {
+ return keyring.signMessage(address, message)
+ })
+ .then((result) => {
+ assert.equal(result, expectedResult)
+ done()
+ })
+ })
+
+ it('reliably can decode messages it signs', function (done) {
+
+ const message = 'hello there!'
+ const msgHashHex = web3.sha3(message)
+ let address
+ let addresses = []
+
+ keyring.deserialize([ privateKey ])
+ .then(() => {
+ keyring.addAccounts(9)
+ })
+ .then(() => {
+ return keyring.getAccounts()
+ })
+ .then((addrs) => {
+ addresses = addrs
+ return Promise.all(addresses.map((address) => {
+ return keyring.signMessage(address, msgHashHex)
+ }))
+ })
+ .then((signatures) => {
+
+ signatures.forEach((sgn, index) => {
+ const address = addresses[index]
+
+ var r = ethUtil.toBuffer(sgn.slice(0,66))
+ var s = ethUtil.toBuffer('0x' + sgn.slice(66,130))
+ var v = ethUtil.bufferToInt(ethUtil.toBuffer('0x' + sgn.slice(130,132)))
+ var m = ethUtil.toBuffer(msgHashHex)
+ var pub = ethUtil.ecrecover(m, v, r, s)
+ var adr = '0x' + ethUtil.pubToAddress(pub).toString('hex')
+
+ assert.equal(adr, address, 'recovers address from signature correctly')
+ })
+ done()
+ })
+ })
+ })
+
+ describe('#addAccounts', function() {
+ describe('with no arguments', function() {
+ it('creates a single wallet', function() {
+ keyring.addAccounts()
+ .then(() => {
+ assert.equal(keyring.wallets.length, 1)
+ })
+ })
+ })
+
+ describe('with a numeric argument', function() {
+ it('creates that number of wallets', function() {
+ keyring.addAccounts(3)
+ .then(() => {
+ assert.equal(keyring.wallets.length, 3)
+ })
+ })
+ })
+ })
+
+ describe('#getAccounts', function() {
+ it('calls getAddress on each wallet', function(done) {
+
+ // Push a mock wallet
+ const desiredOutput = '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761'
+ keyring.wallets.push({
+ getAddress() {
+ return ethUtil.toBuffer(desiredOutput)
+ }
+ })
+
+ keyring.getAccounts()
+ .then((output) => {
+ assert.equal(output[0], desiredOutput)
+ assert.equal(output.length, 1)
+ done()
+ })
+ })
+ })
+})
diff --git a/test/unit/message-manager-test.js b/test/unit/message-manager-test.js
new file mode 100644
index 000000000..faf7429d4
--- /dev/null
+++ b/test/unit/message-manager-test.js
@@ -0,0 +1,89 @@
+const assert = require('assert')
+const extend = require('xtend')
+const EventEmitter = require('events')
+
+const MessageManger = require('../../app/scripts/lib/message-manager')
+
+describe('Transaction Manager', function() {
+ let messageManager
+
+ beforeEach(function() {
+ messageManager = new MessageManger ()
+ })
+
+ describe('#getMsgList', function() {
+ it('when new should return empty array', function() {
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ it('should also return transactions from local storage if any', function() {
+
+ })
+ })
+
+ describe('#addMsg', function() {
+ it('adds a Msg returned in getMsgList', function() {
+ var Msg = { id: 1, status: 'approved', metamaskNetworkId: 'unit test' }
+ messageManager.addMsg(Msg)
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].id, 1)
+ })
+ })
+
+ describe('#setMsgStatusApproved', function() {
+ it('sets the Msg status to approved', function() {
+ var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
+ messageManager.addMsg(Msg)
+ messageManager.setMsgStatusApproved(1)
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'approved')
+ })
+ })
+
+ describe('#rejectMsg', function() {
+ it('sets the Msg status to rejected', function() {
+ var Msg = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test' }
+ messageManager.addMsg(Msg)
+ messageManager.rejectMsg(1)
+ var result = messageManager.messages
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'rejected')
+ })
+ })
+
+ describe('#_updateMsg', function() {
+ it('replaces the Msg with the same id', function() {
+ messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
+ messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
+ messageManager._updateMsg({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test' })
+ var result = messageManager.getMsg('1')
+ assert.equal(result.hash, 'foo')
+ })
+ })
+
+ describe('#getUnapprovedMsgs', function() {
+ it('returns unapproved Msgs in a hash', function() {
+ messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
+ messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
+ let result = messageManager.getUnapprovedMsgs()
+ assert.equal(typeof result, 'object')
+ assert.equal(result['1'].status, 'unapproved')
+ assert.equal(result['2'], undefined)
+ })
+ })
+
+ describe('#getMsg', function() {
+ it('returns a Msg with the requested id', function() {
+ messageManager.addMsg({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test' })
+ messageManager.addMsg({ id: '2', status: 'approved', metamaskNetworkId: 'unit test' })
+ assert.equal(messageManager.getMsg('1').status, 'unapproved')
+ assert.equal(messageManager.getMsg('2').status, 'approved')
+ })
+ })
+})
diff --git a/test/unit/metamask-controller-test.js b/test/unit/metamask-controller-test.js
new file mode 100644
index 000000000..78b9e9df7
--- /dev/null
+++ b/test/unit/metamask-controller-test.js
@@ -0,0 +1,29 @@
+const assert = require('assert')
+const sinon = require('sinon')
+const clone = require('clone')
+const MetaMaskController = require('../../app/scripts/metamask-controller')
+const firstTimeState = require('../../app/scripts/first-time-state')
+
+const STORAGE_KEY = 'metamask-config'
+
+describe('MetaMaskController', function() {
+ const noop = () => {}
+ let controller = new MetaMaskController({
+ showUnconfirmedMessage: noop,
+ unlockAccountMessage: noop,
+ showUnapprovedTx: noop,
+ // initial state
+ initState: clone(firstTimeState),
+ })
+
+ beforeEach(function() {
+ // sinon allows stubbing methods that are easily verified
+ this.sinon = sinon.sandbox.create()
+ })
+
+ afterEach(function() {
+ // sinon requires cleanup otherwise it will overwrite context
+ this.sinon.restore()
+ })
+
+}) \ No newline at end of file
diff --git a/test/unit/migrations-test.js b/test/unit/migrations-test.js
index 9ea8d5c5a..d2a83be77 100644
--- a/test/unit/migrations-test.js
+++ b/test/unit/migrations-test.js
@@ -1,34 +1,97 @@
-var assert = require('assert')
-var path = require('path')
+const assert = require('assert')
+const path = require('path')
-var wallet1 = require(path.join('..', 'lib', 'migrations', '001.json'))
+const wallet1 = require(path.join('..', 'lib', 'migrations', '001.json'))
+const vault4 = require(path.join('..', 'lib', 'migrations', '004.json'))
+let vault5, vault6, vault7, vault8, vault9, vault10, vault11
-var migration2 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '002'))
-var migration3 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '003'))
-var migration4 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '004'))
+const migration2 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '002'))
+const migration3 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '003'))
+const migration4 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '004'))
+const migration5 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '005'))
+const migration6 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '006'))
+const migration7 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '007'))
+const migration8 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '008'))
+const migration9 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '009'))
+const migration10 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '010'))
+const migration11 = require(path.join('..', '..', 'app', 'scripts', 'migrations', '011'))
-describe('wallet1 is migrated successfully', function() {
+const oldTestRpc = 'https://rawtestrpc.metamask.io/'
+const newTestRpc = 'https://testrpc.metamask.io/'
- it('should convert providers', function(done) {
+describe('wallet1 is migrated successfully', () => {
+ it('should convert providers', () => {
wallet1.data.config.provider = { type: 'etherscan', rpcTarget: null }
- var firstResult = migration2.migrate(wallet1.data)
- assert.equal(firstResult.config.provider.type, 'rpc', 'provider should be rpc')
- assert.equal(firstResult.config.provider.rpcTarget, 'https://rpc.metamask.io/', 'main provider should be our rpc')
+ return migration2.migrate(wallet1)
+ .then((secondResult) => {
+ const secondData = secondResult.data
+ assert.equal(secondData.config.provider.type, 'rpc', 'provider should be rpc')
+ assert.equal(secondData.config.provider.rpcTarget, 'https://rpc.metamask.io/', 'main provider should be our rpc')
+ secondResult.data.config.provider.rpcTarget = oldTestRpc
+ return migration3.migrate(secondResult)
+ }).then((thirdResult) => {
+ assert.equal(thirdResult.data.config.provider.rpcTarget, newTestRpc, 'config.provider.rpcTarget should be set to the proper testrpc url.')
+ return migration4.migrate(thirdResult)
+ }).then((fourthResult) => {
+ const fourthData = fourthResult.data
+ assert.equal(fourthData.config.provider.rpcTarget, null, 'old rpcTarget should not exist.')
+ assert.equal(fourthData.config.provider.type, 'testnet', 'config.provider should be set to testnet.')
- var oldTestRpc = 'https://rawtestrpc.metamask.io/'
- var newTestRpc = 'https://testrpc.metamask.io/'
- firstResult.config.provider.rpcTarget = oldTestRpc
+ return migration5.migrate(vault4)
+ }).then((fifthResult) => {
+ const fifthData = fifthResult.data
+ assert.equal(fifthData.vault, null, 'old vault should not exist')
+ assert.equal(fifthData.walletNicknames, null, 'old walletNicknames should not exist')
+ assert.equal(fifthData.config.selectedAccount, null, 'old config.selectedAccount should not exist')
+ assert.equal(fifthData.KeyringController.vault, vault4.data.vault, 'KeyringController.vault should exist')
+ assert.equal(fifthData.KeyringController.selectedAccount, vault4.data.config.selectedAccount, 'KeyringController.selectedAccount should have moved')
+ assert.equal(fifthData.KeyringController.walletNicknames['0x0beb674745816b125fbc07285d39fd373e64895c'], vault4.data.walletNicknames['0x0beb674745816b125fbc07285d39fd373e64895c'], 'KeyringController.walletNicknames should have moved')
- var secondResult = migration3.migrate(firstResult)
- assert.equal(secondResult.config.provider.rpcTarget, newTestRpc)
+ vault5 = fifthResult
+ return migration6.migrate(fifthResult)
+ }).then((sixthResult) => {
+ assert.equal(sixthResult.data.KeyringController.selectedAccount, null, 'old selectedAccount should not exist')
+ assert.equal(sixthResult.data.PreferencesController.selectedAddress, vault5.data.KeyringController.selectedAccount, 'selectedAccount should have moved')
- var thirdResult = migration4.migrate(secondResult)
- assert.equal(secondResult.config.provider.rpcTarget, null)
- assert.equal(secondResult.config.provider.type, 'testnet')
+ vault6 = sixthResult
+ return migration7.migrate(sixthResult)
+ }).then((seventhResult) => {
+ assert.equal(seventhResult.data.transactions, null, 'old transactions should not exist')
+ assert.equal(seventhResult.data.gasMultiplier, null, 'old gasMultiplier should not exist')
+ assert.equal(seventhResult.data.TransactionManager.transactions[0].id, vault6.data.transactions[0].id, 'transactions should have moved')
+ assert.equal(seventhResult.data.TransactionManager.gasMultiplier, vault6.data.gasMultiplier, 'gasMultiplier should have moved')
+
+ vault7 = seventhResult
+ return migration8.migrate(seventhResult)
+ }).then((eighthResult) => {
+ assert.equal(eighthResult.data.noticesList, null, 'old noticesList should not exist')
+ assert.equal(eighthResult.data.NoticeController.noticesList[0].title, vault7.data.noticesList[0].title, 'noticesList should have moved')
+
+ vault8 = eighthResult
+ return migration9.migrate(eighthResult)
+ }).then((ninthResult) => {
+ assert.equal(ninthResult.data.currentFiat, null, 'old currentFiat should not exist')
+ assert.equal(ninthResult.data.fiatCurrency, null, 'old fiatCurrency should not exist')
+ assert.equal(ninthResult.data.conversionRate, null, 'old conversionRate should not exist')
+ assert.equal(ninthResult.data.conversionDate, null, 'old conversionDate should not exist')
+
+ assert.equal(ninthResult.data.CurrencyController.currentCurrency, vault8.data.fiatCurrency, 'currentFiat should have moved')
+ assert.equal(ninthResult.data.CurrencyController.conversionRate, vault8.data.conversionRate, 'conversionRate should have moved')
+ assert.equal(ninthResult.data.CurrencyController.conversionDate, vault8.data.conversionDate, 'conversionDate should have moved')
+
+ vault9 = ninthResult
+ return migration10.migrate(ninthResult)
+ }).then((tenthResult) => {
+ assert.equal(tenthResult.data.shapeShiftTxList, null, 'old shapeShiftTxList should not exist')
+ assert.equal(tenthResult.data.ShapeShiftController.shapeShiftTxList[0].transaction, vault9.data.shapeShiftTxList[0].transaction)
+
+ return migration11.migrate(tenthResult)
+ }).then((eleventhResult) => {
+ assert.equal(eleventhResult.data.isDisclaimerConfirmed, null, 'isDisclaimerConfirmed should not exist')
+ assert.equal(eleventhResult.data.TOSHash, null, 'TOSHash should not exist')
+ })
- done()
})
})
-
diff --git a/test/unit/nodeify-test.js b/test/unit/nodeify-test.js
new file mode 100644
index 000000000..a14d34338
--- /dev/null
+++ b/test/unit/nodeify-test.js
@@ -0,0 +1,22 @@
+const assert = require('assert')
+const nodeify = require('../../app/scripts/lib/nodeify')
+
+describe('nodeify', function() {
+
+ var obj = {
+ foo: 'bar',
+ promiseFunc: function (a) {
+ var solution = this.foo + a
+ return Promise.resolve(solution)
+ }
+ }
+
+ it('should retain original context', function(done) {
+ var nodified = nodeify(obj.promiseFunc).bind(obj)
+ nodified('baz', function (err, res) {
+ assert.equal(res, 'barbaz')
+ done()
+ })
+ })
+
+})
diff --git a/test/unit/notice-controller-test.js b/test/unit/notice-controller-test.js
new file mode 100644
index 000000000..cf00daeba
--- /dev/null
+++ b/test/unit/notice-controller-test.js
@@ -0,0 +1,116 @@
+const assert = require('assert')
+const extend = require('xtend')
+const rp = require('request-promise')
+const nock = require('nock')
+const configManagerGen = require('../lib/mock-config-manager')
+const NoticeController = require('../../app/scripts/notice-controller')
+const STORAGE_KEY = 'metamask-persistance-key'
+
+describe('notice-controller', function() {
+ var noticeController
+
+ beforeEach(function() {
+ // simple localStorage polyfill
+ window.localStorage = {}
+ if (window.localStorage.clear) window.localStorage.clear()
+ let configManager = configManagerGen()
+ noticeController = new NoticeController({
+ configManager: configManager,
+ })
+ })
+
+ describe('notices', function() {
+ describe('#getNoticesList', function() {
+ it('should return an empty array when new', function() {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ var result = noticeController.getNoticesList()
+ assert.equal(result.length, 0)
+ })
+ })
+
+ describe('#setNoticesList', function() {
+ it('should set data appropriately', function () {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ var testListId = noticeController.getNoticesList()[0].id
+ assert.equal(testListId, 0)
+ })
+ })
+
+ describe('#updateNoticeslist', function() {
+ it('should integrate the latest changes from the source', function() {
+ var testList = [{
+ id:55,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ noticeController.updateNoticesList().then(() => {
+ var newList = noticeController.getNoticesList()
+ assert.ok(newList[0].id === 55)
+ assert.ok(newList[1])
+ })
+ })
+ it('should not overwrite any existing fields', function () {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ noticeController.updateNoticesList().then(() => {
+ var newList = noticeController.getNoticesList()
+ assert.equal(newList[0].id, 0)
+ assert.equal(newList[0].title, "Futuristic Notice")
+ assert.equal(newList.length, 1)
+ })
+ })
+ })
+
+ describe('#markNoticeRead', function () {
+ it('should mark a notice as read', function () {
+ var testList = [{
+ id:0,
+ read:false,
+ title:"Futuristic Notice"
+ }]
+ noticeController.setNoticesList(testList)
+ noticeController.markNoticeRead(testList[0])
+ var newList = noticeController.getNoticesList()
+ assert.ok(newList[0].read)
+ })
+ })
+
+ describe('#getLatestUnreadNotice', function () {
+ it('should retrieve the latest unread notice', function () {
+ var testList = [
+ {id:0,read:true,title:"Past Notice"},
+ {id:1,read:false,title:"Current Notice"},
+ {id:2,read:false,title:"Future Notice"},
+ ]
+ noticeController.setNoticesList(testList)
+ var latestUnread = noticeController.getLatestUnreadNotice()
+ assert.equal(latestUnread.id, 2)
+ })
+ it('should return undefined if no unread notices exist.', function () {
+ var testList = [
+ {id:0,read:true,title:"Past Notice"},
+ {id:1,read:true,title:"Current Notice"},
+ {id:2,read:true,title:"Future Notice"},
+ ]
+ noticeController.setNoticesList(testList)
+ var latestUnread = noticeController.getLatestUnreadNotice()
+ assert.ok(!latestUnread)
+ })
+ })
+ })
+
+})
diff --git a/test/unit/tx-manager-test.js b/test/unit/tx-manager-test.js
new file mode 100644
index 000000000..f64f048e3
--- /dev/null
+++ b/test/unit/tx-manager-test.js
@@ -0,0 +1,211 @@
+const assert = require('assert')
+const extend = require('xtend')
+const EventEmitter = require('events')
+const ObservableStore = require('obs-store')
+const STORAGE_KEY = 'metamask-persistance-key'
+const TransactionManager = require('../../app/scripts/transaction-manager')
+const noop = () => true
+
+describe('Transaction Manager', function() {
+ let txManager
+
+ beforeEach(function() {
+ txManager = new TransactionManager({
+ networkStore: new ObservableStore({ network: 'unit test' }),
+ txHistoryLimit: 10,
+ blockTracker: new EventEmitter(),
+ })
+ })
+
+ describe('#validateTxParams', function () {
+ it('returns null for positive values', function() {
+ var sample = {
+ value: '0x01'
+ }
+ var res = txManager.txProviderUtils.validateTxParams(sample, (err) => {
+ assert.equal(err, null, 'no error')
+ })
+ })
+
+
+ it('returns error for negative values', function() {
+ var sample = {
+ value: '-0x01'
+ }
+ var res = txManager.txProviderUtils.validateTxParams(sample, (err) => {
+ assert.ok(err, 'error')
+ })
+ })
+ })
+
+ describe('#getTxList', function() {
+ it('when new should return empty array', function() {
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 0)
+ })
+ it('should also return transactions from local storage if any', function() {
+
+ })
+ })
+
+ describe('#addTx', function() {
+ it('adds a tx returned in getTxList', function() {
+ var tx = { id: 1, status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].id, 1)
+ })
+
+ it('cuts off early txs beyond a limit', function() {
+ const limit = txManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ }
+ var result = txManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 1, 'early txs truncted')
+ })
+
+ it('cuts off early txs beyond a limit whether or not it is confirmed or rejected', function() {
+ const limit = txManager.txHistoryLimit
+ for (let i = 0; i < limit + 1; i++) {
+ let tx = { id: i, time: new Date(), status: 'rejected', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ }
+ var result = txManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 1, 'early txs truncted')
+ })
+
+ it('cuts off early txs beyond a limit but does not cut unapproved txs', function() {
+ var unconfirmedTx = { id: 0, time: new Date(), status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(unconfirmedTx, noop)
+ const limit = txManager.txHistoryLimit
+ for (let i = 1; i < limit + 1; i++) {
+ let tx = { id: i, time: new Date(), status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ }
+ var result = txManager.getTxList()
+ assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
+ assert.equal(result[0].id, 0, 'first tx should still be there')
+ assert.equal(result[0].status, 'unapproved', 'first tx should be unapproved')
+ assert.equal(result[1].id, 2, 'early txs truncted')
+ })
+ })
+
+ describe('#setTxStatusSigned', function() {
+ it('sets the tx status to signed', function() {
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx, noop)
+ txManager.setTxStatusSigned(1)
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'signed')
+ })
+
+ it('should emit a signed event to signal the exciton of callback', (done) => {
+ this.timeout(10000)
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ let noop = function () {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txManager.addTx(tx)
+ txManager.on('1:signed', noop)
+ txManager.setTxStatusSigned(1)
+ })
+ })
+
+ describe('#setTxStatusRejected', function() {
+ it('sets the tx status to rejected', function() {
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx)
+ txManager.setTxStatusRejected(1)
+ var result = txManager.getTxList()
+ assert.ok(Array.isArray(result))
+ assert.equal(result.length, 1)
+ assert.equal(result[0].status, 'rejected')
+ })
+
+ it('should emit a rejected event to signal the exciton of callback', (done) => {
+ this.timeout(10000)
+ var tx = { id: 1, status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }
+ txManager.addTx(tx)
+ let noop = function (err, txId) {
+ assert(true, 'event listener has been triggered and noop executed')
+ done()
+ }
+ txManager.on('1:rejected', noop)
+ txManager.setTxStatusRejected(1)
+ })
+
+ })
+
+ describe('#updateTx', function() {
+ it('replaces the tx with the same id', function() {
+ txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.updateTx({ id: '1', status: 'blah', hash: 'foo', metamaskNetworkId: 'unit test', txParams: {} })
+ var result = txManager.getTx('1')
+ assert.equal(result.hash, 'foo')
+ })
+ })
+
+ describe('#getUnapprovedTxList', function() {
+ it('returns unapproved txs in a hash', function() {
+ txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ let result = txManager.getUnapprovedTxList()
+ assert.equal(typeof result, 'object')
+ assert.equal(result['1'].status, 'unapproved')
+ assert.equal(result['2'], undefined)
+ })
+ })
+
+ describe('#getTx', function() {
+ it('returns a tx with the requested id', function() {
+ txManager.addTx({ id: '1', status: 'unapproved', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ txManager.addTx({ id: '2', status: 'confirmed', metamaskNetworkId: 'unit test', txParams: {} }, noop)
+ assert.equal(txManager.getTx('1').status, 'unapproved')
+ assert.equal(txManager.getTx('2').status, 'confirmed')
+ })
+ })
+
+ describe('#getFilteredTxList', function() {
+ it('returns a tx with the requested data', function() {
+ let txMetas = [
+ { id: 0, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 1, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 2, status: 'unapproved', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 3, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 4, status: 'unapproved', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 5, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 6, status: 'confirmed', txParams: { from: '0xaa', to: '0xbb' }, metamaskNetworkId: 'unit test' },
+ { id: 7, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 8, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ { id: 9, status: 'confirmed', txParams: { from: '0xbb', to: '0xaa' }, metamaskNetworkId: 'unit test' },
+ ]
+ txMetas.forEach((txMeta) => txManager.addTx(txMeta, noop))
+ let filterParams
+
+ filterParams = { status: 'unapproved', from: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'unapproved', to: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 2, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed', from: '0xbb' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 3, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { status: 'confirmed' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { from: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ filterParams = { to: '0xaa' }
+ assert.equal(txManager.getFilteredTxList(filterParams).length, 5, `getFilteredTxList - ${JSON.stringify(filterParams)}`)
+ })
+ })
+
+})
diff --git a/test/unit/util_test.js b/test/unit/util_test.js
index 45e545e8e..00528b905 100644
--- a/test/unit/util_test.js
+++ b/test/unit/util_test.js
@@ -227,5 +227,27 @@ describe('util', function() {
assert.equal(result.toString(10), '1111000000000000000', 'accepts decimals')
})
})
+ describe('#isHex', function(){
+ it('should return true when given a hex string', function() {
+ var result = util.isHex('c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2')
+ assert(result)
+ })
+
+ it('should return false when given a non-hex string', function() {
+ var result = util.isHex('c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714imnotreal')
+ assert(!result)
+ })
+
+ it('should return false when given a string containing a non letter/number character', function() {
+ var result = util.isHex('c3ab8ff13720!8ad9047dd39466b3c%8974e592c2fa383d4a396071imnotreal')
+ assert(!result)
+ })
+
+ it('should return true when given a hex string with hex-prefix', function() {
+ var result = util.isHex('0xc3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2')
+ assert(result)
+ })
+
+ })
})
})
diff --git a/testem.yml b/testem.yml
index 7923a2929..2cf40f7f4 100644
--- a/testem.yml
+++ b/testem.yml
@@ -6,4 +6,5 @@ launch_in_ci:
- Firefox
framework:
- qunit
+before_tests: "npm run buildCiUnits"
test_page: "test/integration/index.html"
diff --git a/ui-dev.js b/ui-dev.js
index bfc84d415..e39340859 100644
--- a/ui-dev.js
+++ b/ui-dev.js
@@ -25,7 +25,7 @@ const Selector = require('./development/selector')
// Query String
const qs = require('qs')
let queryString = qs.parse(window.location.href.split('#')[1])
-let selectedView = queryString.view || 'account detail'
+let selectedView = queryString.view || 'first time'
const firstState = states[selectedView]
updateQueryParams(selectedView)
@@ -41,7 +41,7 @@ function updateQueryParams(newView) {
}
const actions = {
- _setAccountManager(){},
+ _setBackgroundConnection(){},
update: function(stateName) {
selectedView = stateName
updateQueryParams(stateName)
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
index 486a1a633..514be7f25 100644
--- a/ui/app/account-detail.js
+++ b/ui/app/account-detail.js
@@ -10,7 +10,7 @@ const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const valuesFor = require('./util').valuesFor
const Identicon = require('./components/identicon')
-const AccountEtherBalance = require('./components/account-eth-balance')
+const EthBalance = require('./components/eth-balance')
const TransactionList = require('./components/transaction-list')
const ExportAccountView = require('./components/account-export')
const ethUtil = require('ethereumjs-util')
@@ -24,14 +24,12 @@ function mapStateToProps (state) {
metamask: state.metamask,
identities: state.metamask.identities,
accounts: state.metamask.accounts,
- address: state.metamask.selectedAccount,
+ address: state.metamask.selectedAddress,
accountDetail: state.appState.accountDetail,
- transactions: state.metamask.transactions,
network: state.metamask.network,
- unconfTxs: valuesFor(state.metamask.unconfTxs),
- unconfMsgs: valuesFor(state.metamask.unconfMsgs),
- isEthWarningConfirmed: state.metamask.isEthConfirmed,
+ unapprovedMsgs: valuesFor(state.metamask.unapprovedMsgs),
shapeShiftTxList: state.metamask.shapeShiftTxList,
+ transactions: state.metamask.selectedAddressTxList || [],
}
}
@@ -43,6 +41,7 @@ function AccountDetailScreen () {
AccountDetailScreen.prototype.render = function () {
var props = this.props
var selected = props.address || Object.keys(props.accounts)[0]
+ var checksumAddress = selected && ethUtil.toChecksumAddress(selected)
var identity = props.identities[selected]
var account = props.accounts[selected]
const { network } = props
@@ -118,25 +117,39 @@ AccountDetailScreen.prototype.render = function () {
marginBottom: '15px',
color: '#AEAEAE',
},
- }, ethUtil.toChecksumAddress(selected)),
+ }, checksumAddress),
// copy and export
h('.flex-row', {
style: {
justifyContent: 'flex-end',
- position: 'relative',
- bottom: '15px',
},
}, [
h(AccountInfoLink, { selected, network }),
h(CopyButton, {
- value: ethUtil.toChecksumAddress(selected),
+ value: checksumAddress,
}),
h(Tooltip, {
+ title: 'QR Code',
+ }, [
+ h('i.fa.fa-qrcode.pointer.pop-hover', {
+ onClick: () => props.dispatch(actions.showQrView(selected, identity ? identity.name : '')),
+ style: {
+ fontSize: '18px',
+ position: 'relative',
+ color: 'rgb(247, 134, 28)',
+ top: '5px',
+ marginLeft: '3px',
+ marginRight: '3px',
+ },
+ }),
+ ]),
+
+ h(Tooltip, {
title: 'Export Private Key',
}, [
h('div', {
@@ -168,7 +181,7 @@ AccountDetailScreen.prototype.render = function () {
},
}, [
- h(AccountEtherBalance, {
+ h(EthBalance, {
value: account && account.balance,
style: {
lineHeight: '7px',
@@ -233,21 +246,11 @@ AccountDetailScreen.prototype.subview = function () {
}
AccountDetailScreen.prototype.transactionList = function () {
- const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props
-
- var txsToRender = transactions
- // only transactions that are from the current address
- .filter(tx => tx.txParams.from === address)
- // only transactions that are on the current network
- .filter(tx => tx.txParams.metamaskNetworkId === network)
- // sort by recency
- .sort((a, b) => b.time - a.time)
-
+ const {transactions, unapprovedMsgs, address, network, shapeShiftTxList } = this.props
return h(TransactionList, {
- txsToRender,
+ transactions: transactions.sort((a, b) => b.time - a.time),
network,
- unconfTxs,
- unconfMsgs,
+ unapprovedMsgs,
address,
shapeShiftTxList,
viewPendingTx: (txId) => {
@@ -263,10 +266,11 @@ AccountDetailScreen.prototype.requestAccountExport = function () {
AccountDetailScreen.prototype.buyButtonDeligator = function () {
var props = this.props
+ var selected = props.address || Object.keys(props.accounts)[0]
if (this.props.accountDetail.subview === 'buyForm') {
props.dispatch(actions.backToAccountDetail(props.address))
} else {
- props.dispatch(actions.buyEthView())
+ props.dispatch(actions.buyEthView(selected))
}
}
diff --git a/ui/app/accounts/account-list-item.js b/ui/app/accounts/account-list-item.js
index aa80e0e23..2a3c13d05 100644
--- a/ui/app/accounts/account-list-item.js
+++ b/ui/app/accounts/account-list-item.js
@@ -3,34 +3,34 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
-const AccountEtherBalance = require('../components/account-eth-balance')
+const EthBalance = require('../components/eth-balance')
const CopyButton = require('../components/copyButton')
const Identicon = require('../components/identicon')
-module.exports = NewComponent
+module.exports = AccountListItem
-inherits(NewComponent, Component)
-function NewComponent () {
+inherits(AccountListItem, Component)
+function AccountListItem () {
Component.call(this)
}
-NewComponent.prototype.render = function () {
- const identity = this.props.identity
- var isSelected = this.props.selectedAddress === identity.address
- var account = this.props.accounts[identity.address]
+AccountListItem.prototype.render = function () {
+ const { identity, selectedAddress, accounts, onShowDetail } = this.props
+
+ const checksumAddress = identity && identity.address && ethUtil.toChecksumAddress(identity.address)
+ const isSelected = selectedAddress === identity.address
+ const account = accounts[identity.address]
const selectedClass = isSelected ? '.selected' : ''
return (
h(`.accounts-list-option.flex-row.flex-space-between.pointer.hover-white${selectedClass}`, {
key: `account-panel-${identity.address}`,
- style: {
- flex: '1 0 auto',
- },
- onClick: (event) => this.props.onShowDetail(identity.address, event),
+ onClick: (event) => onShowDetail(identity.address, event),
}, [
h('.identicon-wrapper.flex-column.flex-center.select-none', [
this.pendingOrNot(),
+ this.indicateIfLoose(),
h(Identicon, {
address: identity.address,
imageify: true,
@@ -49,9 +49,9 @@ NewComponent.prototype.render = function () {
overflow: 'hidden',
textOverflow: 'ellipsis',
},
- }, ethUtil.toChecksumAddress(identity.address)),
- h(AccountEtherBalance, {
- value: account.balance,
+ }, checksumAddress),
+ h(EthBalance, {
+ value: account && account.balance,
style: {
lineHeight: '7px',
marginTop: '10px',
@@ -66,14 +66,22 @@ NewComponent.prototype.render = function () {
},
}, [
h(CopyButton, {
- value: ethUtil.toChecksumAddress(identity.address),
+ value: checksumAddress,
}),
]),
])
)
}
-NewComponent.prototype.pendingOrNot = function () {
+AccountListItem.prototype.indicateIfLoose = function () {
+ try { // Sometimes keyrings aren't loaded yet:
+ const type = this.props.keyring.type
+ const isLoose = type !== 'HD Key Tree'
+ return isLoose ? h('.keyring-label', 'LOOSE') : null
+ } catch (e) { return }
+}
+
+AccountListItem.prototype.pendingOrNot = function () {
const pending = this.props.pending
if (pending.length === 0) return null
return h('.pending-dot', pending.length)
diff --git a/ui/app/accounts/import/index.js b/ui/app/accounts/import/index.js
new file mode 100644
index 000000000..96350852a
--- /dev/null
+++ b/ui/app/accounts/import/index.js
@@ -0,0 +1,91 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const connect = require('react-redux').connect
+import Select from 'react-select'
+
+// Subviews
+const JsonImportView = require('./json.js')
+const PrivateKeyImportView = require('./private-key.js')
+
+const menuItems = [
+ 'Private Key',
+ 'JSON File',
+]
+
+module.exports = connect(mapStateToProps)(AccountImportSubview)
+
+function mapStateToProps (state) {
+ return {
+ menuItems,
+ }
+}
+
+inherits(AccountImportSubview, Component)
+function AccountImportSubview () {
+ Component.call(this)
+}
+
+AccountImportSubview.prototype.render = function () {
+ const props = this.props
+ const state = this.state || {}
+ const { menuItems } = props
+ const { type } = state
+
+ return (
+ h('div', {
+ style: {
+ },
+ }, [
+ h('div', {
+ style: {
+ padding: '10px',
+ color: 'rgb(174, 174, 174)',
+ },
+ }, [
+
+ h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'),
+
+ h('style', `
+ .has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
+ color: rgb(174,174,174);
+ }
+ `),
+
+ h(Select, {
+ name: 'import-type-select',
+ clearable: false,
+ value: type || menuItems[0],
+ options: menuItems.map((type) => {
+ return {
+ value: type,
+ label: type,
+ }
+ }),
+ onChange: (opt) => {
+ this.setState({ type: opt.value })
+ },
+ }),
+ ]),
+
+ this.renderImportView(),
+ ])
+ )
+}
+
+AccountImportSubview.prototype.renderImportView = function() {
+ const props = this.props
+ const state = this.state || {}
+ const { type } = state
+ const { menuItems } = props
+ const current = type || menuItems[0]
+
+ switch (current) {
+ case 'Private Key':
+ return h(PrivateKeyImportView)
+ case 'JSON File':
+ return h(JsonImportView)
+ default:
+ return h(JsonImportView)
+ }
+}
diff --git a/ui/app/accounts/import/json.js b/ui/app/accounts/import/json.js
new file mode 100644
index 000000000..1c2b331d4
--- /dev/null
+++ b/ui/app/accounts/import/json.js
@@ -0,0 +1,98 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const connect = require('react-redux').connect
+const actions = require('../../actions')
+const FileInput = require('react-simple-file-input').default
+
+module.exports = connect(mapStateToProps)(JsonImportSubview)
+
+function mapStateToProps (state) {
+ return {
+ error: state.appState.warning,
+ }
+}
+
+inherits(JsonImportSubview, Component)
+function JsonImportSubview () {
+ Component.call(this)
+}
+
+JsonImportSubview.prototype.render = function () {
+ const { error } = this.props
+
+ return (
+ h('div', {
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ padding: '5px 15px 0px 15px',
+ },
+ }, [
+
+ h('p', 'Used by a variety of different clients'),
+
+ h(FileInput, {
+ readAs: 'text',
+ onLoad: this.onLoad.bind(this),
+ style: {
+ margin: '20px 0px 12px 20px',
+ fontSize: '15px',
+ },
+ }),
+
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ placeholder: 'Enter password',
+ id: 'json-password-box',
+ onKeyPress: this.createKeyringOnEnter.bind(this),
+ style: {
+ width: 260,
+ marginTop: 12,
+ },
+ }),
+
+ h('button.primary', {
+ onClick: this.createNewKeychain.bind(this),
+ style: {
+ margin: 12,
+ },
+ }, 'Import'),
+
+ error ? h('span.warning', error) : null,
+ ])
+ )
+}
+
+JsonImportSubview.prototype.onLoad = function (event, file) {
+ this.setState({file: file, fileContents: event.target.result})
+}
+
+JsonImportSubview.prototype.createKeyringOnEnter = function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.createNewKeychain()
+ }
+}
+
+JsonImportSubview.prototype.createNewKeychain = function () {
+ const state = this.state
+ const { fileContents } = state
+
+ if (!fileContents) {
+ const message = 'You must select a file to import.'
+ return this.props.dispatch(actions.displayWarning(message))
+ }
+
+ const passwordInput = document.getElementById('json-password-box')
+ const password = passwordInput.value
+
+ if (!password) {
+ const message = 'You must enter a password for the selected file.'
+ return this.props.dispatch(actions.displayWarning(message))
+ }
+
+ this.props.dispatch(actions.importNewAccount('JSON File', [ fileContents, password ]))
+}
+
diff --git a/ui/app/accounts/import/private-key.js b/ui/app/accounts/import/private-key.js
new file mode 100644
index 000000000..b139a0374
--- /dev/null
+++ b/ui/app/accounts/import/private-key.js
@@ -0,0 +1,68 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const connect = require('react-redux').connect
+const actions = require('../../actions')
+
+module.exports = connect(mapStateToProps)(PrivateKeyImportView)
+
+function mapStateToProps (state) {
+ return {
+ error: state.appState.warning,
+ }
+}
+
+inherits(PrivateKeyImportView, Component)
+function PrivateKeyImportView () {
+ Component.call(this)
+}
+
+PrivateKeyImportView.prototype.render = function () {
+ const { error } = this.props
+
+ return (
+ h('div', {
+ style: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ padding: '5px 15px 0px 15px',
+ },
+ }, [
+ h('span', 'Paste your private key string here'),
+
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'private-key-box',
+ onKeyPress: this.createKeyringOnEnter.bind(this),
+ style: {
+ width: 260,
+ marginTop: 12,
+ },
+ }),
+
+ h('button.primary', {
+ onClick: this.createNewKeychain.bind(this),
+ style: {
+ margin: 12,
+ },
+ }, 'Import'),
+
+ error ? h('span.warning', error) : null,
+ ])
+ )
+}
+
+PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.createNewKeychain()
+ }
+}
+
+PrivateKeyImportView.prototype.createNewKeychain = function () {
+ const input = document.getElementById('private-key-box')
+ const privateKey = input.value
+ this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
+}
+
diff --git a/ui/app/accounts/import/seed.js b/ui/app/accounts/import/seed.js
new file mode 100644
index 000000000..b4a7c0afa
--- /dev/null
+++ b/ui/app/accounts/import/seed.js
@@ -0,0 +1,30 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const connect = require('react-redux').connect
+
+module.exports = connect(mapStateToProps)(SeedImportSubview)
+
+function mapStateToProps (state) {
+ return {}
+}
+
+inherits(SeedImportSubview, Component)
+function SeedImportSubview () {
+ Component.call(this)
+}
+
+SeedImportSubview.prototype.render = function () {
+ return (
+ h('div', {
+ style: {
+ },
+ }, [
+ `Paste your seed phrase here!`,
+ h('textarea'),
+ h('br'),
+ h('button', 'Submit'),
+ ])
+ )
+}
+
diff --git a/ui/app/accounts/index.js b/ui/app/accounts/index.js
index 6e12accc7..e236a4e85 100644
--- a/ui/app/accounts/index.js
+++ b/ui/app/accounts/index.js
@@ -10,18 +10,19 @@ const AccountListItem = require('./account-list-item')
module.exports = connect(mapStateToProps)(AccountsScreen)
function mapStateToProps (state) {
- const pendingTxs = valuesFor(state.metamask.unconfTxs)
- const pendingMsgs = valuesFor(state.metamask.unconfMsgs)
+ const pendingTxs = valuesFor(state.metamask.unapprovedTxs)
+ .filter(tx => tx.txParams.metamaskNetworkId === state.metamask.network)
+ const pendingMsgs = valuesFor(state.metamask.unapprovedMsgs)
const pending = pendingTxs.concat(pendingMsgs)
return {
accounts: state.metamask.accounts,
identities: state.metamask.identities,
- unconfTxs: state.metamask.unconfTxs,
+ unapprovedTxs: state.metamask.unapprovedTxs,
selectedAddress: state.metamask.selectedAddress,
- currentDomain: state.appState.currentDomain,
scrollToBottom: state.appState.scrollToBottom,
pending,
+ keyrings: state.metamask.keyrings,
}
}
@@ -31,14 +32,11 @@ function AccountsScreen () {
}
AccountsScreen.prototype.render = function () {
- var state = this.props
- var identityList = valuesFor(state.identities)
- var unconfTxList = valuesFor(state.unconfTxs)
- var actions = {
- onSelect: this.onSelect.bind(this),
- onShowDetail: this.onShowDetail.bind(this),
- goHome: this.goHome.bind(this),
- }
+ const props = this.props
+ const { keyrings } = props
+ const identityList = valuesFor(props.identities)
+ const unapprovedTxList = valuesFor(props.unapprovedTxs)
+
return (
h('.accounts-section.flex-grow', [
@@ -46,7 +44,7 @@ AccountsScreen.prototype.render = function () {
// subtitle and nav
h('.section-title.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
- onClick: actions.goHome,
+ onClick: this.goHome.bind(this),
}),
h('h2.page-subtitle', 'Select Account'),
]),
@@ -54,7 +52,7 @@ AccountsScreen.prototype.render = function () {
h('hr.horizontal-line'),
// identity selection
- h('section.identity-section.flex-column', {
+ h('section.identity-section', {
style: {
height: '418px',
overflowY: 'auto',
@@ -73,6 +71,12 @@ AccountsScreen.prototype.render = function () {
}
})
+ const simpleAddress = identity.address.substring(2).toLowerCase()
+ const keyring = keyrings.find((kr) => {
+ return kr.accounts.includes(simpleAddress) ||
+ kr.accounts.includes(identity.address)
+ })
+
return h(AccountListItem, {
key: `acct-panel-${identity.address}`,
identity,
@@ -80,18 +84,18 @@ AccountsScreen.prototype.render = function () {
accounts: this.props.accounts,
onShowDetail: this.onShowDetail.bind(this),
pending,
+ keyring,
})
}),
- h('hr.horizontal-line', {key: 'horizontal-line1'}),
+ h('hr.horizontal-line'),
h('div.footer.hover-white.pointer', {
key: 'reveal-account-bar',
onClick: () => {
- this.onRevealAccount()
+ this.addNewAccount()
},
style: {
display: 'flex',
- flex: '1 0 auto',
height: '40px',
paddint: '10px',
justifyContent: 'center',
@@ -100,9 +104,10 @@ AccountsScreen.prototype.render = function () {
}, [
h('i.fa.fa-plus.fa-lg', {key: ''}),
]),
+ h('hr.horizontal-line'),
]),
- unconfTxList.length ? (
+ unapprovedTxList.length ? (
h('.unconftx-link.flex-row.flex-center', {
onClick: this.navigateToConfTx.bind(this),
@@ -134,21 +139,21 @@ AccountsScreen.prototype.navigateToConfTx = function () {
this.props.dispatch(actions.showConfTxPage())
}
-AccountsScreen.prototype.onSelect = function (address, event) {
- event.stopPropagation()
- // if already selected, deselect
- if (this.props.selectedAddress === address) address = null
- this.props.dispatch(actions.setSelectedAddress(address))
-}
-
AccountsScreen.prototype.onShowDetail = function (address, event) {
event.stopPropagation()
this.props.dispatch(actions.showAccountDetail(address))
}
-AccountsScreen.prototype.onRevealAccount = function () {
- this.props.dispatch(actions.revealAccount())
+AccountsScreen.prototype.addNewAccount = function () {
+ this.props.dispatch(actions.addNewAccount(0))
+}
+
+/* An optional view proposed in this design:
+ * https://consensys.quip.com/zZVrAysM5znY
+AccountsScreen.prototype.addNewAccount = function () {
+ this.props.dispatch(actions.navigateToNewAccountScreen())
}
+*/
AccountsScreen.prototype.goHome = function () {
this.props.dispatch(actions.goHome())
diff --git a/ui/app/actions.js b/ui/app/actions.js
index c6c932296..6552e7f5c 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -1,32 +1,50 @@
var actions = {
+ _setBackgroundConnection: _setBackgroundConnection,
+
GO_HOME: 'GO_HOME',
goHome: goHome,
// menu state
getNetworkStatus: 'getNetworkStatus',
-
+ // transition state
+ TRANSITION_FORWARD: 'TRANSITION_FORWARD',
+ TRANSITION_BACKWARD: 'TRANSITION_BACKWARD',
+ transitionForward,
+ transitionBackward,
// remote state
UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE',
updateMetamaskState: updateMetamaskState,
+ // notices
+ MARK_NOTICE_READ: 'MARK_NOTICE_READ',
+ markNoticeRead: markNoticeRead,
+ SHOW_NOTICE: 'SHOW_NOTICE',
+ showNotice: showNotice,
+ CLEAR_NOTICES: 'CLEAR_NOTICES',
+ clearNotices: clearNotices,
+ markAccountsFound,
// intialize screen
- AGREE_TO_DISCLAIMER: 'AGREE_TO_DISCLAIMER',
- agreeToDisclaimer: agreeToDisclaimer,
CREATE_NEW_VAULT_IN_PROGRESS: 'CREATE_NEW_VAULT_IN_PROGRESS',
SHOW_CREATE_VAULT: 'SHOW_CREATE_VAULT',
SHOW_RESTORE_VAULT: 'SHOW_RESTORE_VAULT',
+ FORGOT_PASSWORD: 'FORGOT_PASSWORD',
+ forgotPassword: forgotPassword,
SHOW_INIT_MENU: 'SHOW_INIT_MENU',
SHOW_NEW_VAULT_SEED: 'SHOW_NEW_VAULT_SEED',
SHOW_INFO_PAGE: 'SHOW_INFO_PAGE',
- RECOVER_FROM_SEED: 'RECOVER_FROM_SEED',
- CLEAR_SEED_WORD_CACHE: 'CLEAR_SEED_WORD_CACHE',
- clearSeedWordCache: clearSeedWordCache,
- recoverFromSeed: recoverFromSeed,
+ SHOW_IMPORT_PAGE: 'SHOW_IMPORT_PAGE',
unlockMetamask: unlockMetamask,
unlockFailed: unlockFailed,
showCreateVault: showCreateVault,
showRestoreVault: showRestoreVault,
showInitializeMenu: showInitializeMenu,
- createNewVault: createNewVault,
+ showImportPage,
+ createNewVaultAndKeychain: createNewVaultAndKeychain,
+ createNewVaultAndRestore: createNewVaultAndRestore,
createNewVaultInProgress: createNewVaultInProgress,
+ addNewKeyring,
+ importNewAccount,
+ addNewAccount,
+ NEW_ACCOUNT_SCREEN: 'NEW_ACCOUNT_SCREEN',
+ navigateToNewAccountScreen,
showNewVaultSeed: showNewVaultSeed,
showInfoPage: showInfoPage,
// seed recovery actions
@@ -43,7 +61,6 @@ var actions = {
unlockInProgress: unlockInProgress,
// error handling
displayWarning: displayWarning,
- showWarning: showWarning, // alias
DISPLAY_WARNING: 'DISPLAY_WARNING',
HIDE_WARNING: 'HIDE_WARNING',
hideWarning: hideWarning,
@@ -53,8 +70,6 @@ var actions = {
SHOW_ACCOUNTS_PAGE: 'SHOW_ACCOUNTS_PAGE',
SHOW_CONF_TX_PAGE: 'SHOW_CONF_TX_PAGE',
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
- REVEAL_ACCOUNT: 'REVEAL_ACCOUNT',
- revealAccount: revealAccount,
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
setCurrentFiat: setCurrentFiat,
// account detail screen
@@ -68,16 +83,11 @@ var actions = {
showPrivateKey: showPrivateKey,
SAVE_ACCOUNT_LABEL: 'SAVE_ACCOUNT_LABEL',
saveAccountLabel: saveAccountLabel,
- AGREE_TO_ETH_WARNING: 'AGREE_TO_ETH_WARNING',
- agreeToEthWarning: agreeToEthWarning,
- SHOW_ETH_WARNING: 'SHOW_ETH_WARNING',
- showEthWarning: showEthWarning,
// tx conf screen
COMPLETED_TX: 'COMPLETED_TX',
TRANSACTION_ERROR: 'TRANSACTION_ERROR',
NEXT_TX: 'NEXT_TX',
PREVIOUS_TX: 'PREV_TX',
- setSelectedAddress: setSelectedAddress,
signMsg: signMsg,
cancelMsg: cancelMsg,
sendTx: sendTx,
@@ -90,12 +100,12 @@ var actions = {
viewPendingTx: viewPendingTx,
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
// app messages
+ confirmSeedWords: confirmSeedWords,
showAccountDetail: showAccountDetail,
BACK_TO_ACCOUNT_DETAIL: 'BACK_TO_ACCOUNT_DETAIL',
backToAccountDetail: backToAccountDetail,
showAccountsPage: showAccountsPage,
showConfTxPage: showConfTxPage,
- confirmSeedWords: confirmSeedWords,
// config screen
SHOW_CONFIG_PAGE: 'SHOW_CONFIG_PAGE',
SET_RPC_TARGET: 'SET_RPC_TARGET',
@@ -105,8 +115,6 @@ var actions = {
showConfigPage: showConfigPage,
setRpcTarget: setRpcTarget,
setProviderType: setProviderType,
- // hacky - need a way to get a reference to account manager
- _setAccountManager: _setAccountManager,
// loading overlay
SHOW_LOADING: 'SHOW_LOADING_INDICATION',
HIDE_LOADING: 'HIDE_LOADING_INDICATION',
@@ -134,16 +142,28 @@ var actions = {
hideSubLoadingIndication: hideSubLoadingIndication,
// QR STUFF:
SHOW_QR: 'SHOW_QR',
- getQr: getQr,
+ showQrView: showQrView,
reshowQrCode: reshowQrCode,
SHOW_QR_VIEW: 'SHOW_QR_VIEW',
+// FORGOT PASSWORD:
+ BACK_TO_INIT_MENU: 'BACK_TO_INIT_MENU',
+ goBackToInitView: goBackToInitView,
+ RECOVERY_IN_PROGRESS: 'RECOVERY_IN_PROGRESS',
+ BACK_TO_UNLOCK_VIEW: 'BACK_TO_UNLOCK_VIEW',
+ backToUnlockView: backToUnlockView,
+ // SHOWING KEYCHAIN
+ SHOW_NEW_KEYCHAIN: 'SHOW_NEW_KEYCHAIN',
+ showNewKeychain: showNewKeychain,
+
+ callBackgroundThenUpdate,
+ forceUpdateMetamaskState,
}
module.exports = actions
-var _accountManager = null
-function _setAccountManager (accountManager) {
- _accountManager = accountManager
+var background = null
+function _setBackgroundConnection (backgroundConnection) {
+ background = backgroundConnection
}
function goHome () {
@@ -156,25 +176,77 @@ function goHome () {
function tryUnlockMetamask (password) {
return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
dispatch(actions.unlockInProgress())
- _accountManager.submitPassword(password, (err, selectedAccount) => {
+ if (global.METAMASK_DEBUG) console.log(`background.submitPassword`)
+ background.submitPassword(password, (err) => {
+ dispatch(actions.hideLoadingIndication())
if (err) {
- dispatch(actions.unlockFailed())
+ dispatch(actions.unlockFailed(err.message))
} else {
- dispatch(actions.unlockMetamask(selectedAccount))
+ dispatch(actions.transitionForward())
+ forceUpdateMetamaskState(dispatch)
+ }
+ })
+ }
+}
+
+function transitionForward () {
+ return {
+ type: this.TRANSITION_FORWARD,
+ }
+}
+
+function transitionBackward () {
+ return {
+ type: this.TRANSITION_BACKWARD,
+ }
+}
+
+function confirmSeedWords () {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
+ if (global.METAMASK_DEBUG) console.log(`background.clearSeedWordCache`)
+ background.clearSeedWordCache((err, account) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
}
+
+ console.log('Seed word cache cleared. ' + account)
+ dispatch(actions.showAccountDetail(account))
+ })
+ }
+}
+
+function createNewVaultAndRestore (password, seed) {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
+ if (global.METAMASK_DEBUG) console.log(`background.createNewVaultAndRestore`)
+ background.createNewVaultAndRestore(password, seed, (err) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) return dispatch(actions.displayWarning(err.message))
+ dispatch(actions.showAccountsPage())
})
}
}
-function createNewVault (password, entropy) {
+function createNewVaultAndKeychain (password) {
return (dispatch) => {
- dispatch(actions.createNewVaultInProgress())
- _accountManager.createNewVault(password, entropy, (err, result) => {
+ dispatch(actions.showLoadingIndication())
+ if (global.METAMASK_DEBUG) console.log(`background.createNewVaultAndKeychain`)
+ background.createNewVaultAndKeychain(password, (err) => {
if (err) {
- return dispatch(actions.showWarning(err.message))
+ return dispatch(actions.displayWarning(err.message))
}
- dispatch(actions.showNewVaultSeed(result))
+ if (global.METAMASK_DEBUG) console.log(`background.placeSeedWords`)
+ background.placeSeedWords((err) => {
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
+ dispatch(actions.hideLoadingIndication())
+ forceUpdateMetamaskState(dispatch)
+ })
})
}
}
@@ -188,61 +260,79 @@ function revealSeedConfirmation () {
function requestRevealSeed (password) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- _accountManager.tryPassword(password, (err, seed) => {
- dispatch(actions.hideLoadingIndication())
+ if (global.METAMASK_DEBUG) console.log(`background.submitPassword`)
+ background.submitPassword(password, (err) => {
if (err) return dispatch(actions.displayWarning(err.message))
- _accountManager.recoverSeed((err, seed) => {
+ if (global.METAMASK_DEBUG) console.log(`background.placeSeedWords`)
+ background.placeSeedWords((err) => {
if (err) return dispatch(actions.displayWarning(err.message))
- dispatch(actions.showNewVaultSeed(seed))
+ dispatch(actions.hideLoadingIndication())
})
})
}
}
-function recoverFromSeed (password, seed) {
+function addNewKeyring (type, opts) {
return (dispatch) => {
- // dispatch(actions.createNewVaultInProgress())
dispatch(actions.showLoadingIndication())
- _accountManager.recoverFromSeed(password, seed, (err, metamaskState) => {
+ if (global.METAMASK_DEBUG) console.log(`background.addNewKeyring`)
+ background.addNewKeyring(type, opts, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) return dispatch(actions.displayWarning(err.message))
+ dispatch(actions.showAccountsPage())
+ })
+ }
+}
- var account = Object.keys(metamaskState.identities)[0]
- dispatch(actions.unlockMetamask(account))
+function importNewAccount (strategy, args) {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication('This may take a while, be patient.'))
+ if (global.METAMASK_DEBUG) console.log(`background.importAccountWithStrategy`)
+ background.importAccountWithStrategy(strategy, args, (err) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) return dispatch(actions.displayWarning(err.message))
+ if (global.METAMASK_DEBUG) console.log(`background.getState`)
+ background.getState((err, newState) => {
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
+ dispatch(actions.updateMetamaskState(newState))
+ dispatch({
+ type: actions.SHOW_ACCOUNT_DETAIL,
+ value: newState.selectedAddress,
+ })
+ })
})
}
}
-function showInfoPage () {
+function navigateToNewAccountScreen() {
return {
- type: actions.SHOW_INFO_PAGE,
+ type: this.NEW_ACCOUNT_SCREEN,
}
}
-function setSelectedAddress (address) {
- return (dispatch) => {
- _accountManager.setSelectedAddress(address)
- }
+function addNewAccount () {
+ if (global.METAMASK_DEBUG) console.log(`background.addNewAccount`)
+ return callBackgroundThenUpdate(background.addNewAccount)
}
-function revealAccount () {
- return (dispatch) => {
- dispatch(actions.showLoadingIndication())
- _accountManager.revealAccount((err) => {
- dispatch(actions.hideLoadingIndication())
- if (err) return dispatch(actions.displayWarning(err.message))
- dispatch({
- type: actions.REVEAL_ACCOUNT,
- })
- })
+function showInfoPage () {
+ return {
+ type: actions.SHOW_INFO_PAGE,
}
}
-function setCurrentFiat (fiat) {
+function setCurrentFiat (currencyCode) {
return (dispatch) => {
dispatch(this.showLoadingIndication())
- _accountManager.setCurrentFiat(fiat, (data, err) => {
+ if (global.METAMASK_DEBUG) console.log(`background.setCurrentFiat`)
+ background.setCurrentCurrency(currencyCode, (err, data) => {
dispatch(this.hideLoadingIndication())
+ if (err) {
+ console.error(err.stack)
+ return dispatch(actions.displayWarning(err.message))
+ }
dispatch({
type: this.SET_CURRENT_FIAT,
value: {
@@ -259,7 +349,8 @@ function signMsg (msgData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- _accountManager.signMessage(msgData, (err) => {
+ if (global.METAMASK_DEBUG) console.log(`background.signMessage`)
+ background.signMessage(msgData, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) return dispatch(actions.displayWarning(err.message))
@@ -270,23 +361,25 @@ function signMsg (msgData) {
function signTx (txData) {
return (dispatch) => {
- dispatch(actions.showLoadingIndication())
-
- web3.eth.sendTransaction(txData, (err, data) => {
- dispatch(actions.hideLoadingIndication())
-
+ if (global.METAMASK_DEBUG) console.log(`background.setGasMultiplier`)
+ background.setGasMultiplier(txData.gasMultiplier, (err) => {
if (err) return dispatch(actions.displayWarning(err.message))
- dispatch(actions.hideWarning())
- dispatch(actions.goHome())
+ web3.eth.sendTransaction(txData, (err, data) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) return dispatch(actions.displayWarning(err.message))
+ dispatch(actions.hideWarning())
+ dispatch(actions.goHome())
+ })
+ dispatch(this.showConfTxPage())
})
}
}
function sendTx (txData) {
return (dispatch) => {
- _accountManager.approveTransaction(txData.id, (err) => {
+ if (global.METAMASK_DEBUG) console.log(`background.approveTransaction`)
+ background.approveTransaction(txData.id, (err) => {
if (err) {
- alert(err.message)
dispatch(actions.txError(err))
return console.error(err.message)
}
@@ -310,12 +403,14 @@ function txError (err) {
}
function cancelMsg (msgData) {
- _accountManager.cancelMessage(msgData.id)
+ if (global.METAMASK_DEBUG) console.log(`background.cancelMessage`)
+ background.cancelMessage(msgData.id)
return actions.completedTx(msgData.id)
}
function cancelTx (txData) {
- _accountManager.cancelTransaction(txData.id)
+ if (global.METAMASK_DEBUG) console.log(`background.cancelTransaction`)
+ background.cancelTransaction(txData.id)
return actions.completedTx(txData.id)
}
@@ -335,25 +430,21 @@ function showRestoreVault () {
}
}
+function forgotPassword () {
+ return {
+ type: actions.FORGOT_PASSWORD,
+ }
+}
+
function showInitializeMenu () {
return {
type: actions.SHOW_INIT_MENU,
}
}
-function agreeToDisclaimer () {
- return (dispatch) => {
- dispatch(this.showLoadingIndication())
- _accountManager.agreeToDisclaimer((err) => {
- if (err) {
- return dispatch(actions.showWarning(err.message))
- }
-
- dispatch(this.hideLoadingIndication())
- dispatch({
- type: this.AGREE_TO_DISCLAIMER,
- })
- })
+function showImportPage () {
+ return {
+ type: actions.SHOW_IMPORT_PAGE,
}
}
@@ -370,6 +461,18 @@ function showNewVaultSeed (seed) {
}
}
+function backToUnlockView () {
+ return {
+ type: actions.BACK_TO_UNLOCK_VIEW,
+ }
+}
+
+function showNewKeychain () {
+ return {
+ type: actions.SHOW_NEW_KEYCHAIN,
+ }
+}
+
//
// unlock screen
//
@@ -380,9 +483,10 @@ function unlockInProgress () {
}
}
-function unlockFailed () {
+function unlockFailed (message) {
return {
type: actions.UNLOCK_FAILED,
+ value: message,
}
}
@@ -401,29 +505,19 @@ function updateMetamaskState (newState) {
}
function lockMetamask () {
- return (dispatch) => {
- _accountManager.setLocked((err) => {
- dispatch(actions.hideLoadingIndication())
- if (err) {
- return dispatch(actions.showWarning(err.message))
- }
-
- dispatch({
- type: actions.LOCK_METAMASK,
- })
- })
- }
+ if (global.METAMASK_DEBUG) console.log(`background.setLocked`)
+ return callBackgroundThenUpdate(background.setLocked)
}
function showAccountDetail (address) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- _accountManager.setSelectedAddress(address, (err, address) => {
+ if (global.METAMASK_DEBUG) console.log(`background.setSelectedAddress`)
+ background.setSelectedAddress(address, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) {
- return dispatch(actions.showWarning(err.message))
+ return dispatch(actions.displayWarning(err.message))
}
-
dispatch({
type: actions.SHOW_ACCOUNT_DETAIL,
value: address,
@@ -438,27 +532,6 @@ function backToAccountDetail (address) {
value: address,
}
}
-function clearSeedWordCache (account) {
- return {
- type: actions.CLEAR_SEED_WORD_CACHE,
- value: account,
- }
-}
-
-function confirmSeedWords () {
- return (dispatch) => {
- dispatch(actions.showLoadingIndication())
- _accountManager.clearSeedWordCache((err, account) => {
- dispatch(actions.hideLoadingIndication())
- if (err) {
- return dispatch(actions.showWarning(err.message))
- }
-
- console.log('Seed word cache cleared. ' + account)
- dispatch(actions.showAccountDetail(account))
- })
- }
-}
function showAccountsPage () {
return {
@@ -466,9 +539,10 @@ function showAccountsPage () {
}
}
-function showConfTxPage () {
+function showConfTxPage (transForward = true) {
return {
type: actions.SHOW_CONF_TX_PAGE,
+ transForward: transForward,
}
}
@@ -498,12 +572,62 @@ function showConfigPage (transitionForward = true) {
}
}
+function goBackToInitView () {
+ return {
+ type: actions.BACK_TO_INIT_MENU,
+ }
+}
+
+//
+// notice
+//
+
+function markNoticeRead (notice) {
+ return (dispatch) => {
+ dispatch(this.showLoadingIndication())
+ if (global.METAMASK_DEBUG) console.log(`background.markNoticeRead`)
+ background.markNoticeRead(notice, (err, notice) => {
+ dispatch(this.hideLoadingIndication())
+ if (err) {
+ return dispatch(actions.displayWarning(err))
+ }
+ if (notice) {
+ return dispatch(actions.showNotice(notice))
+ } else {
+ dispatch(this.clearNotices())
+ return {
+ type: actions.SHOW_ACCOUNTS_PAGE,
+ }
+ }
+ })
+ }
+}
+
+function showNotice (notice) {
+ return {
+ type: actions.SHOW_NOTICE,
+ value: notice,
+ }
+}
+
+function clearNotices () {
+ return {
+ type: actions.CLEAR_NOTICES,
+ }
+}
+
+function markAccountsFound() {
+ if (global.METAMASK_DEBUG) console.log(`background.markAccountsFound`)
+ return callBackgroundThenUpdate(background.markAccountsFound)
+}
+
//
// config
//
function setRpcTarget (newRpc) {
- _accountManager.setRpcTarget(newRpc)
+ if (global.METAMASK_DEBUG) console.log(`background.setRpcTarget`)
+ background.setRpcTarget(newRpc)
return {
type: actions.SET_RPC_TARGET,
value: newRpc,
@@ -511,7 +635,8 @@ function setRpcTarget (newRpc) {
}
function setProviderType (type) {
- _accountManager.setProviderType(type)
+ if (global.METAMASK_DEBUG) console.log(`background.setProviderType`)
+ background.setProviderType(type)
return {
type: actions.SET_PROVIDER_TYPE,
value: type,
@@ -519,15 +644,17 @@ function setProviderType (type) {
}
function useEtherscanProvider () {
- _accountManager.useEtherscanProvider()
+ if (global.METAMASK_DEBUG) console.log(`background.useEtherscanProvider`)
+ background.useEtherscanProvider()
return {
type: actions.USE_ETHERSCAN_PROVIDER,
}
}
-function showLoadingIndication () {
+function showLoadingIndication (message) {
return {
type: actions.SHOW_LOADING,
+ value: message,
}
}
@@ -549,10 +676,6 @@ function hideSubLoadingIndication () {
}
}
-function showWarning (text) {
- return this.displayWarning(text)
-}
-
function displayWarning (text) {
return {
type: actions.DISPLAY_WARNING,
@@ -578,7 +701,8 @@ function exportAccount (address) {
return function (dispatch) {
dispatch(self.showLoadingIndication())
- _accountManager.exportAccount(address, function (err, result) {
+ if (global.METAMASK_DEBUG) console.log(`background.exportAccount`)
+ background.exportAccount(address, function (err, result) {
dispatch(self.hideLoadingIndication())
if (err) {
@@ -601,10 +725,11 @@ function showPrivateKey (key) {
function saveAccountLabel (account, label) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
- _accountManager.saveAccountLabel(account, label, (err) => {
+ if (global.METAMASK_DEBUG) console.log(`background.saveAccountLabel`)
+ background.saveAccountLabel(account, label, (err) => {
dispatch(actions.hideLoadingIndication())
if (err) {
- return dispatch(actions.showWarning(err.message))
+ return dispatch(actions.displayWarning(err.message))
}
dispatch({
type: actions.SAVE_ACCOUNT_LABEL,
@@ -620,28 +745,10 @@ function showSendPage () {
}
}
-function agreeToEthWarning () {
- return (dispatch) => {
- _accountManager.agreeToEthWarning((err) => {
- if (err) {
- return dispatch(actions.showEthWarning(err.message))
- }
- dispatch({
- type: actions.AGREE_TO_ETH_WARNING,
- })
- })
- }
-}
-
-function showEthWarning () {
- return {
- type: actions.SHOW_ETH_WARNING,
- }
-}
-
function buyEth (address, amount) {
return (dispatch) => {
- _accountManager.buyEth(address, amount)
+ if (global.METAMASK_DEBUG) console.log(`background.buyEth`)
+ background.buyEth(address, amount)
dispatch({
type: actions.BUY_ETH,
})
@@ -699,7 +806,7 @@ function shapeShiftSubview (network) {
shapeShiftRequest('marketinfo', {pair}, (mktResponse) => {
shapeShiftRequest('getcoins', {}, (response) => {
dispatch(actions.hideSubLoadingIndication())
- if (mktResponse.error) return dispatch(actions.showWarning(mktResponse.error))
+ if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error))
dispatch({
type: actions.SHAPESHIFT_SUBVIEW,
value: {
@@ -716,36 +823,31 @@ function coinShiftRquest (data, marketData) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
shapeShiftRequest('shift', { method: 'POST', data}, (response) => {
- if (response.error) return dispatch(actions.showWarning(response.error))
+ dispatch(actions.hideLoadingIndication())
+ if (response.error) return dispatch(actions.displayWarning(response.error))
var message = `
Deposit your ${response.depositType} to the address bellow:`
- _accountManager.createShapeShiftTx(response.deposit, response.depositType)
- dispatch(actions.getQr(response.deposit, '125x125', [message].concat(marketData)))
+ if (global.METAMASK_DEBUG) console.log(`background.createShapeShiftTx`)
+ background.createShapeShiftTx(response.deposit, response.depositType)
+ dispatch(actions.showQrView(response.deposit, [message].concat(marketData)))
})
}
}
-function getQr (data, size, message) {
- return (dispatch) => {
- qrRequest(data, size, (response) => {
- dispatch(actions.hideLoadingIndication())
- if (response.error) return dispatch(actions.showWarning(response.error))
- dispatch({
- type: actions.SHOW_QR,
- value: {
- qr: response,
- message: message,
- data: data,
- },
- })
- })
+function showQrView (data, message) {
+ return {
+ type: actions.SHOW_QR_VIEW,
+ value: {
+ message: message,
+ data: data,
+ },
}
}
function reshowQrCode (data, coin) {
return (dispatch) => {
dispatch(actions.showLoadingIndication())
shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => {
- if (mktResponse.error) return dispatch(actions.showWarning(mktResponse.error))
+ if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error))
var message = [
`Deposit your ${coin} to the address bellow:`,
@@ -753,17 +855,8 @@ function reshowQrCode (data, coin) {
`Deposit Minimum:${mktResponse.minimum}`,
]
- qrRequest(data, '125x125', (response) => {
- dispatch(actions.hideLoadingIndication())
- dispatch({
- type: actions.SHOW_QR_VIEW,
- value: {
- qr: response,
- message: message,
- data: data,
- },
- })
- })
+ dispatch(actions.hideLoadingIndication())
+ return dispatch(actions.showQrView(data, message))
})
}
}
@@ -792,14 +885,33 @@ function shapeShiftRequest (query, options, cb) {
}
}
-function qrRequest (data, size, cb) {
- var requestListner = function (request) {
- cb ? cb(this.responseText) : null
- return this.responseText
+// Call Background Then Update
+//
+// A function generator for a common pattern wherein:
+// We show loading indication.
+// We call a background method.
+// We hide loading indication.
+// If it errored, we show a warning.
+// If it didn't, we update the state.
+function callBackgroundThenUpdate (method, ...args) {
+ return (dispatch) => {
+ dispatch(actions.showLoadingIndication())
+ method.call(background, ...args, (err) => {
+ dispatch(actions.hideLoadingIndication())
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
+ forceUpdateMetamaskState(dispatch)
+ })
}
+}
- var qrReq = new XMLHttpRequest()
- qrReq.addEventListener('load', requestListner)
- qrReq.open('GET', `https://api.qrserver.com/v1/create-qr-code/?size=${size}&format=svg&data=${data}`, true)
- qrReq.send()
+function forceUpdateMetamaskState(dispatch){
+ if (global.METAMASK_DEBUG) console.log(`background.getState`)
+ background.getState((err, newState) => {
+ if (err) {
+ return dispatch(actions.displayWarning(err.message))
+ }
+ dispatch(actions.updateMetamaskState(newState))
+ })
}
diff --git a/ui/app/app.js b/ui/app/app.js
index 84ff16ec8..8aae38067 100644
--- a/ui/app/app.js
+++ b/ui/app/app.js
@@ -5,11 +5,8 @@ const h = require('react-hyperscript')
const actions = require('./actions')
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
// init
-const DisclaimerScreen = require('./first-time/disclaimer')
const InitializeMenuScreen = require('./first-time/init-menu')
-const CreateVaultScreen = require('./first-time/create-vault')
-const CreateVaultCompleteScreen = require('./first-time/create-vault-complete')
-const RestoreVaultScreen = require('./first-time/restore-vault')
+const NewKeyChainScreen = require('./new-keychain')
// unlock
const UnlockScreen = require('./unlock')
// accounts
@@ -17,19 +14,25 @@ const AccountsScreen = require('./accounts')
const AccountDetailScreen = require('./account-detail')
const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx')
+// notice
+const NoticeScreen = require('./components/notice')
+const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
// other views
const ConfigScreen = require('./config')
-const RevealSeedConfirmation = require('./recover-seed/confirmation')
+const Import = require('./accounts/import')
const InfoScreen = require('./info')
-const LoadingIndicator = require('./loading')
+const LoadingIndicator = require('./components/loading')
const SandwichExpando = require('sandwich-expando')
const MenuDroppo = require('menu-droppo')
const DropMenuItem = require('./components/drop-menu-item')
const NetworkIndicator = require('./components/network')
const Tooltip = require('./components/tooltip')
-const EthStoreWarning = require('./eth-store-warning')
const BuyView = require('./components/buy-button-subview')
const QrView = require('./components/qr-code')
+const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
+const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
+const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
+
module.exports = connect(mapStateToProps)(App)
inherits(App, Component)
@@ -38,25 +41,29 @@ function App () { Component.call(this) }
function mapStateToProps (state) {
return {
// state from plugin
- isConfirmed: state.metamask.isConfirmed,
- isEthConfirmed: state.metamask.isEthConfirmed,
+ isLoading: state.appState.isLoading,
+ loadingMessage: state.appState.loadingMessage,
+ noActiveNotices: state.metamask.noActiveNotices,
isInitialized: state.metamask.isInitialized,
isUnlocked: state.metamask.isUnlocked,
currentView: state.appState.currentView,
activeAddress: state.appState.activeAddress,
transForward: state.appState.transForward,
seedWords: state.metamask.seedWords,
- unconfTxs: state.metamask.unconfTxs,
- unconfMsgs: state.metamask.unconfMsgs,
+ unapprovedTxs: state.metamask.unapprovedTxs,
+ unapprovedMsgs: state.metamask.unapprovedMsgs,
menuOpen: state.appState.menuOpen,
network: state.metamask.network,
provider: state.metamask.provider,
+ forgottenPassword: state.appState.forgottenPassword,
+ lastUnreadNotice: state.metamask.lastUnreadNotice,
+ lostAccounts: state.metamask.lostAccounts,
}
}
App.prototype.render = function () {
var props = this.props
- var transForward = props.transForward
+ const { isLoading, loadingMessage, transForward } = props
return (
@@ -68,7 +75,7 @@ App.prototype.render = function () {
},
}, [
- h(LoadingIndicator),
+ h(LoadingIndicator, { isLoading, loadingMessage }),
// app bar
this.renderAppBar(),
@@ -96,6 +103,10 @@ App.prototype.render = function () {
}
App.prototype.renderAppBar = function () {
+ if (window.METAMASK_UI_TYPE === 'notification') {
+ return null
+ }
+
const props = this.props
const state = this.state || {}
const isNetworkMenuOpen = state.isNetworkMenuOpen || false
@@ -130,15 +141,21 @@ App.prototype.renderAppBar = function () {
src: '/images/icon-128.png',
}),
- h(NetworkIndicator, {
- network: this.props.network,
- provider: this.props.provider,
- onClick: (event) => {
- event.preventDefault()
- event.stopPropagation()
- this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
+ h('#network-spacer.flex-center', {
+ style: {
+ marginRight: '-72px',
},
- }),
+ }, [
+ h(NetworkIndicator, {
+ network: this.props.network,
+ provider: this.props.provider,
+ onClick: (event) => {
+ event.preventDefault()
+ event.stopPropagation()
+ this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
+ },
+ }),
+ ]),
]),
// metamask name
@@ -227,22 +244,31 @@ App.prototype.renderNetworkDropdown = function () {
}),
h(DropMenuItem, {
- label: 'Morden Test Network',
+ label: 'Ropsten Test Network',
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => props.dispatch(actions.setProviderType('testnet')),
icon: h('.menu-icon.red-dot'),
activeNetworkRender: props.network,
+ provider: props.provider,
}),
h(DropMenuItem, {
label: 'Localhost 8545',
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
action: () => props.dispatch(actions.setRpcTarget('http://localhost:8545')),
- icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
+ icon: h('i.fa.fa-question-circle.fa-lg'),
activeNetworkRender: props.provider.rpcTarget,
}),
- this.renderCustomOption(props.provider.rpcTarget),
+ this.renderCustomOption(props.provider),
+
+ h(DropMenuItem, {
+ label: 'Custom RPC',
+ closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
+ action: () => this.props.dispatch(actions.showConfigPage()),
+ icon: h('i.fa.fa-question-circle.fa-lg'),
+ }),
+
])
}
@@ -275,65 +301,100 @@ App.prototype.renderDropdown = function () {
label: 'Settings',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.showConfigPage()),
- icon: h('i.fa.fa-gear.fa-lg', { ariaHidden: true }),
+ icon: h('i.fa.fa-gear.fa-lg'),
+ }),
+
+ h(DropMenuItem, {
+ label: 'Import Account',
+ closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
+ action: () => this.props.dispatch(actions.showImportPage()),
+ icon: h('i.fa.fa-arrow-circle-o-up.fa-lg'),
}),
h(DropMenuItem, {
label: 'Lock',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.lockMetamask()),
- icon: h('i.fa.fa-lock.fa-lg', { ariaHidden: true }),
+ icon: h('i.fa.fa-lock.fa-lg'),
}),
h(DropMenuItem, {
- label: 'Help',
+ label: 'Info',
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
action: () => this.props.dispatch(actions.showInfoPage()),
- icon: h('i.fa.fa-question.fa-lg', { ariaHidden: true }),
+ icon: h('i.fa.fa-question.fa-lg'),
}),
])
}
+App.prototype.renderBackButton = function (style, justArrow = false) {
+ var props = this.props
+ return (
+ h('.flex-row', {
+ key: 'leftArrow',
+ style: style,
+ onClick: () => props.dispatch(actions.goBackToInitView()),
+ }, [
+ h('i.fa.fa-arrow-left.cursor-pointer'),
+ justArrow ? null : h('div.cursor-pointer', {
+ style: {
+ marginLeft: '3px',
+ },
+ onClick: () => props.dispatch(actions.goBackToInitView()),
+ }, 'BACK'),
+ ])
+ )
+}
+
App.prototype.renderPrimary = function () {
var props = this.props
- if (!props.isConfirmed) {
- return h(DisclaimerScreen, {key: 'disclaimerScreen'})
+ // notices
+ if (!props.noActiveNotices && !global.METAMASK_DEBUG) {
+ return h(NoticeScreen, {
+ notice: props.lastUnreadNotice,
+ key: 'NoticeScreen',
+ onConfirm: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
+ })
+ } else if (props.lostAccounts && props.lostAccounts.length > 0) {
+ return h(NoticeScreen, {
+ notice: generateLostAccountsNotice(props.lostAccounts),
+ key: 'LostAccountsNotice',
+ onConfirm: () => props.dispatch(actions.markAccountsFound()),
+ })
}
if (props.seedWords) {
- return h(CreateVaultCompleteScreen, {key: 'createVaultComplete'})
+ return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
}
// show initialize screen
- if (!props.isInitialized) {
+ if (!props.isInitialized || props.forgottenPassword) {
// show current view
switch (props.currentView.name) {
- case 'createVault':
- return h(CreateVaultScreen, {key: 'createVault'})
-
case 'restoreVault':
- return h(RestoreVaultScreen, {key: 'restoreVault'})
-
- case 'createVaultComplete':
- return h(CreateVaultCompleteScreen, {key: 'createVaultComplete'})
+ return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
default:
return h(InitializeMenuScreen, {key: 'menuScreenInit'})
-
}
}
// show unlock screen
if (!props.isUnlocked) {
- return h(UnlockScreen, {key: 'locked'})
+ switch (props.currentView.name) {
+
+ case 'restoreVault':
+ return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
+
+ default:
+ return h(UnlockScreen, {key: 'locked'})
+ }
}
// show current view
switch (props.currentView.name) {
- case 'EthStoreWarning':
- return h(EthStoreWarning, {key: 'ethWarning'})
case 'accounts':
return h(AccountsScreen, {key: 'accounts'})
@@ -344,22 +405,27 @@ App.prototype.renderPrimary = function () {
case 'sendTransaction':
return h(SendTransactionScreen, {key: 'send-transaction'})
+ case 'newKeychain':
+ return h(NewKeyChainScreen, {key: 'new-keychain'})
+
case 'confTx':
return h(ConfirmTxScreen, {key: 'confirm-tx'})
case 'config':
return h(ConfigScreen, {key: 'config'})
+ case 'import-menu':
+ return h(Import, {key: 'import-menu'})
+
case 'reveal-seed-conf':
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
case 'info':
return h(InfoScreen, {key: 'info'})
- case 'createVault':
- return h(CreateVaultScreen, {key: 'createVault'})
case 'buyEth':
return h(BuyView, {key: 'buyEthView'})
+
case 'qr':
return h('div', {
style: {
@@ -379,7 +445,6 @@ App.prototype.renderPrimary = function () {
h('div', {
style: {
position: 'absolute',
- bottom: '115px',
left: '44px',
width: '285px',
},
@@ -405,28 +470,20 @@ App.prototype.toggleMetamaskActive = function () {
}
}
-App.prototype.renderCustomOption = function (rpcTarget) {
+App.prototype.renderCustomOption = function (provider) {
+ const { rpcTarget, type } = provider
+ if (type !== 'rpc') return null
+
switch (rpcTarget) {
- case undefined:
- return h(DropMenuItem, {
- label: 'Custom RPC',
- closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
- action: () => this.props.dispatch(actions.showConfigPage()),
- icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
- })
+
case 'http://localhost:8545':
- return h(DropMenuItem, {
- label: 'Custom RPC',
- closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
- action: () => this.props.dispatch(actions.showConfigPage()),
- icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
- })
+ return null
default:
return h(DropMenuItem, {
label: `${rpcTarget}`,
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
- icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
+ icon: h('i.fa.fa-question-circle.fa-lg'),
activeNetworkRender: 'custom',
})
}
diff --git a/ui/app/components/account-eth-balance.js b/ui/app/components/account-eth-balance.js
deleted file mode 100644
index 8d693685f..000000000
--- a/ui/app/components/account-eth-balance.js
+++ /dev/null
@@ -1,140 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const connect = require('react-redux').connect
-const formatBalance = require('../util').formatBalance
-const generateBalanceObject = require('../util').generateBalanceObject
-const Tooltip = require('./tooltip.js')
-
-module.exports = connect(mapStateToProps)(EthBalanceComponent)
-
-function mapStateToProps (state) {
- return {
- conversionRate: state.metamask.conversionRate,
- conversionDate: state.metamask.conversionDate,
- currentFiat: state.metamask.currentFiat,
- }
-}
-
-inherits(EthBalanceComponent, Component)
-function EthBalanceComponent () {
- Component.call(this)
-}
-
-EthBalanceComponent.prototype.render = function () {
- var state = this.props
- var style = state.style
-
- const value = formatBalance(state.value, 6)
- var width = state.width
-
- return (
-
- h('.ether-balance', {
- style: style,
- }, [
- h('.ether-balance-amount', {
- style: {
- display: 'inline',
- width: width,
- },
- }, this.renderBalance(value, state)),
- ])
-
- )
-}
-EthBalanceComponent.prototype.renderBalance = function (value, state) {
- if (value === 'None') return value
- var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
- var balance, fiatDisplayNumber, fiatTooltipNumber
- var splitBalance = value.split(' ')
- var ethNumber = splitBalance[0]
- var ethSuffix = splitBalance[1]
-
-
- if (state.conversionRate !== 0) {
- fiatTooltipNumber = Number(splitBalance[0]) * state.conversionRate
- fiatDisplayNumber = fiatTooltipNumber.toFixed(2)
- } else {
- fiatDisplayNumber = 'N/A'
- }
-
- var fiatSuffix = state.currentFiat
-
- if (state.shorten) {
- balance = balanceObj.shortBalance
- } else {
- balance = balanceObj.balance
- }
-
- var label = balanceObj.label
-
- return (
- h('.flex-column', [
- h(Tooltip, {
- position: 'bottom',
- title: `${ethNumber} ${ethSuffix}`,
- }, [
- h('.flex-row', {
- style: {
- alignItems: 'flex-end',
- lineHeight: '13px',
- fontFamily: 'Montserrat Light',
- textRendering: 'geometricPrecision',
- marginBottom: '5px',
- },
- }, [
- h('div', {
- style: {
- width: '100%',
- textAlign: 'right',
- },
- }, balance),
- h('div', {
- style: {
- color: '#AEAEAE',
- marginLeft: '5px',
- },
- }, label),
- ]),
- ]),
- h(Tooltip, {
- position: 'bottom',
- title: `${fiatTooltipNumber} ${fiatSuffix}`,
- }, [
- fiatDisplay(fiatDisplayNumber, fiatSuffix),
- ]),
- ])
- )
-}
-
-function fiatDisplay (fiatDisplayNumber, fiatSuffix) {
- if (fiatDisplayNumber !== 'N/A') {
- return h('.flex-row', {
- style: {
- alignItems: 'flex-end',
- lineHeight: '13px',
- fontFamily: 'Montserrat Light',
- textRendering: 'geometricPrecision',
- },
- }, [
- h('div', {
- style: {
- width: '100%',
- textAlign: 'right',
- fontSize: '12px',
- color: '#333333',
- },
- }, fiatDisplayNumber),
- h('div', {
- style: {
- color: '#AEAEAE',
- marginLeft: '5px',
- fontSize: '12px',
- },
- }, fiatSuffix),
- ])
- } else {
- return h('div')
- }
-}
diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js
index f36b9faeb..6d8b099a5 100644
--- a/ui/app/components/account-export.js
+++ b/ui/app/components/account-export.js
@@ -3,6 +3,7 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const copyToClipboard = require('copy-to-clipboard')
const actions = require('../actions')
+const ethUtil = require('ethereumjs-util')
module.exports = ExportAccountView
@@ -61,7 +62,9 @@ ExportAccountView.prototype.render = function () {
if (accountExported) {
return h('div.privateKey', {
-
+ style: {
+ margin: '0 20px',
+ },
}, [
h('label', 'Your private key (click to copy):'),
h('p.error.cursor-pointer', {
@@ -72,9 +75,9 @@ ExportAccountView.prototype.render = function () {
width: '100%',
},
onClick: function (event) {
- copyToClipboard(accountDetail.privateKey)
+ copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey))
},
- }, accountDetail.privateKey),
+ }, ethUtil.stripHexPrefix(accountDetail.privateKey)),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
}, 'Done'),
diff --git a/ui/app/components/account-info-link.js b/ui/app/components/account-info-link.js
index 4fe3b8b5d..49c42e9ec 100644
--- a/ui/app/components/account-info-link.js
+++ b/ui/app/components/account-info-link.js
@@ -14,7 +14,7 @@ function AccountInfoLink () {
AccountInfoLink.prototype.render = function () {
const { selected, network } = this.props
- const title = 'View account on etherscan'
+ const title = 'View account on Etherscan'
const url = genAccountLink(selected, network)
if (!url) {
diff --git a/ui/app/components/buy-button-subview.js b/ui/app/components/buy-button-subview.js
index 742241e5b..3074bd7cd 100644
--- a/ui/app/components/buy-button-subview.js
+++ b/ui/app/components/buy-button-subview.js
@@ -6,16 +6,19 @@ const actions = require('../actions')
const CoinbaseForm = require('./coinbase-form')
const ShapeshiftForm = require('./shapeshift-form')
const extension = require('../../../app/scripts/lib/extension')
+const Loading = require('./loading')
+const TabBar = require('./tab-bar')
module.exports = connect(mapStateToProps)(BuyButtonSubview)
function mapStateToProps (state) {
return {
- selectedAccount: state.selectedAccount,
warning: state.appState.warning,
buyView: state.appState.buyView,
network: state.metamask.network,
provider: state.metamask.provider,
+ context: state.appState.currentView.context,
+ isSubLoading: state.appState.isSubLoading,
}
}
@@ -26,7 +29,7 @@ function BuyButtonSubview () {
BuyButtonSubview.prototype.render = function () {
const props = this.props
- const currentForm = props.buyView.formView
+ const isLoading = props.isSubLoading
return (
h('.buy-eth-section', [
@@ -38,7 +41,7 @@ BuyButtonSubview.prototype.render = function () {
},
}, [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
- onClick: () => props.dispatch(actions.backToAccountDetail(props.selectedAccount)),
+ onClick: this.backButtonContext.bind(this),
style: {
position: 'absolute',
left: '10px',
@@ -46,43 +49,56 @@ BuyButtonSubview.prototype.render = function () {
}),
h('h2.page-subtitle', 'Buy Eth'),
]),
- h('h3.flex-row.text-transform-uppercase', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- paddingTop: '4px',
- justifyContent: 'space-around',
+
+ h(Loading, { isLoading }),
+
+ h(TabBar, {
+ tabs: [
+ {
+ content: [
+ 'Coinbase',
+ h('a', {
+ onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'),
+ }, [
+ h('i.fa.fa-question-circle', {
+ style: {
+ margin: '0px 5px',
+ },
+ }),
+ ]),
+ ],
+ key: 'coinbase',
+ },
+ {
+ content: [
+ 'Shapeshift',
+ h('a', {
+ href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md',
+ onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'),
+ }, [
+ h('i.fa.fa-question-circle', {
+ style: {
+ margin: '0px 5px',
+ },
+ }),
+ ]),
+ ],
+ key: 'shapeshift',
+ },
+ ],
+ defaultTab: 'coinbase',
+ tabSelected: (key) => {
+ switch (key) {
+ case 'coinbase':
+ props.dispatch(actions.coinBaseSubview())
+ break
+ case 'shapeshift':
+ props.dispatch(actions.shapeShiftSubview(props.provider.type))
+ break
+ }
},
- }, [
- h(currentForm.coinbase ? '.activeForm' : '.inactiveForm.pointer', {
- onClick: () => props.dispatch(actions.coinBaseSubview()),
- }, 'Coinbase'),
- h('a', {
- onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'),
- }, [
- h('i.fa.fa-question-circle', {
- style: {
- position: 'relative',
- right: '33px',
- },
- }),
- ]),
- h(currentForm.shapeshift ? '.activeForm' : '.inactiveForm.pointer', {
- onClick: () => props.dispatch(actions.shapeShiftSubview(props.provider.type)),
- }, 'Shapeshift'),
+ }),
- h('a', {
- href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md',
- onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'),
- }, [
- h('i.fa.fa-question-circle', {
- style: {
- position: 'relative',
- right: '28px',
- },
- }),
- ]),
- ]),
this.formVersionSubview(),
])
)
@@ -106,9 +122,9 @@ BuyButtonSubview.prototype.formVersionSubview = function () {
style: {
width: '225px',
},
- }, 'In order to access this feature please switch too the Main Network'),
- h('h3.text-transform-uppercase', 'or:'),
- this.props.network === '2' ? h('button.text-transform-uppercase', {
+ }, 'In order to access this feature, please switch to the Main Network'),
+ (this.props.network === '3') ? h('h3.text-transform-uppercase', 'or:') : null,
+ (this.props.network === '3') ? h('button.text-transform-uppercase', {
onClick: () => this.props.dispatch(actions.buyEth()),
style: {
marginTop: '15px',
@@ -121,3 +137,11 @@ BuyButtonSubview.prototype.formVersionSubview = function () {
BuyButtonSubview.prototype.navigateTo = function (url) {
extension.tabs.create({ url })
}
+
+BuyButtonSubview.prototype.backButtonContext = function () {
+ if (this.props.context === 'confTx') {
+ this.props.dispatch(actions.showConfTxPage(false))
+ } else {
+ this.props.dispatch(actions.goHome())
+ }
+}
diff --git a/ui/app/components/coinbase-form.js b/ui/app/components/coinbase-form.js
index efd05ec96..40f5719bb 100644
--- a/ui/app/components/coinbase-form.js
+++ b/ui/app/components/coinbase-form.js
@@ -7,16 +7,15 @@ const actions = require('../actions')
const isValidAddress = require('../util').isValidAddress
module.exports = connect(mapStateToProps)(CoinbaseForm)
-function mapStateToProps(state) {
+function mapStateToProps (state) {
return {
- selectedAccount: state.selectedAccount,
warning: state.appState.warning,
}
}
inherits(CoinbaseForm, Component)
-function CoinbaseForm() {
+function CoinbaseForm () {
Component.call(this)
}
@@ -72,7 +71,7 @@ CoinbaseForm.prototype.render = function () {
lineHeight: '13px',
},
},
- `there is a USD$ 5 a day max and a USD$ 50
+ `there is a USD$ 15 a day max and a USD$ 50
dollar limit per the life time of an account without a
coinbase account. A fee of 3.75% will be aplied to debit/credit cards.`),
@@ -116,15 +115,14 @@ CoinbaseForm.prototype.toCoinbase = function () {
props.dispatch(actions.buyEth(address, props.buyView.amount))
} else if (!isValidAmountforCoinBase(amount).valid) {
message = isValidAmountforCoinBase(amount).message
- return props.dispatch(actions.showWarning(message))
+ return props.dispatch(actions.displayWarning(message))
} else {
message = 'Receiving address is invalid.'
- return props.dispatch(actions.showWarning(message))
+ return props.dispatch(actions.displayWarning(message))
}
}
CoinbaseForm.prototype.renderLoading = function () {
-
return h('img', {
style: {
width: '27px',
@@ -134,18 +132,17 @@ CoinbaseForm.prototype.renderLoading = function () {
})
}
-function isValidAmountforCoinBase(amount) {
+function isValidAmountforCoinBase (amount) {
amount = parseFloat(amount)
-
if (amount) {
- if (amount <= 5 && amount > 0) {
+ if (amount <= 15 && amount > 0) {
return {
valid: true,
}
- } else if (amount > 5) {
+ } else if (amount > 15) {
return {
valid: false,
- message: 'The amount can not be greater then $5',
+ message: 'The amount can not be greater then $15',
}
} else {
return {
diff --git a/ui/app/components/copyButton.js b/ui/app/components/copyButton.js
index a01603585..a25d0719c 100644
--- a/ui/app/components/copyButton.js
+++ b/ui/app/components/copyButton.js
@@ -50,12 +50,10 @@ CopyButton.prototype.render = function () {
])
}
-CopyButton.prototype.debounceRestore = function() {
-
+CopyButton.prototype.debounceRestore = function () {
this.setState({ copied: true })
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.setState({ copied: false })
}, 850)
-
}
diff --git a/ui/app/components/drop-menu-item.js b/ui/app/components/drop-menu-item.js
index 0ca1988c6..9f002234e 100644
--- a/ui/app/components/drop-menu-item.js
+++ b/ui/app/components/drop-menu-item.js
@@ -32,17 +32,17 @@ DropMenuItem.prototype.render = function () {
}
DropMenuItem.prototype.activeNetworkRender = function () {
- let activeNetwork = this.props.activeNetworkRender
- let { provider } = this.props
- let providerType = provider ? provider.type : null
+ const activeNetwork = this.props.activeNetworkRender
+ const { provider } = this.props
+ const providerType = provider ? provider.type : null
if (activeNetwork === undefined) return
switch (this.props.label) {
case 'Main Ethereum Network':
if (providerType === 'mainnet') return h('.check', '✓')
break
- case 'Morden Test Network':
- if (activeNetwork === '2') return h('.check', '✓')
+ case 'Ropsten Test Network':
+ if (provider.type === 'testnet') return h('.check', '✓')
break
case 'Localhost 8545':
if (activeNetwork === 'http://localhost:8545') return h('.check', '✓')
diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js
index 498873faa..57ca84564 100644
--- a/ui/app/components/eth-balance.js
+++ b/ui/app/components/eth-balance.js
@@ -4,6 +4,7 @@ const inherits = require('util').inherits
const formatBalance = require('../util').formatBalance
const generateBalanceObject = require('../util').generateBalanceObject
const Tooltip = require('./tooltip.js')
+const FiatValue = require('./fiat-value.js')
module.exports = EthBalanceComponent
@@ -13,11 +14,12 @@ function EthBalanceComponent () {
}
EthBalanceComponent.prototype.render = function () {
- var state = this.props
- var style = state.style
+ var props = this.props
+ let { value } = props
+ var style = props.style
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
- const value = formatBalance(state.value, 6, needsParse)
- var width = state.width
+ value = value ? formatBalance(value, 6, needsParse) : '...'
+ var width = props.width
return (
@@ -35,15 +37,17 @@ EthBalanceComponent.prototype.render = function () {
)
}
EthBalanceComponent.prototype.renderBalance = function (value) {
- var state = this.props
+ var props = this.props
if (value === 'None') return value
- var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
+ if (value === '...') return value
+ var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3)
var balance
var splitBalance = value.split(' ')
var ethNumber = splitBalance[0]
var ethSuffix = splitBalance[1]
+ const showFiat = 'showFiat' in props ? props.showFiat : true
- if (state.shorten) {
+ if (props.shorten) {
balance = balanceObj.shortBalance
} else {
balance = balanceObj.balance
@@ -55,8 +59,8 @@ EthBalanceComponent.prototype.renderBalance = function (value) {
h(Tooltip, {
position: 'bottom',
title: `${ethNumber} ${ethSuffix}`,
- }, [
- h('.flex-column', {
+ }, h('div.flex-column', [
+ h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
@@ -74,9 +78,12 @@ EthBalanceComponent.prototype.renderBalance = function (value) {
style: {
color: ' #AEAEAE',
fontSize: '12px',
+ marginLeft: '5px',
},
}, label),
]),
- ])
+
+ showFiat ? h(FiatValue, { value: props.value }) : null,
+ ]))
)
}
diff --git a/ui/app/components/fiat-value.js b/ui/app/components/fiat-value.js
new file mode 100644
index 000000000..13ee48245
--- /dev/null
+++ b/ui/app/components/fiat-value.js
@@ -0,0 +1,71 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+const connect = require('react-redux').connect
+const formatBalance = require('../util').formatBalance
+
+module.exports = connect(mapStateToProps)(FiatValue)
+
+function mapStateToProps (state) {
+ return {
+ conversionRate: state.metamask.conversionRate,
+ currentFiat: state.metamask.currentFiat,
+ }
+}
+
+inherits(FiatValue, Component)
+function FiatValue () {
+ Component.call(this)
+}
+
+FiatValue.prototype.render = function () {
+ const props = this.props
+ const value = formatBalance(props.value, 6)
+
+ if (value === 'None') return value
+ var fiatDisplayNumber, fiatTooltipNumber
+ var splitBalance = value.split(' ')
+
+ if (props.conversionRate !== 0) {
+ fiatTooltipNumber = Number(splitBalance[0]) * props.conversionRate
+ fiatDisplayNumber = fiatTooltipNumber.toFixed(2)
+ } else {
+ fiatDisplayNumber = 'N/A'
+ fiatTooltipNumber = 'Unknown'
+ }
+
+ var fiatSuffix = props.currentFiat
+
+ return fiatDisplay(fiatDisplayNumber, fiatSuffix)
+}
+
+function fiatDisplay (fiatDisplayNumber, fiatSuffix) {
+ if (fiatDisplayNumber !== 'N/A') {
+ return h('.flex-row', {
+ style: {
+ alignItems: 'flex-end',
+ lineHeight: '13px',
+ fontFamily: 'Montserrat Light',
+ textRendering: 'geometricPrecision',
+ },
+ }, [
+ h('div', {
+ style: {
+ width: '100%',
+ textAlign: 'right',
+ fontSize: '12px',
+ color: '#333333',
+ },
+ }, fiatDisplayNumber),
+ h('div', {
+ style: {
+ color: '#AEAEAE',
+ marginLeft: '5px',
+ fontSize: '12px',
+ },
+ }, fiatSuffix),
+ ])
+ } else {
+ return h('div')
+ }
+}
diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js
index 4b2bf899e..6d4871d02 100644
--- a/ui/app/components/identicon.js
+++ b/ui/app/components/identicon.js
@@ -16,8 +16,8 @@ function IdenticonComponent () {
}
IdenticonComponent.prototype.render = function () {
- var state = this.props
- var diameter = state.diameter || this.defaultDiameter
+ var props = this.props
+ var diameter = props.diameter || this.defaultDiameter
return (
h('div', {
key: 'identicon-' + this.props.address,
@@ -33,15 +33,31 @@ IdenticonComponent.prototype.render = function () {
}
IdenticonComponent.prototype.componentDidMount = function () {
- var state = this.props
- var address = state.address
+ var props = this.props
+ var address = props.address
if (!address) return
var container = findDOMNode(this)
- var diameter = state.diameter || this.defaultDiameter
- var imageify = state.imageify === undefined ? true : state.imageify
- var img = iconFactory.iconForAddress(address, diameter, imageify)
+ var diameter = props.diameter || this.defaultDiameter
+ var img = iconFactory.iconForAddress(address, diameter, false)
container.appendChild(img)
}
+IdenticonComponent.prototype.componentDidUpdate = function () {
+ var props = this.props
+ var address = props.address
+
+ if (!address) return
+
+ var container = findDOMNode(this)
+
+ var children = container.children
+ for (var i = 0; i < children.length; i++) {
+ container.removeChild(children[i])
+ }
+
+ var diameter = props.diameter || this.defaultDiameter
+ var img = iconFactory.iconForAddress(address, diameter, false)
+ container.appendChild(img)
+}
diff --git a/ui/app/loading.js b/ui/app/components/loading.js
index 606b53dde..88dc535df 100644
--- a/ui/app/loading.js
+++ b/ui/app/components/loading.js
@@ -1,24 +1,18 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
-const connect = require('react-redux').connect
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
-module.exports = connect(mapStateToProps)(LoadingIndicator)
-
-function mapStateToProps (state) {
- return {
- isLoading: state.appState.isLoading,
- }
-}
inherits(LoadingIndicator, Component)
+module.exports = LoadingIndicator
+
function LoadingIndicator () {
Component.call(this)
}
LoadingIndicator.prototype.render = function () {
- var isLoading = this.props.isLoading
+ const { isLoading, loadingMessage } = this.props
return (
h(ReactCSSTransitionGroup, {
@@ -30,6 +24,7 @@ LoadingIndicator.prototype.render = function () {
isLoading ? h('div', {
style: {
+ zIndex: 10,
position: 'absolute',
display: 'flex',
justifyContent: 'center',
@@ -42,8 +37,14 @@ LoadingIndicator.prototype.render = function () {
h('img', {
src: 'images/loading.svg',
}),
+
+ showMessageIfAny(loadingMessage),
]) : null,
])
)
}
+function showMessageIfAny (loadingMessage) {
+ if (!loadingMessage) return null
+ return h('span', loadingMessage)
+}
diff --git a/ui/app/components/mascot.js b/ui/app/components/mascot.js
index f2b00262b..f015d0c4d 100644
--- a/ui/app/components/mascot.js
+++ b/ui/app/components/mascot.js
@@ -14,9 +14,8 @@ function Mascot () {
pxNotRatio: true,
width: 200,
height: 200,
- staticImage: './images/icon-512.png',
})
- if (!this.logo.webGLSupport) return
+
this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000)
this.unfollowMouse = this.logo.setFollowMouse.bind(this.logo, false)
}
@@ -27,32 +26,25 @@ Mascot.prototype.render = function () {
// and we dont get that until render
this.handleAnimationEvents()
- return (
-
- h('#metamask-mascot-container')
-
- )
+ return h('#metamask-mascot-container', {
+ style: { zIndex: 2 },
+ })
}
Mascot.prototype.componentDidMount = function () {
var targetDivId = 'metamask-mascot-container'
var container = document.getElementById(targetDivId)
- if (!this.logo.webGLSupport) {
- var staticLogo = this.logo.staticLogo
- staticLogo.style.marginBottom = '40px'
- container.appendChild(staticLogo)
- } else {
- container.appendChild(this.logo.canvas)
- }
+ container.appendChild(this.logo.container)
}
Mascot.prototype.componentWillUnmount = function () {
- if (!this.logo.webGLSupport) return
- this.logo.canvas.remove()
+ this.animations = this.props.animationEventEmitter
+ this.animations.removeAllListeners()
+ this.logo.container.remove()
+ this.logo.stopAnimation()
}
Mascot.prototype.handleAnimationEvents = function () {
- if (!this.logo.webGLSupport) return
// only setup listeners once
if (this.animations) return
this.animations = this.props.animationEventEmitter
@@ -61,7 +53,6 @@ Mascot.prototype.handleAnimationEvents = function () {
}
Mascot.prototype.lookAt = function (target) {
- if (!this.logo.webGLSupport) return
this.unfollowMouse()
this.logo.lookAt(target)
this.refollowMouse()
diff --git a/ui/app/components/network.js b/ui/app/components/network.js
index 2f1bf639a..77805fd57 100644
--- a/ui/app/components/network.js
+++ b/ui/app/components/network.js
@@ -22,7 +22,6 @@ Network.prototype.render = function () {
let iconName, hoverText
if (networkNumber === 'loading') {
-
return h('img.network-indicator', {
title: 'Attempting to connect to blockchain.',
onClick: (event) => this.props.onClick(event),
@@ -32,23 +31,22 @@ Network.prototype.render = function () {
},
src: 'images/loading.svg',
})
-
} else if (providerName === 'mainnet') {
hoverText = 'Main Ethereum Network'
iconName = 'ethereum-network'
- } else if (parseInt(networkNumber) === 2) {
- hoverText = 'Morden Test Network'
- iconName = 'morden-test-network'
+ } else if (providerName === 'testnet') {
+ hoverText = 'Ropsten Test Network'
+ iconName = 'ropsten-test-network'
+ } else if (parseInt(networkNumber) === 3) {
+ hoverText = 'Ropsten Test Network'
+ iconName = 'ropsten-test-network'
} else {
hoverText = 'Unknown Private Network'
iconName = 'unknown-private-network'
}
+
return (
- h('#network_component.flex-center.pointer', {
- style: {
- marginRight: '-27px',
- marginLeft: '-3px',
- },
+ h('#network_component.pointer', {
title: hoverText,
onClick: (event) => this.props.onClick(event),
}, [
@@ -61,21 +59,20 @@ Network.prototype.render = function () {
style: {
color: '#039396',
}},
- 'Etherum Main Net'),
+ 'Ethereum Main Net'),
])
- case 'morden-test-network':
+ case 'ropsten-test-network':
return h('.network-indicator', [
h('.menu-icon.red-dot'),
h('.network-name', {
style: {
color: '#ff6666',
}},
- 'Morden Test Net'),
+ 'Ropsten Test Net'),
])
default:
return h('.network-indicator', [
h('i.fa.fa-question-circle.fa-lg', {
- ariaHidden: true,
style: {
margin: '10px',
color: 'rgb(125, 128, 130)',
diff --git a/ui/app/components/notice.js b/ui/app/components/notice.js
new file mode 100644
index 000000000..00db734d7
--- /dev/null
+++ b/ui/app/components/notice.js
@@ -0,0 +1,110 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const ReactMarkdown = require('react-markdown')
+const linker = require('extension-link-enabler')
+const findDOMNode = require('react-dom').findDOMNode
+
+module.exports = Notice
+
+inherits(Notice, Component)
+function Notice () {
+ Component.call(this)
+}
+
+Notice.prototype.render = function () {
+ const { notice, onConfirm } = this.props
+ const { title, date, body } = notice
+
+ return (
+ h('.flex-column.flex-center.flex-grow', [
+ h('h3.flex-center.text-transform-uppercacse.terms-header', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ width: '100%',
+ fontSize: '20px',
+ textAlign: 'center',
+ padding: 6,
+ },
+ }, [
+ title,
+ ]),
+
+ h('h5.flex-center.text-transform-uppercacse.terms-header', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: 24,
+ width: '100%',
+ fontSize: '20px',
+ textAlign: 'center',
+ padding: 6,
+ },
+ }, [
+ date,
+ ]),
+
+ h('style', `
+
+ .markdown {
+ overflow-x: hidden;
+ }
+
+ .markdown h1, .markdown h2, .markdown h3 {
+ margin: 10px 0;
+ font-weight: bold;
+ }
+
+ .markdown strong {
+ font-weight: bold;
+ }
+ .markdown em {
+ font-style: italic;
+ }
+
+ .markdown p {
+ margin: 10px 0;
+ }
+
+ .markdown a {
+ color: #df6b0e;
+ }
+
+ `),
+
+ h('div.markdown', {
+ style: {
+ background: 'rgb(235, 235, 235)',
+ height: '310px',
+ padding: '6px',
+ width: '90%',
+ overflowY: 'scroll',
+ scroll: 'auto',
+ },
+ }, [
+ h(ReactMarkdown, {
+ source: body,
+ skipHtml: true,
+ }),
+ ]),
+
+ h('button', {
+ onClick: onConfirm,
+ style: {
+ marginTop: '18px',
+ },
+ }, 'Continue'),
+ ])
+ )
+}
+
+Notice.prototype.componentDidMount = function () {
+ var node = findDOMNode(this)
+ linker.setupListener(node)
+}
+
+Notice.prototype.componentWillUnmount = function () {
+ var node = findDOMNode(this)
+ linker.teardownListener(node)
+}
diff --git a/ui/app/components/pending-msg.js b/ui/app/components/pending-msg.js
index f4bde91dc..b2cac164a 100644
--- a/ui/app/components/pending-msg.js
+++ b/ui/app/components/pending-msg.js
@@ -28,6 +28,15 @@ PendingMsg.prototype.render = function () {
},
}, 'Sign Message'),
+ h('.error', {
+ style: {
+ margin: '10px',
+ },
+ }, `Signing this message can have
+ dangerous side effects. Only sign messages from
+ sites you fully trust with your entire account.
+ This will be fixed in a future version.`),
+
// message details
h(PendingTxDetails, state),
diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js
index 2fb0eae3f..e8615404e 100644
--- a/ui/app/components/pending-tx-details.js
+++ b/ui/app/components/pending-tx-details.js
@@ -1,16 +1,12 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const carratInline = require('fs').readFileSync('./images/forward-carrat.svg', 'utf8')
const MiniAccountPanel = require('./mini-account-panel')
-const EtherBalance = require('./eth-balance')
-const addressSummary = require('../util').addressSummary
-const formatBalance = require('../util').formatBalance
+const EthBalance = require('./eth-balance')
+const util = require('../util')
+const addressSummary = util.addressSummary
const nameForAddress = require('../../lib/contract-namer')
-const ethUtil = require('ethereumjs-util')
-const BN = ethUtil.BN
-
module.exports = PendingTxDetails
@@ -31,13 +27,9 @@ PTXP.render = function () {
var account = props.accounts[address]
var balance = account ? account.balance : '0x0'
- var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txData.estimatedGas), 16)
- var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16)
- var txFee = gasCost.mul(gasPrice)
- var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16)
- var maxCost = txValue.add(txFee)
+ var txFee = txData.txFee || ''
+ var maxCost = txData.maxCost || ''
var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
-
var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons
return (
@@ -70,7 +62,7 @@ PTXP.render = function () {
fontFamily: 'Montserrat Light, Montserrat, sans-serif',
},
}, [
- h(EtherBalance, {
+ h(EthBalance, {
value: balance,
inline: true,
labelColor: '#F7861C',
@@ -79,7 +71,7 @@ PTXP.render = function () {
]),
- forwardCarrat(imageify),
+ forwardCarrat(),
this.miniAccountPanelForRecipient(),
]),
@@ -107,12 +99,12 @@ PTXP.render = function () {
h('.row', [
h('.cell.label', 'Amount'),
- h('.cell.value', formatBalance(txParams.value)),
+ h(EthBalance, { value: txParams.value }),
]),
h('.cell.row', [
h('.cell.label', 'Max Transaction Fee'),
- h('.cell.value', formatBalance(txFee.toString(16))),
+ h(EthBalance, { value: txFee.toString(16) }),
]),
h('.cell.row', {
@@ -129,7 +121,7 @@ PTXP.render = function () {
alignItems: 'center',
},
}, [
- h(EtherBalance, {
+ h(EthBalance, {
value: maxCost.toString(16),
inline: true,
labelColor: 'black',
@@ -154,8 +146,6 @@ PTXP.render = function () {
]),
]), // End of Table
- this.warnIfNeeded(),
-
])
)
}
@@ -201,53 +191,16 @@ PTXP.miniAccountPanelForRecipient = function () {
}
}
-// Should analyze if there is a DELEGATECALL opcode
-// in the recipient contract, and show a warning if so.
-PTXP.warnIfNeeded = function () {
- const containsDelegateCall = !!this.props.txData.containsDelegateCall
-
- if (!containsDelegateCall) {
- return null
- }
-
- return h('span.error', {
- style: {
- fontFamily: 'Montserrat Light',
- fontSize: '13px',
- display: 'flex',
- justifyContent: 'center',
- },
- }, [
- h('i.fa.fa-lg.fa-info-circle', { style: { margin: '5px' } }),
- h('span', ' Your identity may be used in other contracts!'),
- ])
-}
-
-
-function forwardCarrat (imageify) {
- if (imageify) {
- return (
-
- h('img', {
- src: 'images/forward-carrat.svg',
- style: {
- padding: '5px 6px 0px 10px',
- height: '37px',
- },
- })
+function forwardCarrat () {
+ return (
- )
- } else {
- return (
+ h('img', {
+ src: 'images/forward-carrat.svg',
+ style: {
+ padding: '5px 6px 0px 10px',
+ height: '37px',
+ },
+ })
- h('div', {
- dangerouslySetInnerHTML: { __html: carratInline },
- style: {
- padding: '0px 6px 0px 10px',
- height: '45px',
- },
- })
-
- )
- }
+ )
}
diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js
index 1feedbbbc..96f968929 100644
--- a/ui/app/components/pending-tx.js
+++ b/ui/app/components/pending-tx.js
@@ -3,7 +3,6 @@ const h = require('react-hyperscript')
const inherits = require('util').inherits
const PendingTxDetails = require('./pending-tx-details')
-
module.exports = PendingTx
inherits(PendingTx, Component)
@@ -31,6 +30,24 @@ PendingTx.prototype.render = function () {
}
`),
+ txData.simulationFails ?
+ h('.error', {
+ style: {
+ marginLeft: 50,
+ fontSize: '0.9em',
+ },
+ }, 'Transaction Error. Exception thrown in contract code.')
+ : null,
+
+ state.insufficientBalance ?
+ h('span.error', {
+ style: {
+ marginLeft: 50,
+ fontSize: '0.9em',
+ },
+ }, 'Insufficient balance for transaction')
+ : null,
+
// send + cancel
h('.flex-row.flex-space-around.conf-buttons', {
style: {
@@ -39,17 +56,22 @@ PendingTx.prototype.render = function () {
margin: '14px 25px',
},
}, [
+
+ state.insufficientBalance ?
+ h('button.btn-green', {
+ onClick: state.buyEth,
+ }, 'Buy Ether')
+ : null,
+
h('button.confirm', {
+ disabled: state.insufficientBalance,
onClick: state.sendTransaction,
- style: { background: 'rgb(251,117,1)' },
}, 'Accept'),
- h('button.cancel', {
+ h('button.cancel.btn-red', {
onClick: state.cancelTransaction,
- style: { background: 'rgb(254,35,17)' },
}, 'Reject'),
]),
])
)
}
-
diff --git a/ui/app/components/qr-code.js b/ui/app/components/qr-code.js
index c26b02b94..5488599eb 100644
--- a/ui/app/components/qr-code.js
+++ b/ui/app/components/qr-code.js
@@ -1,5 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
+const qrCode = require('qrcode-npm').qrcode
const inherits = require('util').inherits
const connect = require('react-redux').connect
const CopyButton = require('./copyButton')
@@ -23,15 +24,22 @@ function QrCodeView () {
QrCodeView.prototype.render = function () {
var props = this.props
var Qr = props.Qr
+ var qrImage = qrCode(4, 'M')
+
+ qrImage.addData(Qr.data)
+ qrImage.make()
+
return h('.main-container.flex-column', {
key: 'qr',
style: {
justifyContent: 'center',
- padding: '45px',
+ paddingBottom: '45px',
+ paddingLeft: '45px',
+ paddingRight: '45px',
alignItems: 'center',
},
}, [
- Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('h3', Qr.message),
+ Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('.qr-header', Qr.message),
this.props.warning ? this.props.warning && h('span.error.flex-center', {
style: {
@@ -48,7 +56,7 @@ QrCodeView.prototype.render = function () {
marginBottom: '15px',
},
dangerouslySetInnerHTML: {
- __html: Qr.image,
+ __html: qrImage.createTableTag(4),
},
}),
h('.flex-row', [
diff --git a/ui/app/components/range-slider.js b/ui/app/components/range-slider.js
new file mode 100644
index 000000000..823f5eb01
--- /dev/null
+++ b/ui/app/components/range-slider.js
@@ -0,0 +1,58 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+
+module.exports = RangeSlider
+
+inherits(RangeSlider, Component)
+function RangeSlider () {
+ Component.call(this)
+}
+
+RangeSlider.prototype.render = function () {
+ const state = this.state || {}
+ const props = this.props
+ const onInput = props.onInput || function () {}
+ const name = props.name
+ const {
+ min = 0,
+ max = 100,
+ increment = 1,
+ defaultValue = 50,
+ mirrorInput = false,
+ } = this.props.options
+ const {container, input, range} = props.style
+
+ return (
+ h('.flex-row', {
+ style: container,
+ }, [
+ h('input', {
+ type: 'range',
+ name: name,
+ min: min,
+ max: max,
+ step: increment,
+ style: range,
+ value: state.value || defaultValue,
+ onChange: mirrorInput ? this.mirrorInputs.bind(this, event) : onInput,
+ }),
+
+ // Mirrored input for range
+ mirrorInput ? h('input.large-input', {
+ type: 'number',
+ name: `${name}Mirror`,
+ min: min,
+ max: max,
+ value: state.value || defaultValue,
+ step: increment,
+ style: input,
+ onChange: this.mirrorInputs.bind(this, event),
+ }) : null,
+ ])
+ )
+}
+
+RangeSlider.prototype.mirrorInputs = function (event) {
+ this.setState({value: event.target.value})
+}
diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js
index b8650f7d5..8c9686035 100644
--- a/ui/app/components/shapeshift-form.js
+++ b/ui/app/components/shapeshift-form.js
@@ -1,4 +1,4 @@
-const Component = require('react').Component
+const PersistentForm = require('../../lib/persistent-form')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
@@ -8,20 +8,21 @@ const Qr = require('./qr-code')
const isValidAddress = require('../util').isValidAddress
module.exports = connect(mapStateToProps)(ShapeshiftForm)
-function mapStateToProps(state) {
+function mapStateToProps (state) {
return {
- selectedAccount: state.selectedAccount,
warning: state.appState.warning,
isSubLoading: state.appState.isSubLoading,
qrRequested: state.appState.qrRequested,
}
}
-inherits(ShapeshiftForm, Component)
+inherits(ShapeshiftForm, PersistentForm)
function ShapeshiftForm () {
- Component.call(this)
+ PersistentForm.call(this)
+ this.persistentFormParentId = 'shapeshift-buy-form'
}
+
ShapeshiftForm.prototype.render = function () {
return h(ReactCSSTransitionGroup, {
className: 'css-transition-group',
@@ -31,7 +32,6 @@ ShapeshiftForm.prototype.render = function () {
}, [
this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain(),
])
-
}
ShapeshiftForm.prototype.renderMain = function () {
@@ -66,6 +66,9 @@ ShapeshiftForm.prototype.renderMain = function () {
h('input#fromCoin.buy-inputs.ex-coins', {
type: 'text',
list: 'coinList',
+ dataset: {
+ persistentFormId: 'input-coin',
+ },
style: {
boxSizing: 'border-box',
},
@@ -122,7 +125,6 @@ ShapeshiftForm.prototype.renderMain = function () {
this.props.isSubLoading ? this.renderLoading() : null,
h('.flex-column', {
style: {
- width: '235px',
alignItems: 'flex-start',
},
}, [
@@ -159,6 +161,9 @@ ShapeshiftForm.prototype.renderMain = function () {
h('input#fromCoinAddress.buy-inputs', {
type: 'text',
placeholder: `Your ${coin} Refund Address`,
+ dataset: {
+ persistentFormId: 'refund-address',
+ },
style: {
boxSizing: 'border-box',
width: '278px',
@@ -236,7 +241,7 @@ ShapeshiftForm.prototype.updateCoin = function (event) {
if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') {
var message = 'Not a valid coin'
- return props.dispatch(actions.showWarning(message))
+ return props.dispatch(actions.displayWarning(message))
} else {
return props.dispatch(actions.pairUpdate(coin))
}
@@ -261,17 +266,17 @@ ShapeshiftForm.prototype.renderInfo = function () {
return h('span', {
style: {
- marginTop: '15px',
+ marginTop: '10px',
marginBottom: '15px',
},
}, [
h('h3.flex-row.text-transform-uppercase', {
style: {
- color: '#AEAEAE',
+ color: '#868686',
paddingTop: '4px',
justifyContent: 'space-around',
textAlign: 'center',
- fontSize: '14px',
+ fontSize: '17px',
},
}, `Market Info for ${marketinfo.pair.replace('_', ' to ').toUpperCase()}:`),
h('.marketinfo', ['Status : ', `${coinOptions[coin].status}`]),
diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js
index 38c19eb28..e0243e247 100644
--- a/ui/app/components/shift-list-item.js
+++ b/ui/app/components/shift-list-item.js
@@ -26,7 +26,6 @@ function ShiftListItem () {
}
ShiftListItem.prototype.render = function () {
-
return (
h('.transaction-list-item.flex-row', {
style: {
diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js
new file mode 100644
index 000000000..65078e0a4
--- /dev/null
+++ b/ui/app/components/tab-bar.js
@@ -0,0 +1,35 @@
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const inherits = require('util').inherits
+
+module.exports = TabBar
+
+inherits(TabBar, Component)
+function TabBar () {
+ Component.call(this)
+}
+
+TabBar.prototype.render = function () {
+ const props = this.props
+ const state = this.state || {}
+ const { tabs = [], defaultTab, tabSelected } = props
+ const { subview = defaultTab } = state
+
+ return (
+ h('.flex-row.space-around.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ paddingTop: '4px',
+ },
+ }, tabs.map((tab) => {
+ const { key, content } = tab
+ return h(subview === key ? '.activeForm' : '.inactiveForm.pointer', {
+ onClick: () => {
+ this.setState({ subview: key })
+ tabSelected(key)
+ },
+ }, content)
+ }))
+ )
+}
diff --git a/ui/app/components/tooltip.js b/ui/app/components/tooltip.js
index fb67c717e..edbc074bb 100644
--- a/ui/app/components/tooltip.js
+++ b/ui/app/components/tooltip.js
@@ -12,11 +12,11 @@ function Tooltip () {
Tooltip.prototype.render = function () {
const props = this.props
+ const { position, title, children } = props
return h(ReactTooltip, {
- position: props.position ? props.position : 'left',
- title: props.title,
+ position: position || 'left',
+ title,
fixed: false,
- }, props.children)
-
+ }, children)
}
diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js
index 8b118b1d4..90b4ec094 100644
--- a/ui/app/components/transaction-list-item-icon.js
+++ b/ui/app/components/transaction-list-item-icon.js
@@ -13,13 +13,34 @@ function TransactionIcon () {
TransactionIcon.prototype.render = function () {
const { transaction, txParams, isMsg } = this.props
+ switch (transaction.status) {
+ case 'unapproved':
+ return h( !isMsg ? '.unapproved-tx-icon' : 'i.fa.fa-certificate.fa-lg', {
+ style: {
+ width: '24px',
+ },
+ })
- if (transaction.status === 'rejected') {
- return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
- style: {
- width: '24px',
- },
- })
+ case 'rejected':
+ return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
+ style: {
+ width: '24px',
+ },
+ })
+
+ case 'failed':
+ return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
+ style: {
+ width: '24px',
+ },
+ })
+
+ case 'submitted':
+ return h('i.fa.fa-ellipsis-h', {
+ style: {
+ fontSize: '27px',
+ },
+ })
}
if (isMsg) {
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
index 1b85464e1..44d2dc587 100644
--- a/ui/app/components/transaction-list-item.js
+++ b/ui/app/components/transaction-list-item.js
@@ -8,6 +8,7 @@ const explorerLink = require('../../lib/explorer-link')
const CopyButton = require('./copyButton')
const vreme = new (require('vreme'))
const extension = require('../../../app/scripts/lib/extension')
+const Tooltip = require('./tooltip')
const TransactionIcon = require('./transaction-list-item-icon')
const ShiftListItem = require('./shift-list-item')
@@ -27,12 +28,11 @@ TransactionListItem.prototype.render = function () {
let isLinkable = false
const numericNet = parseInt(network)
- isLinkable = numericNet === 1 || numericNet === 2
+ isLinkable = numericNet === 1 || numericNet === 3
var isMsg = ('msgParams' in transaction)
var isTx = ('txParams' in transaction)
- var isPending = transaction.status === 'unconfirmed'
-
+ var isPending = transaction.status === 'unapproved'
let txParams
if (isTx) {
txParams = transaction.txParams
@@ -41,14 +41,13 @@ TransactionListItem.prototype.render = function () {
}
const isClickable = ('hash' in transaction && isLinkable) || isPending
-
return (
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
onClick: (event) => {
if (isPending) {
this.props.showTx(transaction.id)
}
-
+ event.stopPropagation()
if (!transaction.hash || !isLinkable) return
var url = explorerLink(transaction.hash, parseInt(network))
extension.tabs.create({ url })
@@ -58,10 +57,17 @@ TransactionListItem.prototype.render = function () {
},
}, [
- // large identicon
h('.identicon-wrapper.flex-column.flex-center.select-none', [
- transaction.status === 'unconfirmed' ? h('i.fa.fa-ellipsis-h', {style: { fontSize: '27px' }})
- : h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
+ h('.pop-hover', {
+ onClick: (event) => {
+ event.stopPropagation()
+ if (!isTx || isPending) return
+ var url = `https://metamask.github.io/eth-tx-viz/?tx=${transaction.hash}`
+ extension.tabs.create({ url })
+ },
+ }, [
+ h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
+ ]),
]),
h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
@@ -77,6 +83,7 @@ TransactionListItem.prototype.render = function () {
value: txParams.value,
width: '55px',
shorten: true,
+ showFiat: false,
style: {fontSize: '15px'},
}) : h('.flex-column'),
])
@@ -127,7 +134,14 @@ function failIfFailed (transaction) {
if (transaction.status === 'rejected') {
return h('span.error', ' (Rejected)')
}
- if (transaction.status === 'failed') {
- return h('span.error', ' (Failed)')
+ if (transaction.err) {
+
+ return h(Tooltip, {
+ title: transaction.err.message,
+ position: 'bottom',
+ }, [
+ h('span.error', ' (Failed)'),
+ ])
}
+
}
diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js
index 7e1bedb05..3ae953637 100644
--- a/ui/app/components/transaction-list.js
+++ b/ui/app/components/transaction-list.js
@@ -13,12 +13,13 @@ function TransactionList () {
}
TransactionList.prototype.render = function () {
- const { txsToRender, network, unconfMsgs } = this.props
+ const { transactions, network, unapprovedMsgs } = this.props
+
var shapeShiftTxList
if (network === '1') {
shapeShiftTxList = this.props.shapeShiftTxList
}
- const transactions = !shapeShiftTxList ? txsToRender.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList)
+ const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
.sort((a, b) => b.time - a.time)
return (
@@ -55,8 +56,8 @@ TransactionList.prototype.render = function () {
},
}, [
- transactions.length
- ? transactions.map((transaction, i) => {
+ txsToRender.length
+ ? txsToRender.map((transaction, i) => {
let key
switch (transaction.key) {
case 'shapeshift':
diff --git a/ui/app/conf-tx.js b/ui/app/conf-tx.js
index 43604e8cf..a27219576 100644
--- a/ui/app/conf-tx.js
+++ b/ui/app/conf-tx.js
@@ -4,7 +4,11 @@ const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('./actions')
+const NetworkIndicator = require('./components/network')
const txHelper = require('../lib/tx-helper')
+const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
+const ethUtil = require('ethereumjs-util')
+const BN = ethUtil.BN
const PendingTx = require('./components/pending-tx')
const PendingMsg = require('./components/pending-msg')
@@ -16,10 +20,12 @@ function mapStateToProps (state) {
identities: state.metamask.identities,
accounts: state.metamask.accounts,
selectedAddress: state.metamask.selectedAddress,
- unconfTxs: state.metamask.unconfTxs,
- unconfMsgs: state.metamask.unconfMsgs,
+ unapprovedTxs: state.metamask.unapprovedTxs,
+ unapprovedMsgs: state.metamask.unapprovedMsgs,
index: state.appState.currentView.context,
warning: state.appState.warning,
+ network: state.metamask.network,
+ provider: state.metamask.provider,
}
}
@@ -31,11 +37,17 @@ function ConfirmTxScreen () {
ConfirmTxScreen.prototype.render = function () {
var state = this.props
- var unconfTxs = state.unconfTxs
- var unconfMsgs = state.unconfMsgs
- var unconfTxList = txHelper(unconfTxs, unconfMsgs)
- var index = state.index !== undefined ? state.index : 0
- var txData = unconfTxList[index] || unconfTxList[0] || {}
+ var network = state.network
+ var provider = state.provider
+ var unapprovedTxs = state.unapprovedTxs
+ var unapprovedMsgs = state.unapprovedMsgs
+
+ var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, network)
+ var index = state.index !== undefined && unconfTxList[index] ? state.index : 0
+ var txData = unconfTxList[index] || {}
+ var txParams = txData.params || {}
+ var isNotification = isPopupOrNotification() === 'notification'
+ if (unconfTxList.length === 0) return null
return (
@@ -43,10 +55,14 @@ ConfirmTxScreen.prototype.render = function () {
// subtitle and nav
h('.section-title.flex-row.flex-center', [
- h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
+ !isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: this.goHome.bind(this),
- }),
+ }) : null,
h('h2.page-subtitle', 'Confirm Transaction'),
+ isNotification ? h(NetworkIndicator, {
+ network: network,
+ provider: provider,
+ }) : null,
]),
h('h3', {
@@ -86,7 +102,9 @@ ConfirmTxScreen.prototype.render = function () {
selectedAddress: state.selectedAddress,
accounts: state.accounts,
identities: state.identities,
+ insufficientBalance: this.checkBalanceAgainstTx(txData),
// Actions
+ buyEth: this.buyEth.bind(this, txParams.from || state.selectedAddress),
sendTransaction: this.sendTransaction.bind(this, txData),
cancelTransaction: this.cancelTransaction.bind(this, txData),
signMessage: this.signMessage.bind(this, txData),
@@ -99,14 +117,33 @@ ConfirmTxScreen.prototype.render = function () {
}
function currentTxView (opts) {
- if ('txParams' in opts.txData) {
+ const { txData } = opts
+ const { txParams, msgParams } = txData
+
+ if (txParams) {
// This is a pending transaction
return h(PendingTx, opts)
- } else if ('msgParams' in opts.txData) {
+ } else if (msgParams) {
// This is a pending message to sign
return h(PendingMsg, opts)
}
}
+ConfirmTxScreen.prototype.checkBalanceAgainstTx = function (txData) {
+ if (!txData.txParams) return false
+ var state = this.props
+ var address = txData.txParams.from || state.selectedAddress
+ var account = state.accounts[address]
+ var balance = account ? account.balance : '0x0'
+ var maxCost = new BN(txData.maxCost, 16)
+
+ var balanceBn = new BN(ethUtil.stripHexPrefix(balance), 16)
+ return maxCost.gt(balanceBn)
+}
+
+ConfirmTxScreen.prototype.buyEth = function (address, event) {
+ event.stopPropagation()
+ this.props.dispatch(actions.buyEthView(address))
+}
ConfirmTxScreen.prototype.sendTransaction = function (txData, event) {
event.stopPropagation()
@@ -136,7 +173,13 @@ ConfirmTxScreen.prototype.goHome = function (event) {
}
function warningIfExists (warning) {
- if (warning) {
- return h('span.error', { style: { margin: 'auto' } }, warning)
+ if (warning &&
+ // Do not display user rejections on this screen:
+ warning.indexOf('User denied transaction signature') === -1) {
+ return h('.error', {
+ style: {
+ margin: 'auto',
+ },
+ }, warning)
}
}
diff --git a/ui/app/config.js b/ui/app/config.js
index b043a47d6..65b1ed712 100644
--- a/ui/app/config.js
+++ b/ui/app/config.js
@@ -3,12 +3,14 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('./actions')
-const currencies = require('./conversion-util').availableCurrencies.rows
+const currencies = require('./conversion.json').rows
+const validUrl = require('valid-url')
module.exports = connect(mapStateToProps)(ConfigScreen)
function mapStateToProps (state) {
return {
metamask: state.metamask,
+ warning: state.appState.warning,
}
}
@@ -20,6 +22,7 @@ function ConfigScreen () {
ConfigScreen.prototype.render = function () {
var state = this.props
var metamaskState = state.metamask
+ var warning = state.warning
return (
h('.flex-column.flex-grow', [
@@ -31,9 +34,17 @@ ConfigScreen.prototype.render = function () {
state.dispatch(actions.goHome())
},
}),
- h('h2.page-subtitle', 'Configuration'),
+ h('h2.page-subtitle', 'Settings'),
]),
+ h('.error', {
+ style: {
+ display: warning ? 'block' : 'none',
+ padding: '0 20px',
+ textAlign: 'center',
+ },
+ }, warning),
+
// conf view
h('.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-space-around', {
@@ -57,7 +68,7 @@ ConfigScreen.prototype.render = function () {
if (event.key === 'Enter') {
var element = event.target
var newRpc = element.value
- state.dispatch(actions.setRpcTarget(newRpc))
+ rpcValidation(newRpc, state)
}
},
}),
@@ -69,7 +80,7 @@ ConfigScreen.prototype.render = function () {
event.preventDefault()
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
- state.dispatch(actions.setRpcTarget(newRpc))
+ rpcValidation(newRpc, state)
},
}, 'Save'),
]),
@@ -99,6 +110,19 @@ ConfigScreen.prototype.render = function () {
)
}
+function rpcValidation (newRpc, state) {
+ if (validUrl.isWebUri(newRpc)) {
+ state.dispatch(actions.setRpcTarget(newRpc))
+ } else {
+ var appendedRpc = `http://${newRpc}`
+ if (validUrl.isWebUri(appendedRpc)) {
+ state.dispatch(actions.displayWarning('URIs require the appropriate HTTP/HTTPS prefix.'))
+ } else {
+ state.dispatch(actions.displayWarning('Invalid RPC URI'))
+ }
+ }
+}
+
function currentConversionInformation (metamaskState, state) {
var currentFiat = metamaskState.currentFiat
var conversionDate = metamaskState.conversionDate
@@ -133,7 +157,7 @@ function currentProviderDisplay (metamaskState) {
case 'testnet':
title = 'Current Network'
- value = 'Morden Test Network'
+ value = 'Ropsten Test Network'
break
default:
diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js
deleted file mode 100644
index 19259602a..000000000
--- a/ui/app/conversion-util.js
+++ /dev/null
@@ -1,5 +0,0 @@
-var availableCurrencies = {"rows":[{"code":"007","name":"007","statuses":["primary"]},{"code":"1337","name":"1337","statuses":["primary"]},{"code":"1CR","name":"1CR","statuses":["primary"]},{"code":"256","name":"256","statuses":["primary"]},{"code":"2FLAV","name":"2FLAV","statuses":["primary"]},{"code":"2GIVE","name":"2GIVE","statuses":["primary"]},{"code":"404","name":"404","statuses":["primary"]},{"code":"611","name":"611","statuses":["primary"]},{"code":"888","name":"888","statuses":["primary"]},{"code":"8BIT","name":"8Bit","statuses":["primary"]},{"code":"ACLR","name":"ACLR","statuses":["primary"]},{"code":"ACOIN","name":"ACOIN","statuses":["primary"]},{"code":"ACP","name":"ACP","statuses":["primary"]},{"code":"ADC","name":"ADC","statuses":["primary"]},{"code":"ADZ","name":"Adzcoin","statuses":["primary"]},{"code":"AEC","name":"AEC","statuses":["primary"]},{"code":"AEON","name":"Aeon","statuses":["primary"]},{"code":"AGRS","name":"Agoras Tokens","statuses":["primary"]},{"code":"AIB","name":"AIB","statuses":["primary"]},{"code":"ADN","name":"Aiden","statuses":["primary"]},{"code":"AIR","name":"AIR","statuses":["primary"]},{"code":"ALC","name":"ALC","statuses":["primary"]},{"code":"ALTC","name":"ALTC","statuses":["primary"]},{"code":"AM","name":"AM","statuses":["primary"]},{"code":"AMBER","name":"AMBER","statuses":["primary"]},{"code":"AMS","name":"AMS","statuses":["primary"]},{"code":"ANAL","name":"ANAL","statuses":["primary"]},{"code":"AND","name":"AND","statuses":["primary"]},{"code":"ANI","name":"ANI","statuses":["primary"]},{"code":"ANC","name":"Anoncoin","statuses":["primary"]},{"code":"ANTI","name":"AntiBitcoin","statuses":["primary"]},{"code":"APEX","name":"APEX","statuses":["primary"]},{"code":"APC","name":"Applecoin","statuses":["primary"]},{"code":"APT","name":"APT","statuses":["primary"]},{"code":"AR2","name":"AR2","statuses":["primary"]},{"code":"ARB","name":"ARB","statuses":["primary"]},{"code":"ARC","name":"ARC","statuses":["primary"]},{"code":"ARCH","name":"ARCH","statuses":["primary"]},{"code":"ABY","name":"ArtByte","statuses":["primary"]},{"code":"ARTC","name":"ARTC","statuses":["primary"]},{"code":"ADCN","name":"Asiadigicoin","statuses":["primary"]},{"code":"ATEN","name":"ATEN","statuses":["primary"]},{"code":"REP","name":"Augur","statuses":["primary"]},{"code":"AUR","name":"Auroracoin","statuses":["primary"]},{"code":"AUD","name":"Australian Dollar","statuses":["secondary"]},{"code":"AV","name":"AV","statuses":["primary"]},{"code":"BA","name":"BA","statuses":["primary"]},{"code":"BAC","name":"BAC","statuses":["primary"]},{"code":"BTA","name":"Bata","statuses":["primary"]},{"code":"BAY","name":"BAY","statuses":["primary"]},{"code":"BBCC","name":"BBCC","statuses":["primary"]},{"code":"BQC","name":"BBQCoin","statuses":["primary"]},{"code":"BDC","name":"BDC","statuses":["primary"]},{"code":"BEC","name":"BEC","statuses":["primary"]},{"code":"BEEZ","name":"BEEZ","statuses":["primary"]},{"code":"BELA","name":"BellaCoin","statuses":["primary"]},{"code":"BERN","name":"BERNcash","statuses":["primary"]},{"code":"BILL","name":"BILL","statuses":["primary"]},{"code":"BILS","name":"BILS","statuses":["primary"]},{"code":"BIOS","name":"BiosCrypto","statuses":["primary"]},{"code":"BIT","name":"BIT","statuses":["primary"]},{"code":"BIT16","name":"BIT16","statuses":["primary"]},{"code":"BITB","name":"BitBean","statuses":["primary"]},{"code":"BTC","name":"Bitcoin","statuses":["primary","secondary"]},{"code":"XBC","name":"Bitcoin Plus","statuses":["primary"]},{"code":"BTCD","name":"BitcoinDark","statuses":["primary"]},{"code":"BCY","name":"Bitcrystals","statuses":["primary"]},{"code":"BTM","name":"Bitmark","statuses":["primary"]},{"code":"BTQ","name":"BitQuark","statuses":["primary"]},{"code":"BITS","name":"BITS","statuses":["primary"]},{"code":"BSD","name":"BitSend","statuses":["primary"]},{"code":"BTS","name":"BitShares","statuses":["primary"]},{"code":"PTS","name":"BitShares PTS","statuses":["primary"]},{"code":"SWIFT","name":"BitSwift","statuses":["primary"]},{"code":"BITZ","name":"Bitz","statuses":["primary"]},{"code":"BLK","name":"Blackcoin","statuses":["primary"]},{"code":"JACK","name":"BlackJack","statuses":["primary"]},{"code":"BLC","name":"Blakecoin","statuses":["primary"]},{"code":"BLEU","name":"BLEU","statuses":["primary"]},{"code":"BLITZ","name":"Blitzcoin","statuses":["primary"]},{"code":"BLOCK","name":"Blocknet","statuses":["primary"]},{"code":"BLRY","name":"BLRY","statuses":["primary"]},{"code":"BLU","name":"BLU","statuses":["primary"]},{"code":"BM","name":"BM","statuses":["primary"]},{"code":"BNT","name":"BNT","statuses":["primary"]},{"code":"BOB","name":"BOB","statuses":["primary"]},{"code":"BON","name":"BON","statuses":["primary"]},{"code":"BBR","name":"Boolberry","statuses":["primary"]},{"code":"BOST","name":"BoostCoin","statuses":["primary"]},{"code":"BOSS","name":"BOSS","statuses":["primary"]},{"code":"BPOK","name":"BPOK","statuses":["primary"]},{"code":"BRAIN","name":"BRAIN","statuses":["primary"]},{"code":"BRC","name":"BRC","statuses":["primary"]},{"code":"BRDD","name":"BRDD","statuses":["primary"]},{"code":"BRIT","name":"BRIT","statuses":["primary"]},{"code":"GBP","name":"British Pound Sterling","statuses":["secondary"]},{"code":"BRK","name":"BRK","statuses":["primary"]},{"code":"BRX","name":"BRX","statuses":["primary"]},{"code":"BSC","name":"BSC","statuses":["primary"]},{"code":"BST","name":"BST","statuses":["primary"]},{"code":"BTCHC","name":"BTCHC","statuses":["primary"]},{"code":"BTCR","name":"BTCR","statuses":["primary"]},{"code":"BTCS","name":"BTCS","statuses":["primary"]},{"code":"BTCU","name":"BTCU","statuses":["primary"]},{"code":"BTTF","name":"BTTF","statuses":["primary"]},{"code":"BTX","name":"BTX","statuses":["primary"]},{"code":"BUCKS","name":"BUCKS","statuses":["primary"]},{"code":"BUN","name":"BUN","statuses":["primary"]},{"code":"BURST","name":"Burst","statuses":["primary"]},{"code":"BUZZ","name":"BUZZ","statuses":["primary"]},{"code":"BVC","name":"BVC","statuses":["primary"]},{"code":"BYC","name":"Bytecent","statuses":["primary"]},{"code":"BCN","name":"Bytecoin","statuses":["primary"]},{"code":"XCT","name":"C-Bit","statuses":["primary"]},{"code":"C0C0","name":"C0C0","statuses":["primary"]},{"code":"CAB","name":"Cabbage Unit","statuses":["primary"]},{"code":"CAD","name":"CAD","statuses":["primary","secondary"]},{"code":"CAGE","name":"CAGE","statuses":["primary"]},{"code":"CANN","name":"CannabisCoin","statuses":["primary"]},{"code":"CCN","name":"Cannacoin","statuses":["primary"]},{"code":"CPC","name":"Capricoin","statuses":["primary"]},{"code":"DIEM","name":"CarpeDiemCoin","statuses":["primary"]},{"code":"CASH","name":"CASH","statuses":["primary"]},{"code":"CBIT","name":"CBIT","statuses":["primary"]},{"code":"CC","name":"CC","statuses":["primary"]},{"code":"CCB","name":"CCB","statuses":["primary"]},{"code":"CD","name":"CD","statuses":["primary"]},{"code":"CDN","name":"CDN","statuses":["primary"]},{"code":"CF","name":"CF","statuses":["primary"]},{"code":"CFC","name":"CFC","statuses":["primary"]},{"code":"CGA","name":"CGA","statuses":["primary"]},{"code":"CHC","name":"CHC","statuses":["primary"]},{"code":"CKC","name":"Checkcoin","statuses":["primary"]},{"code":"CHEMX","name":"CHEMX","statuses":["primary"]},{"code":"CHESS","name":"CHESS","statuses":["primary"]},{"code":"CHF","name":"CHF","statuses":["primary","secondary"]},{"code":"CNY","name":"Chinese Yuan","statuses":["secondary"]},{"code":"CHRG","name":"CHRG","statuses":["primary"]},{"code":"CJ","name":"CJ","statuses":["primary"]},{"code":"CLAM","name":"Clams","statuses":["primary"]},{"code":"CLICK","name":"CLICK","statuses":["primary"]},{"code":"CLINT","name":"CLINT","statuses":["primary"]},{"code":"CLOAK","name":"Cloakcoin","statuses":["primary"]},{"code":"CLR","name":"CLR","statuses":["primary"]},{"code":"CLUB","name":"CLUB","statuses":["primary"]},{"code":"CLUD","name":"CLUD","statuses":["primary"]},{"code":"CMT","name":"CMT","statuses":["primary"]},{"code":"CNC","name":"CNC","statuses":["primary"]},{"code":"COXST","name":"CoExistCoin","statuses":["primary"]},{"code":"COIN","name":"COIN","statuses":["primary"]},{"code":"C2","name":"Coin2.1","statuses":["primary"]},{"code":"CNMT","name":"Coinomat","statuses":["primary"]},{"code":"CV2","name":"Colossuscoin2.0","statuses":["primary"]},{"code":"CON","name":"CON","statuses":["primary"]},{"code":"XCP","name":"Counterparty","statuses":["primary"]},{"code":"COV","name":"COV","statuses":["primary"]},{"code":"CRAFT","name":"CRAFT","statuses":["primary"]},{"code":"CRAVE","name":"CRAVE","statuses":["primary"]},{"code":"CRC","name":"CRC","statuses":["primary"]},{"code":"CRE","name":"CRE","statuses":["primary"]},{"code":"CRBIT","name":"Creditbit","statuses":["primary"]},{"code":"CREVA","name":"CrevaCoin","statuses":["primary"]},{"code":"CRIME","name":"CRIME","statuses":["primary"]},{"code":"CRT","name":"CRT","statuses":["primary"]},{"code":"CRW","name":"CRW","statuses":["primary"]},{"code":"CRY","name":"CRY","statuses":["primary"]},{"code":"XCR","name":"Crypti","statuses":["primary"]},{"code":"CBX","name":"Crypto Bullion","statuses":["primary"]},{"code":"CESC","name":"CryptoEscudo","statuses":["primary"]},{"code":"XCN","name":"Cryptonite","statuses":["primary"]},{"code":"CSMIC","name":"CSMIC","statuses":["primary"]},{"code":"CST","name":"CST","statuses":["primary"]},{"code":"CTC","name":"CTC","statuses":["primary"]},{"code":"CTO","name":"CTO","statuses":["primary"]},{"code":"CURE","name":"Curecoin","statuses":["primary"]},{"code":"CYP","name":"Cypher","statuses":["primary"]},{"code":"CZC","name":"CZC","statuses":["primary"]},{"code":"CZECO","name":"CZECO","statuses":["primary"]},{"code":"CZR","name":"CZR","statuses":["primary"]},{"code":"DAO","name":"DAO","statuses":["primary"]},{"code":"DGD","name":"DarkGoldCoin","statuses":["primary"]},{"code":"DNET","name":"Darknet","statuses":["primary"]},{"code":"DASH","name":"Dash","statuses":["primary"]},{"code":"DTC","name":"Datacoin","statuses":["primary"]},{"code":"DBG","name":"DBG","statuses":["primary"]},{"code":"DBLK","name":"DBLK","statuses":["primary"]},{"code":"DBTC","name":"DBTC","statuses":["primary"]},{"code":"DCK","name":"DCK","statuses":["primary"]},{"code":"DCR","name":"Decred","statuses":["primary"]},{"code":"DES","name":"Destiny","statuses":["primary"]},{"code":"DETH","name":"DETH","statuses":["primary"]},{"code":"DEUR","name":"DEUR","statuses":["primary"]},{"code":"DEM","name":"Deutsche eMark","statuses":["primary"]},{"code":"DVC","name":"Devcoin","statuses":["primary"]},{"code":"DGCS","name":"DGCS","statuses":["primary"]},{"code":"DGMS","name":"DGMS","statuses":["primary"]},{"code":"DGORE","name":"DGORE","statuses":["primary"]},{"code":"DMD","name":"Diamond","statuses":["primary"]},{"code":"DGB","name":"Digibyte","statuses":["primary"]},{"code":"CUBE","name":"DigiCube","statuses":["primary"]},{"code":"DGC","name":"Digitalcoin","statuses":["primary"]},{"code":"XDN","name":"DigitalNote","statuses":["primary"]},{"code":"DP","name":"DigitalPrice","statuses":["primary"]},{"code":"DIGS","name":"DIGS","statuses":["primary"]},{"code":"DIME","name":"Dimecoin","statuses":["primary"]},{"code":"DISK","name":"DISK","statuses":["primary"]},{"code":"DLISK","name":"DLISK","statuses":["primary"]},{"code":"NOTE","name":"DNotes","statuses":["primary"]},{"code":"DOGE","name":"Dogecoin","statuses":["primary","secondary"]},{"code":"DON","name":"DON","statuses":["primary"]},{"code":"DOPE","name":"DopeCoin","statuses":["primary"]},{"code":"DOX","name":"DOX","statuses":["primary"]},{"code":"DRACO","name":"DRACO","statuses":["primary"]},{"code":"DRM","name":"DRM","statuses":["primary"]},{"code":"DROP","name":"DROP","statuses":["primary"]},{"code":"DRZ","name":"DRZ","statuses":["primary"]},{"code":"DSH","name":"DSH","statuses":["primary"]},{"code":"DBIC","name":"DubaiCoin","statuses":["primary"]},{"code":"DUO","name":"DUO","statuses":["primary"]},{"code":"DUST","name":"DUST","statuses":["primary"]},{"code":"EAC","name":"Earthcoin","statuses":["primary"]},{"code":"ECCHI","name":"ECCHI","statuses":["primary"]},{"code":"ECC","name":"ECCoin","statuses":["primary"]},{"code":"ECOS","name":"ECOS","statuses":["primary"]},{"code":"EDC","name":"EDC","statuses":["primary"]},{"code":"EDRC","name":"EDRC","statuses":["primary"]},{"code":"EGG","name":"EGG","statuses":["primary"]},{"code":"EMC2","name":"Einsteinium","statuses":["primary"]},{"code":"EKO","name":"EKO","statuses":["primary"]},{"code":"EL","name":"EL","statuses":["primary"]},{"code":"ELCO","name":"ELcoin","statuses":["primary"]},{"code":"ELE","name":"ELE","statuses":["primary"]},{"code":"EFL","name":"Electronic Gulden","statuses":["primary"]},{"code":"EMC","name":"Emercoin","statuses":["primary"]},{"code":"EMIRG","name":"EMIRG","statuses":["primary"]},{"code":"ENE","name":"ENE","statuses":["primary"]},{"code":"ENRG","name":"Energycoin","statuses":["primary"]},{"code":"EPC","name":"EPC","statuses":["primary"]},{"code":"EPY","name":"EPY","statuses":["primary"]},{"code":"ERC","name":"ERC","statuses":["primary"]},{"code":"ERC3","name":"ERC3","statuses":["primary"]},{"code":"ESC","name":"ESC","statuses":["primary"]},{"code":"ETC","name":"Ethereum Classic","statuses":["primary"]},{"code":"ETHS","name":"ETHS","statuses":["primary"]},{"code":"EURC","name":"EURC","statuses":["primary"]},{"code":"EUR","name":"Euro","statuses":["primary","secondary"]},{"code":"EGC","name":"EvergreenCoin","statuses":["primary"]},{"code":"EVIL","name":"EVIL","statuses":["primary"]},{"code":"EVO","name":"EVO","statuses":["primary"]},{"code":"EXCL","name":"EXCL","statuses":["primary"]},{"code":"EXIT","name":"EXIT","statuses":["primary"]},{"code":"EXP","name":"Expanse","statuses":["primary"]},{"code":"FCT","name":"Factom","statuses":["primary"]},{"code":"FAIR","name":"Faircoin","statuses":["primary"]},{"code":"FC2","name":"FC2","statuses":["primary"]},{"code":"FCN","name":"FCN","statuses":["primary"]},{"code":"FTC","name":"Feathercoin","statuses":["primary"]},{"code":"TIPS","name":"Fedoracoin","statuses":["primary"]},{"code":"FFC","name":"FFC","statuses":["primary"]},{"code":"FIBRE","name":"Fibre","statuses":["primary"]},{"code":"FIT","name":"FIT","statuses":["primary"]},{"code":"FJC","name":"FJC","statuses":["primary"]},{"code":"FLO","name":"Florincoin","statuses":["primary"]},{"code":"FLOZ","name":"FLOZ","statuses":["primary"]},{"code":"FLT","name":"FlutterCoin","statuses":["primary"]},{"code":"FLX","name":"FLX","statuses":["primary"]},{"code":"FLY","name":"Flycoin","statuses":["primary"]},{"code":"FLDC","name":"FoldingCoin","statuses":["primary"]},{"code":"FONZ","name":"FONZ","statuses":["primary"]},{"code":"FRK","name":"Franko","statuses":["primary"]},{"code":"FRC","name":"Freicoin","statuses":["primary"]},{"code":"FRN","name":"FRN","statuses":["primary"]},{"code":"FRWC","name":"FRWC","statuses":["primary"]},{"code":"FSC2","name":"FSC2","statuses":["primary"]},{"code":"FST","name":"FST","statuses":["primary"]},{"code":"FTP","name":"FTP","statuses":["primary"]},{"code":"FUN","name":"FUN","statuses":["primary"]},{"code":"FUTC","name":"FUTC","statuses":["primary"]},{"code":"FUZZ","name":"FUZZ","statuses":["primary"]},{"code":"GAIA","name":"GAIA","statuses":["primary"]},{"code":"GAIN","name":"GAIN","statuses":["primary"]},{"code":"GAKH","name":"GAKH","statuses":["primary"]},{"code":"GAM","name":"GAM","statuses":["primary"]},{"code":"GBT","name":"GameBet Coin","statuses":["primary"]},{"code":"GAME","name":"GameCredits","statuses":["primary"]},{"code":"GAP","name":"Gapcoin","statuses":["primary"]},{"code":"GARY","name":"GARY","statuses":["primary"]},{"code":"GB","name":"GB","statuses":["primary"]},{"code":"GBC","name":"GBC","statuses":["primary"]},{"code":"GBIT","name":"GBIT","statuses":["primary"]},{"code":"GCC","name":"GCC","statuses":["primary"]},{"code":"GCN","name":"GCN","statuses":["primary"]},{"code":"GEO","name":"GeoCoin","statuses":["primary"]},{"code":"GEMZ","name":"GetGems","statuses":["primary"]},{"code":"GHOST","name":"GHOST","statuses":["primary"]},{"code":"GHS","name":"GHS","statuses":["primary"]},{"code":"GIFT","name":"GIFT","statuses":["primary"]},{"code":"GIG","name":"GIG","statuses":["primary"]},{"code":"GLC","name":"GLC","statuses":["primary"]},{"code":"BSTY","name":"GlobalBoost-Y","statuses":["primary"]},{"code":"GML","name":"GML","statuses":["primary"]},{"code":"GMX","name":"GMX","statuses":["primary"]},{"code":"GCR","name":"GoCoineR","statuses":["primary"]},{"code":"GLD","name":"GoldCoin","statuses":["primary"]},{"code":"GOON","name":"GOON","statuses":["primary"]},{"code":"GP","name":"GP","statuses":["primary"]},{"code":"GPU","name":"GPU","statuses":["primary"]},{"code":"GRAM","name":"GRAM","statuses":["primary"]},{"code":"GRT","name":"Grantcoin","statuses":["primary"]},{"code":"GRE","name":"GRE","statuses":["primary"]},{"code":"GRC","name":"Gridcoin","statuses":["primary"]},{"code":"GRN","name":"GRN","statuses":["primary"]},{"code":"GRS","name":"Groestlcoin","statuses":["primary"]},{"code":"GRW","name":"GRW","statuses":["primary"]},{"code":"GSM","name":"GSM","statuses":["primary"]},{"code":"GSX","name":"GSX","statuses":["primary"]},{"code":"GUA","name":"GUA","statuses":["primary"]},{"code":"NLG","name":"Gulden","statuses":["primary"]},{"code":"GUN","name":"GUN","statuses":["primary"]},{"code":"HAM","name":"HAM","statuses":["primary"]},{"code":"HAWK","name":"HAWK","statuses":["primary"]},{"code":"HCC","name":"HCC","statuses":["primary"]},{"code":"HEAT","name":"HEAT","statuses":["primary"]},{"code":"HMP","name":"HempCoin","statuses":["primary"]},{"code":"XHI","name":"HiCoin","statuses":["primary"]},{"code":"HIFUN","name":"HIFUN","statuses":["primary"]},{"code":"HILL","name":"HILL","statuses":["primary"]},{"code":"HIRE","name":"HIRE","statuses":["primary"]},{"code":"HNC","name":"HNC","statuses":["primary"]},{"code":"HODL","name":"HOdlcoin","statuses":["primary"]},{"code":"HKD","name":"Hong Kong Dollar","statuses":["secondary"]},{"code":"HZ","name":"Horizon","statuses":["primary"]},{"code":"HTC","name":"HTC","statuses":["primary"]},{"code":"HTML5","name":"HTMLCOIN","statuses":["primary"]},{"code":"HUC","name":"HUC","statuses":["primary"]},{"code":"HVCO","name":"HVCO","statuses":["primary"]},{"code":"HYPER","name":"Hyper","statuses":["primary"]},{"code":"HYP","name":"HyperStake","statuses":["primary"]},{"code":"I0C","name":"I0C","statuses":["primary"]},{"code":"IBANK","name":"IBANK","statuses":["primary"]},{"code":"ICASH","name":"iCash","statuses":["primary"]},{"code":"ICN","name":"ICN","statuses":["primary"]},{"code":"IEC","name":"IEC","statuses":["primary"]},{"code":"IFC","name":"Infinitecoin","statuses":["primary"]},{"code":"INFX","name":"Influxcoin","statuses":["primary"]},{"code":"INV","name":"INV","statuses":["primary"]},{"code":"IOC","name":"IO Coin","statuses":["primary"]},{"code":"ION","name":"ION","statuses":["primary"]},{"code":"IRL","name":"IRL","statuses":["primary"]},{"code":"ISL","name":"IslaCoin","statuses":["primary"]},{"code":"IVZ","name":"IVZ","statuses":["primary"]},{"code":"IXC","name":"IXC","statuses":["primary"]},{"code":"JIF","name":"JIF","statuses":["primary"]},{"code":"JPC","name":"JPC","statuses":["primary"]},{"code":"JPY","name":"JPY","statuses":["primary","secondary"]},{"code":"JBS","name":"Jumbucks","statuses":["primary"]},{"code":"KAT","name":"KAT","statuses":["primary"]},{"code":"KGC","name":"KGC","statuses":["primary"]},{"code":"KNC","name":"KhanCoin","statuses":["primary"]},{"code":"KLC","name":"KLC","statuses":["primary"]},{"code":"KOBO","name":"KOBO","statuses":["primary"]},{"code":"KORE","name":"KoreCoin","statuses":["primary"]},{"code":"KRAK","name":"KRAK","statuses":["primary"]},{"code":"KRYP","name":"KRYP","statuses":["primary"]},{"code":"KR","name":"Krypton","statuses":["primary"]},{"code":"KTK","name":"KTK","statuses":["primary"]},{"code":"KUBO","name":"KUBO","statuses":["primary"]},{"code":"LANA","name":"LANA","statuses":["primary"]},{"code":"LBC","name":"LBC","statuses":["primary"]},{"code":"LC","name":"LC","statuses":["primary"]},{"code":"LEA","name":"LeaCoin","statuses":["primary"]},{"code":"LEMON","name":"LEMON","statuses":["primary"]},{"code":"LEO","name":"LEO","statuses":["primary"]},{"code":"LFC","name":"LFC","statuses":["primary"]},{"code":"LFO","name":"LFO","statuses":["primary"]},{"code":"LFTC","name":"LFTC","statuses":["primary"]},{"code":"LQD","name":"LIQUID","statuses":["primary"]},{"code":"LIR","name":"LIR","statuses":["primary"]},{"code":"LSK","name":"Lisk","statuses":["primary"]},{"code":"LTC","name":"Litecoin","statuses":["primary","secondary"]},{"code":"LTCR","name":"Litecred","statuses":["primary"]},{"code":"LDOGE","name":"LiteDoge","statuses":["primary"]},{"code":"LKC","name":"LKC","statuses":["primary"]},{"code":"LOC","name":"LOC","statuses":["primary"]},{"code":"LOOT","name":"LOOT","statuses":["primary"]},{"code":"LTBC","name":"LTBcoin","statuses":["primary"]},{"code":"LTH","name":"LTH","statuses":["primary"]},{"code":"LTS","name":"LTS","statuses":["primary"]},{"code":"LUN","name":"LUN","statuses":["primary"]},{"code":"LXC","name":"LXC","statuses":["primary"]},{"code":"LYB","name":"LYB","statuses":["primary"]},{"code":"M1","name":"M1","statuses":["primary"]},{"code":"MAD","name":"MAD","statuses":["primary"]},{"code":"XMG","name":"Magi","statuses":["primary"]},{"code":"MAID","name":"MaidSafeCoin","statuses":["primary"]},{"code":"MXT","name":"MarteXcoin","statuses":["primary"]},{"code":"MARV","name":"MARV","statuses":["primary"]},{"code":"MARYJ","name":"MARYJ","statuses":["primary"]},{"code":"OMNI","name":"Mastercoin (Omni)","statuses":["primary"]},{"code":"MTR","name":"MasterTraderCoin","statuses":["primary"]},{"code":"MAX","name":"Maxcoin","statuses":["primary"]},{"code":"MZC","name":"Mazacoin","statuses":["primary"]},{"code":"MBL","name":"MBL","statuses":["primary"]},{"code":"MCAR","name":"MCAR","statuses":["primary"]},{"code":"MCN","name":"MCN","statuses":["primary"]},{"code":"MCZ","name":"MCZ","statuses":["primary"]},{"code":"MED","name":"MediterraneanCoin","statuses":["primary"]},{"code":"MEC","name":"Megacoin","statuses":["primary"]},{"code":"MEME","name":"Memetic","statuses":["primary"]},{"code":"METAL","name":"METAL","statuses":["primary"]},{"code":"MND","name":"MindCoin","statuses":["primary"]},{"code":"MINT","name":"Mintcoin","statuses":["primary"]},{"code":"MIS","name":"MIS","statuses":["primary"]},{"code":"MM","name":"MM","statuses":["primary"]},{"code":"MMC","name":"MMC","statuses":["primary"]},{"code":"MMNXT","name":"MMNXT","statuses":["primary"]},{"code":"MMXVI","name":"MMXVI","statuses":["primary"]},{"code":"MNM","name":"MNM","statuses":["primary"]},{"code":"MOIN","name":"MOIN","statuses":["primary"]},{"code":"MOJO","name":"MojoCoin","statuses":["primary"]},{"code":"MONA","name":"MonaCoin","statuses":["primary"]},{"code":"XMR","name":"Monero","statuses":["primary","secondary"]},{"code":"MNTA","name":"Moneta","statuses":["primary"]},{"code":"MUE","name":"MonetaryUnit","statuses":["primary"]},{"code":"MOON","name":"Mooncoin","statuses":["primary"]},{"code":"MOOND","name":"MOOND","statuses":["primary"]},{"code":"MOTO","name":"MOTO","statuses":["primary"]},{"code":"MPRO","name":"MPRO","statuses":["primary"]},{"code":"MRB","name":"MRB","statuses":["primary"]},{"code":"MRP","name":"MRP","statuses":["primary"]},{"code":"MSC","name":"MSC","statuses":["primary"]},{"code":"MYR","name":"Myriadcoin","statuses":["primary"]},{"code":"NMC","name":"Namecoin","statuses":["primary"]},{"code":"NAUT","name":"Nautiluscoin","statuses":["primary"]},{"code":"NAV","name":"NAV Coin","statuses":["primary"]},{"code":"NCS","name":"NCS","statuses":["primary"]},{"code":"XEM","name":"NEM","statuses":["primary"]},{"code":"NEOS","name":"NeosCoin","statuses":["primary"]},{"code":"NETC","name":"NETC","statuses":["primary"]},{"code":"NET","name":"NetCoin","statuses":["primary"]},{"code":"NEU","name":"NeuCoin","statuses":["primary"]},{"code":"NTRN","name":"Neutron","statuses":["primary"]},{"code":"NEVA","name":"NevaCoin","statuses":["primary"]},{"code":"NEWB","name":"NEWB","statuses":["primary"]},{"code":"NIRO","name":"Nexus","statuses":["primary"]},{"code":"NIC","name":"NIC","statuses":["primary"]},{"code":"NKA","name":"NKA","statuses":["primary"]},{"code":"NKC","name":"NKC","statuses":["primary"]},{"code":"NOBL","name":"NobleCoin","statuses":["primary"]},{"code":"NODE","name":"NODE","statuses":["primary"]},{"code":"NODES","name":"NODES","statuses":["primary"]},{"code":"NOO","name":"NOO","statuses":["primary"]},{"code":"NVC","name":"Novacoin","statuses":["primary"]},{"code":"NRC","name":"NRC","statuses":["primary"]},{"code":"NRS","name":"NRS","statuses":["primary"]},{"code":"NUBIS","name":"NUBIS","statuses":["primary"]},{"code":"NBT","name":"NuBits","statuses":["primary"]},{"code":"NUM","name":"NUM","statuses":["primary"]},{"code":"NSR","name":"NuShares","statuses":["primary"]},{"code":"NXE","name":"NXE","statuses":["primary"]},{"code":"NXT","name":"NXT","statuses":["primary"]},{"code":"NXTTY","name":"Nxttycoin","statuses":["primary"]},{"code":"NYC","name":"NYC","statuses":["primary"]},{"code":"NZC","name":"NZC","statuses":["primary"]},{"code":"NZD","name":"NZD","statuses":["primary","secondary"]},{"code":"OC","name":"OC","statuses":["primary"]},{"code":"OCOW","name":"OCOW","statuses":["primary"]},{"code":"OK","name":"OKCash","statuses":["primary"]},{"code":"OMA","name":"OMA","statuses":["primary"]},{"code":"ONE","name":"ONE","statuses":["primary"]},{"code":"ONEC","name":"ONEC","statuses":["primary"]},{"code":"OP","name":"OP","statuses":["primary"]},{"code":"OPAL","name":"OPAL","statuses":["primary"]},{"code":"OPES","name":"OPES","statuses":["primary"]},{"code":"ORB","name":"Orbitcoin","statuses":["primary"]},{"code":"ORLY","name":"Orlycoin","statuses":["primary"]},{"code":"OS76","name":"OS76","statuses":["primary"]},{"code":"OZC","name":"OZC","statuses":["primary"]},{"code":"PAC","name":"PAC","statuses":["primary"]},{"code":"PAK","name":"PAK","statuses":["primary"]},{"code":"PND","name":"Pandacoin","statuses":["primary"]},{"code":"PAPAF","name":"PAPAF","statuses":["primary"]},{"code":"XPY","name":"Paycoin","statuses":["primary"]},{"code":"PBC","name":"PBC","statuses":["primary"]},{"code":"PDC","name":"PDC","statuses":["primary"]},{"code":"XPB","name":"Pebblecoin","statuses":["primary"]},{"code":"PPC","name":"Peercoin","statuses":["primary"]},{"code":"PEN","name":"PEN","statuses":["primary"]},{"code":"PHR","name":"PHR","statuses":["primary"]},{"code":"PIGGY","name":"Piggycoin","statuses":["primary"]},{"code":"PC","name":"Pinkcoin","statuses":["primary"]},{"code":"PKB","name":"PKB","statuses":["primary"]},{"code":"PLN","name":"PLN","statuses":["primary","secondary"]},{"code":"PLNC","name":"PLNC","statuses":["primary"]},{"code":"PNC","name":"PNC","statuses":["primary"]},{"code":"PNK","name":"PNK","statuses":["primary"]},{"code":"POKE","name":"POKE","statuses":["primary"]},{"code":"PONZ2","name":"PONZ2","statuses":["primary"]},{"code":"PONZI","name":"PONZI","statuses":["primary"]},{"code":"PEX","name":"PosEx","statuses":["primary"]},{"code":"POST","name":"POST","statuses":["primary"]},{"code":"POT","name":"Potcoin","statuses":["primary"]},{"code":"PRES","name":"PRES","statuses":["primary"]},{"code":"PXI","name":"Prime-XI","statuses":["primary"]},{"code":"PRIME","name":"PrimeChain","statuses":["primary"]},{"code":"XPM","name":"Primecoin","statuses":["primary"]},{"code":"PRM","name":"PRM","statuses":["primary"]},{"code":"PRT","name":"PRT","statuses":["primary"]},{"code":"PSP","name":"PSP","statuses":["primary"]},{"code":"PTC","name":"PTC","statuses":["primary"]},{"code":"PULSE","name":"PULSE","statuses":["primary"]},{"code":"PURE","name":"PURE","statuses":["primary"]},{"code":"PUTIN","name":"PUTIN","statuses":["primary"]},{"code":"PWR","name":"PWR","statuses":["primary"]},{"code":"PXL","name":"PXL","statuses":["primary"]},{"code":"QBC","name":"QBC","statuses":["primary"]},{"code":"QBK","name":"QBK","statuses":["primary"]},{"code":"QCN","name":"QCN","statuses":["primary"]},{"code":"QORA","name":"Qora","statuses":["primary"]},{"code":"QTZ","name":"QTZ","statuses":["primary"]},{"code":"QRK","name":"Quark","statuses":["primary"]},{"code":"QTL","name":"Quatloo","statuses":["primary"]},{"code":"RADI","name":"RADI","statuses":["primary"]},{"code":"RADS","name":"Radium","statuses":["primary"]},{"code":"RED","name":"RED","statuses":["primary"]},{"code":"RDD","name":"Reddcoin","statuses":["primary"]},{"code":"REE","name":"REE","statuses":["primary"]},{"code":"REV","name":"Revenu","statuses":["primary"]},{"code":"RBR","name":"RibbitRewards","statuses":["primary"]},{"code":"RICHX","name":"RICHX","statuses":["primary"]},{"code":"RIC","name":"Riecoin","statuses":["primary"]},{"code":"RBT","name":"Rimbit","statuses":["primary"]},{"code":"RIO","name":"RIO","statuses":["primary"]},{"code":"XRP","name":"Ripple","statuses":["primary"]},{"code":"RISE","name":"RISE","statuses":["primary"]},{"code":"RMS","name":"RMS","statuses":["primary"]},{"code":"RONIN","name":"RONIN","statuses":["primary"]},{"code":"ROOT","name":"ROOT","statuses":["primary"]},{"code":"ROS","name":"RosCoin","statuses":["primary"]},{"code":"RPC","name":"RPC","statuses":["primary"]},{"code":"RBIES","name":"Rubies","statuses":["primary"]},{"code":"RUBIT","name":"RUBIT","statuses":["primary"]},{"code":"RUR","name":"Ruble","statuses":["secondary"]},{"code":"RBY","name":"Rubycoin","statuses":["primary"]},{"code":"RUST","name":"RUST","statuses":["primary"]},{"code":"SEC","name":"Safe Exchange Coin","statuses":["primary"]},{"code":"SAK","name":"SAK","statuses":["primary"]},{"code":"SAR","name":"SAR","statuses":["primary"]},{"code":"SBD","name":"SBD","statuses":["primary"]},{"code":"SBIT","name":"SBIT","statuses":["primary"]},{"code":"SCAN","name":"SCAN","statuses":["primary"]},{"code":"SCOT","name":"Scotcoin","statuses":["primary"]},{"code":"SCRPT","name":"SCRPT","statuses":["primary"]},{"code":"SCRT","name":"SCRT","statuses":["primary"]},{"code":"SRC","name":"SecureCoin","statuses":["primary"]},{"code":"SXC","name":"Sexcoin","statuses":["primary"]},{"code":"SFE","name":"SFE","statuses":["primary"]},{"code":"SFR","name":"SFR","statuses":["primary"]},{"code":"SGD","name":"SGD","statuses":["primary","secondary"]},{"code":"SDC","name":"ShadowCash","statuses":["primary"]},{"code":"SHELL","name":"SHELL","statuses":["primary"]},{"code":"SHF","name":"SHF","statuses":["primary"]},{"code":"SHI","name":"SHI","statuses":["primary"]},{"code":"SHIFT","name":"Shift","statuses":["primary"]},{"code":"SHREK","name":"SHREK","statuses":["primary"]},{"code":"SC","name":"Siacoin","statuses":["primary"]},{"code":"SIB","name":"Siberian chervonets","statuses":["primary"]},{"code":"SIC","name":"SIC","statuses":["primary"]},{"code":"SIGU","name":"SIGU","statuses":["primary"]},{"code":"SILK","name":"Silkcoin","statuses":["primary"]},{"code":"SIX","name":"SIX","statuses":["primary"]},{"code":"SLING","name":"Sling","statuses":["primary"]},{"code":"SLS","name":"SLS","statuses":["primary"]},{"code":"SMBR","name":"SMBR","statuses":["primary"]},{"code":"SMC","name":"SMC","statuses":["primary"]},{"code":"SMLY","name":"SmileyCoin","statuses":["primary"]},{"code":"SNRG","name":"SNRG","statuses":["primary"]},{"code":"SOIL","name":"SOILcoin","statuses":["primary"]},{"code":"SLR","name":"Solarcoin","statuses":["primary"]},{"code":"SOLO","name":"SOLO","statuses":["primary"]},{"code":"SONG","name":"SongCoin","statuses":["primary"]},{"code":"SOON","name":"SOON","statuses":["primary"]},{"code":"SPC","name":"SPC","statuses":["primary"]},{"code":"SPEX","name":"SPEX","statuses":["primary"]},{"code":"SPHR","name":"Sphere","statuses":["primary"]},{"code":"SPM","name":"SPM","statuses":["primary"]},{"code":"SPN","name":"SPN","statuses":["primary"]},{"code":"SPOTS","name":"SPOTS","statuses":["primary"]},{"code":"SPR","name":"SpreadCoin","statuses":["primary"]},{"code":"SPRTS","name":"Sprouts","statuses":["primary"]},{"code":"SQC","name":"SQC","statuses":["primary"]},{"code":"SSC","name":"SSC","statuses":["primary"]},{"code":"SSTC","name":"SSTC","statuses":["primary"]},{"code":"STA","name":"STA","statuses":["primary"]},{"code":"START","name":"Startcoin","statuses":["primary"]},{"code":"XST","name":"Stealthcoin","statuses":["primary"]},{"code":"STEEM","name":"Steem","statuses":["primary"]},{"code":"XLM","name":"Stellar","statuses":["primary"]},{"code":"STR","name":"Stellar","statuses":["primary"]},{"code":"STEPS","name":"Steps","statuses":["primary"]},{"code":"SLG","name":"Sterlingcoin","statuses":["primary"]},{"code":"STL","name":"STL","statuses":["primary"]},{"code":"SJCX","name":"Storjcoin X","statuses":["primary"]},{"code":"STP","name":"STP","statuses":["primary"]},{"code":"STRB","name":"STRB","statuses":["primary"]},{"code":"STS","name":"Stress","statuses":["primary"]},{"code":"STRP","name":"STRP","statuses":["primary"]},{"code":"STV","name":"STV","statuses":["primary"]},{"code":"SUB","name":"Subcriptio","statuses":["primary"]},{"code":"SUPER","name":"SUPER","statuses":["primary"]},{"code":"UNITY","name":"SuperNET","statuses":["primary"]},{"code":"SWARM","name":"Swarm","statuses":["primary"]},{"code":"SWING","name":"SWING","statuses":["primary"]},{"code":"SDP","name":"SydPak Coin","statuses":["primary"]},{"code":"SYNC","name":"SYNC","statuses":["primary"]},{"code":"AMP","name":"Synereo","statuses":["primary"]},{"code":"SYS","name":"Syscoin","statuses":["primary"]},{"code":"TAG","name":"TagCoin","statuses":["primary"]},{"code":"TAJ","name":"TAJ","statuses":["primary"]},{"code":"TAK","name":"TAK","statuses":["primary"]},{"code":"TAM","name":"TAM","statuses":["primary"]},{"code":"TAO","name":"TAO","statuses":["primary"]},{"code":"TBC","name":"TBC","statuses":["primary"]},{"code":"TBCX","name":"TBCX","statuses":["primary"]},{"code":"TCR","name":"TCR","statuses":["primary"]},{"code":"TDFB","name":"TDFB","statuses":["primary"]},{"code":"TDY","name":"TDY","statuses":["primary"]},{"code":"TEK","name":"TEKcoin","statuses":["primary"]},{"code":"TRC","name":"Terracoin","statuses":["primary"]},{"code":"TESLA","name":"TESLA","statuses":["primary"]},{"code":"TES","name":"TeslaCoin","statuses":["primary"]},{"code":"TET","name":"TET","statuses":["primary"]},{"code":"USDT","name":"Tether","statuses":["primary","secondary"]},{"code":"THC","name":"THC","statuses":["primary"]},{"code":"THS","name":"THS","statuses":["primary"]},{"code":"TIX","name":"Tickets","statuses":["primary"]},{"code":"XTC","name":"TileCoin","statuses":["primary"]},{"code":"TIT","name":"Titcoin","statuses":["primary"]},{"code":"TTC","name":"TittieCoin","statuses":["primary"]},{"code":"TMC","name":"TMC","statuses":["primary"]},{"code":"TODAY","name":"TODAY","statuses":["primary"]},{"code":"TOKEN","name":"TOKEN","statuses":["primary"]},{"code":"TP1","name":"TP1","statuses":["primary"]},{"code":"TPC","name":"TPC","statuses":["primary"]},{"code":"TPG","name":"TPG","statuses":["primary"]},{"code":"TX","name":"Transfercoin","statuses":["primary"]},{"code":"TRAP","name":"TRAP","statuses":["primary"]},{"code":"TRICK","name":"TRICK","statuses":["primary"]},{"code":"TROLL","name":"TROLL","statuses":["primary"]},{"code":"TRK","name":"Truckcoin","statuses":["primary"]},{"code":"TRUMP","name":"TrumpCoin","statuses":["primary"]},{"code":"TRUST","name":"TRUST","statuses":["primary"]},{"code":"UAE","name":"UAE","statuses":["primary"]},{"code":"UFO","name":"UFO Coin","statuses":["primary"]},{"code":"UIS","name":"UIS","statuses":["primary"]},{"code":"UTC","name":"UltraCoin","statuses":["primary"]},{"code":"UNC","name":"UNC","statuses":["primary"]},{"code":"UNIQ","name":"UNIQ","statuses":["primary"]},{"code":"UNIT","name":"Universal Currency","statuses":["primary"]},{"code":"UNO","name":"Unobtanium","statuses":["primary"]},{"code":"URO","name":"Uro","statuses":["primary"]},{"code":"USD","name":"US Dollar","statuses":["primary","secondary"]},{"code":"USDE","name":"USDE","statuses":["primary"]},{"code":"UTH","name":"UTH","statuses":["primary"]},{"code":"VAL","name":"VAL","statuses":["primary"]},{"code":"XVC","name":"Vcash","statuses":["primary"]},{"code":"VCN","name":"VCN","statuses":["primary"]},{"code":"VEG","name":"VEG","statuses":["primary"]},{"code":"VENE","name":"VENE","statuses":["primary"]},{"code":"XVG","name":"Verge","statuses":["primary"]},{"code":"VRC","name":"VeriCoin","statuses":["primary"]},{"code":"VTC","name":"Vertcoin","statuses":["primary"]},{"code":"VIA","name":"Viacoin","statuses":["primary"]},{"code":"VIOR","name":"Viorcoin","statuses":["primary"]},{"code":"VIP","name":"VIP Tokens","statuses":["primary"]},{"code":"VIRAL","name":"Viral","statuses":["primary"]},{"code":"VOOT","name":"VootCoin","statuses":["primary"]},{"code":"VOX","name":"Voxels","statuses":["primary"]},{"code":"VOYA","name":"VOYA","statuses":["primary"]},{"code":"VPN","name":"VPNCoin","statuses":["primary"]},{"code":"VPRC","name":"VPRC","statuses":["primary"]},{"code":"VTA","name":"VTA","statuses":["primary"]},{"code":"VTN","name":"VTN","statuses":["primary"]},{"code":"VTR","name":"VTR","statuses":["primary"]},{"code":"WAC","name":"WAC","statuses":["primary"]},{"code":"WARP","name":"WARP","statuses":["primary"]},{"code":"WAVES","name":"WAVES","statuses":["primary"]},{"code":"WGC","name":"WGC","statuses":["primary"]},{"code":"XWC","name":"Whitecoin","statuses":["primary"]},{"code":"WBB","name":"Wild Beast Block","statuses":["primary"]},{"code":"WLC","name":"WLC","statuses":["primary"]},{"code":"WMC","name":"WMC","statuses":["primary"]},{"code":"LOG","name":"Woodcoin","statuses":["primary"]},{"code":"WOP","name":"WOP","statuses":["primary"]},{"code":"WDC","name":"Worldcoin","statuses":["primary"]},{"code":"XAB","name":"XAB","statuses":["primary"]},{"code":"XAI","name":"XAI","statuses":["primary"]},{"code":"XAU","name":"Xaurum","statuses":["primary"]},{"code":"XBS","name":"XBS","statuses":["primary"]},{"code":"XBU","name":"XBU","statuses":["primary"]},{"code":"XCO","name":"XCO","statuses":["primary"]},{"code":"XC","name":"XCurrency","statuses":["primary"]},{"code":"XDB","name":"XDB","statuses":["primary"]},{"code":"XEMP","name":"XEMP","statuses":["primary"]},{"code":"XFC","name":"XFC","statuses":["primary"]},{"code":"MI","name":"Xiaomicoin","statuses":["primary"]},{"code":"XID","name":"XID","statuses":["primary"]},{"code":"XJO","name":"XJO","statuses":["primary"]},{"code":"XLTCG","name":"XLTCG","statuses":["primary"]},{"code":"XMS","name":"XMS","statuses":["primary"]},{"code":"XNX","name":"XNX","statuses":["primary"]},{"code":"XPD","name":"XPD","statuses":["primary"]},{"code":"XPOKE","name":"XPOKE","statuses":["primary"]},{"code":"XPRO","name":"XPRO","statuses":["primary"]},{"code":"XQN","name":"XQN","statuses":["primary"]},{"code":"XSEED","name":"XSEED","statuses":["primary"]},{"code":"XSP","name":"XSP","statuses":["primary"]},{"code":"XT","name":"XT","statuses":["primary"]},{"code":"XTP","name":"XTP","statuses":["primary"]},{"code":"XUSD","name":"XUSD","statuses":["primary"]},{"code":"YACC","name":"YACC","statuses":["primary"]},{"code":"YAC","name":"Yacoin","statuses":["primary"]},{"code":"YAY","name":"YAY","statuses":["primary"]},{"code":"YBC","name":"Ybcoin","statuses":["primary"]},{"code":"YOC","name":"YOC","statuses":["primary"]},{"code":"YOVI","name":"YOVI","statuses":["primary"]},{"code":"YUM","name":"YUM","statuses":["primary"]},{"code":"ZCC","name":"ZCC","statuses":["primary"]},{"code":"ZEIT","name":"Zeitcoin","statuses":["primary"]},{"code":"ZET","name":"Zetacoin","statuses":["primary"]},{"code":"ZRC","name":"ZiftrCOIN","statuses":["primary"]},{"code":"ZMC","name":"ZMC","statuses":["primary"]},{"code":"ZNY","name":"ZNY","statuses":["primary"]},{"code":"ZS","name":"ZS","statuses":["primary"]}]}
-
-module.exports = {
- availableCurrencies
-}
diff --git a/ui/app/conversion.json b/ui/app/conversion.json
new file mode 100644
index 000000000..eeca164ce
--- /dev/null
+++ b/ui/app/conversion.json
@@ -0,0 +1,5730 @@
+{
+ "rows":[
+ {
+ "code":"007",
+ "name":"007",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"1337",
+ "name":"1337",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"1CR",
+ "name":"1CR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"256",
+ "name":"256",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"2FLAV",
+ "name":"2FLAV",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"2GIVE",
+ "name":"2GIVE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"32BIT",
+ "name":"32BIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"404",
+ "name":"404",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"611",
+ "name":"611",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"888",
+ "name":"888",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"8BIT",
+ "name":"8Bit",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ACES",
+ "name":"ACES",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ACID",
+ "name":"ACID",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ACLR",
+ "name":"ACLR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ACP",
+ "name":"ACP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ADC",
+ "name":"ADC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ADZ",
+ "name":"Adzcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AEON",
+ "name":"Aeon",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AGRS",
+ "name":"Agoras Tokens",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AIB",
+ "name":"AIB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ALC",
+ "name":"ALC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ALTC",
+ "name":"ALTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AM",
+ "name":"AM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AMBER",
+ "name":"AMBER",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AMS",
+ "name":"AMS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ANAL",
+ "name":"ANAL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ANI",
+ "name":"ANI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ANC",
+ "name":"Anoncoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ANS",
+ "name":"ANS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ANTI",
+ "name":"AntiBitcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"APEX",
+ "name":"APEX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"APC",
+ "name":"Applecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"APT",
+ "name":"APT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AR2",
+ "name":"AR2",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ARB",
+ "name":"ARB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ARC",
+ "name":"ARC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ARCH",
+ "name":"ARCH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ARD",
+ "name":"ARD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ARDR",
+ "name":"ARDR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ABY",
+ "name":"ArtByte",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ARTC",
+ "name":"ARTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ASAFE",
+ "name":"ASAFE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ADCN",
+ "name":"Asiadigicoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ASN",
+ "name":"ASN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ATEN",
+ "name":"ATEN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ATOM",
+ "name":"ATOM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ATX",
+ "name":"ATX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"REP",
+ "name":"Augur",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AUR",
+ "name":"Auroracoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AUD",
+ "name":"Australian Dollar",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"AV",
+ "name":"AV",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"B2",
+ "name":"B2",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"B3",
+ "name":"B3",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BA",
+ "name":"BA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BAC",
+ "name":"BAC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BASH",
+ "name":"BASH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTA",
+ "name":"Bata",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BAY",
+ "name":"BAY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BBCC",
+ "name":"BBCC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BQC",
+ "name":"BBQCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BEC",
+ "name":"BEC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BEEP",
+ "name":"BEEP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BELA",
+ "name":"BellaCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BERN",
+ "name":"BERNcash",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BHC",
+ "name":"BHC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BILL",
+ "name":"BILL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BILS",
+ "name":"BILS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BIOS",
+ "name":"BiosCrypto",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BIT",
+ "name":"BIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BIT16",
+ "name":"BIT16",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BITB",
+ "name":"BitBean",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTC",
+ "name":"Bitcoin",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"XBC",
+ "name":"Bitcoin Plus",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTCD",
+ "name":"BitcoinDark",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BCY",
+ "name":"Bitcrystals",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BFX",
+ "name":"Bitfinex Debt token",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTM",
+ "name":"Bitmark",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BITON",
+ "name":"BITON",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTQ",
+ "name":"BitQuark",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BITS",
+ "name":"BITS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BSD",
+ "name":"BitSend",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTS",
+ "name":"BitShares",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SWIFT",
+ "name":"BitSwift",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BITZ",
+ "name":"Bitz",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLK",
+ "name":"Blackcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLC",
+ "name":"Blakecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLEU",
+ "name":"BLEU",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLITZ",
+ "name":"Blitzcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLOCK",
+ "name":"Blocknet",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLRY",
+ "name":"BLRY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLU",
+ "name":"BLU",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BLUS",
+ "name":"BLUS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BNT",
+ "name":"BNT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BOLI",
+ "name":"Bolivarcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BBR",
+ "name":"Boolberry",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BOOM",
+ "name":"BOOM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BOST",
+ "name":"BoostCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BOSS",
+ "name":"BOSS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BPOK",
+ "name":"BPOK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BRAIN",
+ "name":"BRAIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BRC",
+ "name":"BRC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BRDD",
+ "name":"BRDD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BRIT",
+ "name":"BRIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GBP",
+ "name":"British Pound Sterling",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"BRK",
+ "name":"BRK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BRX",
+ "name":"BRX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BS",
+ "name":"BS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BSC",
+ "name":"BSC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BST",
+ "name":"BST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTCHC",
+ "name":"BTCHC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTCR",
+ "name":"BTCR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTCS",
+ "name":"BTCS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTD",
+ "name":"BTD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTLC",
+ "name":"BTLC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTTF",
+ "name":"BTTF",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BTZ",
+ "name":"BTZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BUCKS",
+ "name":"BUCKS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BUN",
+ "name":"BUN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BURST",
+ "name":"Burst",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BUZZ",
+ "name":"BUZZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BVC",
+ "name":"BVC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BXT",
+ "name":"BXT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BYC",
+ "name":"Bytecent",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BCN",
+ "name":"Bytecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CAB",
+ "name":"Cabbage Unit",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CAGE",
+ "name":"CAGE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CAID",
+ "name":"CAID",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CAD",
+ "name":"Canadian Dollar",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"CANN",
+ "name":"CannabisCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CCN",
+ "name":"Cannacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CPC",
+ "name":"Capricoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CAPT",
+ "name":"CAPT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DIEM",
+ "name":"CarpeDiemCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CASH",
+ "name":"CASH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CBD",
+ "name":"CBD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CBIT",
+ "name":"CBIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CCX",
+ "name":"CCX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CD",
+ "name":"CD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CDN",
+ "name":"CDN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CF",
+ "name":"CF",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CGA",
+ "name":"CGA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CKC",
+ "name":"Checkcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CHEMX",
+ "name":"CHEMX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CHESS",
+ "name":"CHESS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CHF",
+ "name":"CHF",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"CNY",
+ "name":"Chinese Yuan",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"CHOOF",
+ "name":"CHOOF",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CJ",
+ "name":"CJ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLAM",
+ "name":"Clams",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLICK",
+ "name":"CLICK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLINT",
+ "name":"CLINT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLOAK",
+ "name":"Cloakcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLR",
+ "name":"CLR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLUB",
+ "name":"CLUB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLUD",
+ "name":"CLUD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CLV",
+ "name":"CLV",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CME",
+ "name":"CME",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CMT",
+ "name":"CMT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CNC",
+ "name":"CNC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"COC",
+ "name":"COC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"COXST",
+ "name":"CoExistCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"COIN",
+ "name":"COIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"C2",
+ "name":"Coin2.1",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CV2",
+ "name":"Colossuscoin2.0",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CON",
+ "name":"CON",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XCP",
+ "name":"Counterparty",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"COVAL",
+ "name":"COVAL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"COX",
+ "name":"COX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRAB",
+ "name":"CRAB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRC",
+ "name":"CRC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRE",
+ "name":"CRE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRBIT",
+ "name":"Creditbit",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CREVA",
+ "name":"CrevaCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRNK",
+ "name":"CRNK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRPC",
+ "name":"CRPC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRPS",
+ "name":"CRPS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRT",
+ "name":"CRT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRW",
+ "name":"CRW",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRX",
+ "name":"CRX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CRY",
+ "name":"CRY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CBX",
+ "name":"Crypto Bullion",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CESC",
+ "name":"CryptoEscudo",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XCN",
+ "name":"Cryptonite",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CSH",
+ "name":"CSH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CST",
+ "name":"CST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CTK",
+ "name":"CTK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CTL",
+ "name":"CTL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CTO",
+ "name":"CTO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CURE",
+ "name":"Curecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CYC",
+ "name":"CYC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CYP",
+ "name":"Cypher",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CZC",
+ "name":"CZC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CZR",
+ "name":"CZR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DGD",
+ "name":"DarkGoldCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DNET",
+ "name":"Darknet",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DAS",
+ "name":"DAS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DASH",
+ "name":"Dash",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DTC",
+ "name":"Datacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DB",
+ "name":"DB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DBG",
+ "name":"DBG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DBLK",
+ "name":"DBLK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DBTC",
+ "name":"DBTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DC",
+ "name":"DC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DCK",
+ "name":"DCK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DCRE",
+ "name":"DCRE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DCT",
+ "name":"DCT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DCYP",
+ "name":"DCYP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DCR",
+ "name":"Decred",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DES",
+ "name":"Destiny",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DEUR",
+ "name":"DEUR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DEM",
+ "name":"Deutsche eMark",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DVC",
+ "name":"Devcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DGMS",
+ "name":"DGMS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DGORE",
+ "name":"DGORE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DMD",
+ "name":"Diamond",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DGB",
+ "name":"Digibyte",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"CUBE",
+ "name":"DigiCube",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DGC",
+ "name":"Digitalcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XDN",
+ "name":"DigitalNote",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DP",
+ "name":"DigitalPrice",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DIME",
+ "name":"Dimecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DISK",
+ "name":"DISK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DKC",
+ "name":"DKC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DLC",
+ "name":"DLC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DLISK",
+ "name":"DLISK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DMC",
+ "name":"DMC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NOTE",
+ "name":"DNotes",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DOGE",
+ "name":"Dogecoin",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"DOPE",
+ "name":"DopeCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DOV",
+ "name":"DOV",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DOX",
+ "name":"DOX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DPAY",
+ "name":"DPAY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DRACO",
+ "name":"DRACO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DRM8",
+ "name":"DRM8",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DROP",
+ "name":"DROP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DRZ",
+ "name":"DRZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DSH",
+ "name":"DSH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DTT",
+ "name":"DTT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DBIC",
+ "name":"DubaiCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DUO",
+ "name":"DUO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DUST",
+ "name":"DUST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EAGS",
+ "name":"EAGS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EAC",
+ "name":"Earthcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EBST",
+ "name":"EBST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EC",
+ "name":"EC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ECC",
+ "name":"ECCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ECLI",
+ "name":"ECLI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EDC",
+ "name":"EDC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EDRC",
+ "name":"EDRC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EDR",
+ "name":"EDRCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EGG",
+ "name":"EGG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EGO",
+ "name":"EGO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EMC2",
+ "name":"Einsteinium",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EL",
+ "name":"EL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ELE",
+ "name":"ELE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EFL",
+ "name":"Electronic Gulden",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EMB",
+ "name":"EMB",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"EME",
+ "name":"EME",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"EMC",
+ "name":"Emercoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EMIRG",
+ "name":"EMIRG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EMP",
+ "name":"EMP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EMPC",
+ "name":"EMPC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ENRG",
+ "name":"Energycoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ENT",
+ "name":"ENT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EPC",
+ "name":"EPC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EQM",
+ "name":"EQM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EQUAL",
+ "name":"EQUAL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ERC",
+ "name":"ERC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ERC3",
+ "name":"ERC3",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ESB",
+ "name":"ESB",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"ESC",
+ "name":"ESC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ESP",
+ "name":"ESP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ETCO",
+ "name":"ETCO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ETH",
+ "name":"Ethereum",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"ETC",
+ "name":"Ethereum Classic",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ETHS",
+ "name":"ETHS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EUC",
+ "name":"EUC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EUR",
+ "name":"Euro",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"EGC",
+ "name":"EvergreenCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EVIL",
+ "name":"EVIL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EXCL",
+ "name":"EXCL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"EXP",
+ "name":"Expanse",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FCT",
+ "name":"Factom",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FAIR",
+ "name":"Faircoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FC2",
+ "name":"FC2",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FCH",
+ "name":"FCH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FCN",
+ "name":"FCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FCP",
+ "name":"FCP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FTC",
+ "name":"Feathercoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TIPS",
+ "name":"Fedoracoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FIND",
+ "name":"FIND",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FIT",
+ "name":"FIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FJC",
+ "name":"FJC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FLO",
+ "name":"Florincoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FLOZ",
+ "name":"FLOZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FLT",
+ "name":"FlutterCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FLY",
+ "name":"Flycoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FLDC",
+ "name":"FoldingCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FOREX",
+ "name":"FOREX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FRK",
+ "name":"Franko",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FRDC",
+ "name":"FRDC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FRC",
+ "name":"Freicoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FRN",
+ "name":"FRN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FRWC",
+ "name":"FRWC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FSN",
+ "name":"FSN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FST",
+ "name":"FST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FTP",
+ "name":"FTP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FUEL",
+ "name":"FUEL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FUN",
+ "name":"FUN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FUTC",
+ "name":"FUTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FUZZ",
+ "name":"FUZZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"FX",
+ "name":"FX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GAIA",
+ "name":"GAIA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GAIN",
+ "name":"GAIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GAKH",
+ "name":"GAKH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GAM",
+ "name":"GAM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GBT",
+ "name":"GameBet Coin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GAME",
+ "name":"GameCredits",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GAP",
+ "name":"Gapcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GARY",
+ "name":"GARY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GB",
+ "name":"GB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GBC",
+ "name":"GBC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GBIT",
+ "name":"GBIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GBRC",
+ "name":"GBRC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GCN",
+ "name":"GCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GENE",
+ "name":"GENE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GEO",
+ "name":"GeoCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GEMZ",
+ "name":"GetGems",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GHOST",
+ "name":"GHOST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GHS",
+ "name":"GHS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GLC",
+ "name":"GLC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"BSTY",
+ "name":"GlobalBoost-Y",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GMCX",
+ "name":"GMCX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GML",
+ "name":"GML",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GMX",
+ "name":"GMX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GOAT",
+ "name":"GOAT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GCR",
+ "name":"GoCoineR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GLD",
+ "name":"GoldCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GOON",
+ "name":"GOON",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GOTX",
+ "name":"GOTX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GP",
+ "name":"GP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GPU",
+ "name":"GPU",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GRF",
+ "name":"Graffiti",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GRAM",
+ "name":"GRAM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GRT",
+ "name":"Grantcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GREED",
+ "name":"GREED",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GRC",
+ "name":"Gridcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GRN",
+ "name":"GRN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GRS",
+ "name":"Groestlcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GROW",
+ "name":"GrowCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GRW",
+ "name":"GRW",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GSY",
+ "name":"GSY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GUA",
+ "name":"GUA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NLG",
+ "name":"Gulden",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GUM",
+ "name":"GUM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GUN",
+ "name":"GUN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"GYC",
+ "name":"GYC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HALLO",
+ "name":"HALLO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HAM",
+ "name":"HAM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HBT",
+ "name":"HBT",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"HCC",
+ "name":"HCC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HEAT",
+ "name":"HEAT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HMP",
+ "name":"HempCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XHI",
+ "name":"HiCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HILL",
+ "name":"HILL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HODL",
+ "name":"HOdlcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HKD",
+ "name":"Hong Kong Dollar",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"HZ",
+ "name":"Horizon",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HSP",
+ "name":"HSP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HTC",
+ "name":"HTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HTML5",
+ "name":"HTMLCOIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HUC",
+ "name":"HUC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HVCO",
+ "name":"HVCO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HXX",
+ "name":"HXX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HYPER",
+ "name":"Hyper",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"HYP",
+ "name":"HyperStake",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"IBANK",
+ "name":"IBANK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ICASH",
+ "name":"iCash",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ICN",
+ "name":"iCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"IFLT",
+ "name":"IFLT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"IMPS",
+ "name":"IMPS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"INCP",
+ "name":"INCP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"IFC",
+ "name":"Infinitecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"INFX",
+ "name":"Influxcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"IOC",
+ "name":"IO Coin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ION",
+ "name":"ION",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ISL",
+ "name":"IslaCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"IVZ",
+ "name":"IVZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"IXC",
+ "name":"IXC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"JPY",
+ "name":"Japanese Yen",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"JOBS",
+ "name":"JOBS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"JPC",
+ "name":"JPC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"JBS",
+ "name":"Jumbucks",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"JW",
+ "name":"JW",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"JWL",
+ "name":"JWL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KAT",
+ "name":"KAT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KC",
+ "name":"KC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KNC",
+ "name":"KhanCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KLC",
+ "name":"KLC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KOBO",
+ "name":"KOBO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KORE",
+ "name":"KoreCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KRAK",
+ "name":"KRAK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KRB",
+ "name":"KRB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KRC",
+ "name":"KRC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KRYP",
+ "name":"KRYP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KR",
+ "name":"Krypton",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"KTK",
+ "name":"KTK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LANA",
+ "name":"LANA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LAZ",
+ "name":"LAZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LBC",
+ "name":"LBC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LC",
+ "name":"LC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LEA",
+ "name":"LeaCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LEAF",
+ "name":"LEAF",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LEO",
+ "name":"LEO",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"LFC",
+ "name":"LFC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LFO",
+ "name":"LFO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LFTC",
+ "name":"LFTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LGBTQ",
+ "name":"LGBTQ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LIR",
+ "name":"LIR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LSK",
+ "name":"Lisk",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LTC",
+ "name":"Litecoin",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"LTCR",
+ "name":"Litecred",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LIV",
+ "name":"LIV",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LKC",
+ "name":"LKC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LOC",
+ "name":"LOC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LOOT",
+ "name":"LOOT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LTBC",
+ "name":"LTBcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LTH",
+ "name":"LTH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LTS",
+ "name":"LTS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LUCKY",
+ "name":"LUCKY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LUN",
+ "name":"LUN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LXC",
+ "name":"LXC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MAD",
+ "name":"MAD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XMG",
+ "name":"Magi",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MAID",
+ "name":"MaidSafeCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MXT",
+ "name":"MarteXcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OMNI",
+ "name":"Mastercoin (Omni)",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MTR",
+ "name":"MasterTraderCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MAX",
+ "name":"Maxcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MZC",
+ "name":"Mazacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MBL",
+ "name":"MBL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MCZ",
+ "name":"MCZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MED",
+ "name":"MediterraneanCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MEGA",
+ "name":"MEGA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MEC",
+ "name":"Megacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MEME",
+ "name":"Memetic",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"METAL",
+ "name":"METAL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MG",
+ "name":"MG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MND",
+ "name":"MindCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MINT",
+ "name":"Mintcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MIS",
+ "name":"MIS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MMNXT",
+ "name":"MMNXT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MMXVI",
+ "name":"MMXVI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MNM",
+ "name":"MNM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MOIN",
+ "name":"MOIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MOJO",
+ "name":"MojoCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MONA",
+ "name":"MonaCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XMR",
+ "name":"Monero",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"MUE",
+ "name":"MonetaryUnit",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MOON",
+ "name":"Mooncoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MOOND",
+ "name":"MOOND",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MPRO",
+ "name":"MPRO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MRB",
+ "name":"MRB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MUDRA",
+ "name":"MUDRA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MYR",
+ "name":"Myriadcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"N2O",
+ "name":"N2O",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"N7",
+ "name":"N7",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NMC",
+ "name":"Namecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NAT",
+ "name":"NAT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NAUT",
+ "name":"Nautiluscoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NAV",
+ "name":"NAV Coin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NBIT",
+ "name":"NBIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NCS",
+ "name":"NCS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NDOGE",
+ "name":"NDOGE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XEM",
+ "name":"NEM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NEOS",
+ "name":"NeosCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NET",
+ "name":"NetCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NEU",
+ "name":"NeuCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NTRN",
+ "name":"Neutron",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NEVA",
+ "name":"NevaCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NEWB",
+ "name":"NEWB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NXS",
+ "name":"Nexus",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NIC",
+ "name":"NIC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NICE",
+ "name":"NICE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NKC",
+ "name":"NKC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NLC",
+ "name":"NLC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NOBL",
+ "name":"NobleCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NODES",
+ "name":"NODES",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NVC",
+ "name":"Novacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NRS",
+ "name":"NRS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NTC",
+ "name":"NTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NBT",
+ "name":"NuBits",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NUKE",
+ "name":"NUKE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NUM",
+ "name":"NUM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NSR",
+ "name":"NuShares",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NXE",
+ "name":"NXE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NXT",
+ "name":"NXT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NXTTY",
+ "name":"Nxttycoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NYC",
+ "name":"NYC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NZC",
+ "name":"NZC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"NZD",
+ "name":"NZD",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"OBS",
+ "name":"OBS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OCOW",
+ "name":"OCOW",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OK",
+ "name":"OKCash",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OLYMP",
+ "name":"OLYMP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OMC",
+ "name":"OMC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ONE",
+ "name":"ONE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OP",
+ "name":"OP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OPAL",
+ "name":"OPAL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ORB",
+ "name":"Orbitcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"OZC",
+ "name":"OZC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PAC",
+ "name":"PAC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PAL",
+ "name":"PAL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PND",
+ "name":"Pandacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PARA",
+ "name":"PARA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PAY",
+ "name":"PAY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XPY",
+ "name":"Paycoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PBC",
+ "name":"PBC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PCM",
+ "name":"PCM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PCS",
+ "name":"PCS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PDC",
+ "name":"PDC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PEC",
+ "name":"PEC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PPC",
+ "name":"Peercoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PEN",
+ "name":"PEN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PHR",
+ "name":"PHR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PIN",
+ "name":"PIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PC",
+ "name":"Pinkcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PIO",
+ "name":"PIO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PIZZA",
+ "name":"PIZZA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PKB",
+ "name":"PKB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PLN",
+ "name":"PLN",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"PLNC",
+ "name":"PLNC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PNK",
+ "name":"PNK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"POKE",
+ "name":"POKE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PONZ2",
+ "name":"PONZ2",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PONZI",
+ "name":"PONZI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PEX",
+ "name":"PosEx",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"POST",
+ "name":"POST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"POT",
+ "name":"Potcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PRE",
+ "name":"PRE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PRES",
+ "name":"PRES",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PXI",
+ "name":"Prime-XI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PRIME",
+ "name":"PrimeChain",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XPM",
+ "name":"Primecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PRM",
+ "name":"PRM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PRT",
+ "name":"PRT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PSB",
+ "name":"PSB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PSP",
+ "name":"PSP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PSY",
+ "name":"PSY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PTC",
+ "name":"PTC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PURE",
+ "name":"PURE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PUTIN",
+ "name":"PUTIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PWR",
+ "name":"PWR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PX",
+ "name":"PX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"PXL",
+ "name":"PXL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"QBC",
+ "name":"QBC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"QBK",
+ "name":"QBK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"QCN",
+ "name":"QCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"QORA",
+ "name":"Qora",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"QTZ",
+ "name":"QTZ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"QRK",
+ "name":"Quark",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"QTL",
+ "name":"Quatloo",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RADI",
+ "name":"RADI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RADS",
+ "name":"Radium",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XRA",
+ "name":"RateCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RBIT",
+ "name":"RBIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RCN",
+ "name":"RCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RED",
+ "name":"RED",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RDD",
+ "name":"Reddcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"REE",
+ "name":"REE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"REV",
+ "name":"Revenu",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RICHX",
+ "name":"RICHX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RIC",
+ "name":"Riecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RBT",
+ "name":"Rimbit",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"RIO",
+ "name":"RIO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XRP",
+ "name":"Ripple",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RISE",
+ "name":"RISE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RMS",
+ "name":"RMS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RONIN",
+ "name":"RONIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ROYAL",
+ "name":"ROYAL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RPC",
+ "name":"RPC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RRT",
+ "name":"RRT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RBIES",
+ "name":"Rubies",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RUBIT",
+ "name":"RUBIT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RUR",
+ "name":"Ruble",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"RBY",
+ "name":"Rubycoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RUST",
+ "name":"RUST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"RYCN",
+ "name":"RYCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SEC",
+ "name":"Safe Exchange Coin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SAK",
+ "name":"SAK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SAR",
+ "name":"SAR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SBD",
+ "name":"SBD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SCAN",
+ "name":"SCAN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SCB",
+ "name":"SCB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SCN",
+ "name":"SCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SCOT",
+ "name":"Scotcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SCRPT",
+ "name":"SCRPT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SCRT",
+ "name":"SCRT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SCT",
+ "name":"SCT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SRC",
+ "name":"SecureCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SED",
+ "name":"SED",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SXC",
+ "name":"Sexcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SGD",
+ "name":"SGD",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"SH",
+ "name":"SH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SDC",
+ "name":"ShadowCash",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SHELL",
+ "name":"SHELL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SHI",
+ "name":"SHI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SHIFT",
+ "name":"Shift",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SC",
+ "name":"Siacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SIB",
+ "name":"Siberian chervonets",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SIGU",
+ "name":"SIGU",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SLFI",
+ "name":"SLFI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SLING",
+ "name":"Sling",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SLK",
+ "name":"SLK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SLS",
+ "name":"SLS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SMC",
+ "name":"SMC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SMLY",
+ "name":"SmileyCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SNGLS",
+ "name":"SNGLS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SNRG",
+ "name":"SNRG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SOIL",
+ "name":"SOILcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SLR",
+ "name":"Solarcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SONG",
+ "name":"SongCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SOON",
+ "name":"SOON",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SP",
+ "name":"SP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPACE",
+ "name":"SPACE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPEX",
+ "name":"SPEX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPHR",
+ "name":"Sphere",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPKTR",
+ "name":"SPKTR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPN",
+ "name":"SPN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPORT",
+ "name":"SPORT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPR",
+ "name":"SpreadCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPT",
+ "name":"SPT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SPX",
+ "name":"SPX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SSC",
+ "name":"SSC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STA",
+ "name":"STA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STAR",
+ "name":"STAR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"START",
+ "name":"Startcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STE",
+ "name":"STE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XST",
+ "name":"Stealthcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STEEM",
+ "name":"Steem",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XLM",
+ "name":"Stellar",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STR",
+ "name":"Stellar",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STEPS",
+ "name":"Steps",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SLG",
+ "name":"Sterlingcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STHR",
+ "name":"STHR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STL",
+ "name":"STL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STO",
+ "name":"STO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SJCX",
+ "name":"Storjcoin X",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STP",
+ "name":"STP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STRAT",
+ "name":"STRAT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STS",
+ "name":"Stress",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"STV",
+ "name":"STV",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SUB",
+ "name":"Subcriptio",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UNITY",
+ "name":"SuperNET",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SWEET",
+ "name":"SWEET",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SWING",
+ "name":"SWING",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SYNC",
+ "name":"SYNC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"AMP",
+ "name":"Synereo",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SYNX",
+ "name":"SYNX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"SYS",
+ "name":"Syscoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TAB",
+ "name":"TAB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TAG",
+ "name":"TagCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TAJ",
+ "name":"TAJ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TAK",
+ "name":"TAK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TAO",
+ "name":"TAO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TBC",
+ "name":"TBC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TC",
+ "name":"TC",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"TCOIN",
+ "name":"TCOIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TCR",
+ "name":"TCR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TDFB",
+ "name":"TDFB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TDY",
+ "name":"TDY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TEAM",
+ "name":"TEAM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TEC",
+ "name":"TEC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TECH",
+ "name":"TECH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TEK",
+ "name":"TEKcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TRC",
+ "name":"Terracoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TESLA",
+ "name":"TESLA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TES",
+ "name":"TeslaCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TET",
+ "name":"TET",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"THC",
+ "name":"THC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"DAO",
+ "name":"The DAO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TIA",
+ "name":"TIA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TIX",
+ "name":"Tickets",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XTC",
+ "name":"TileCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TIT",
+ "name":"Titcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TMC",
+ "name":"TMC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TNG",
+ "name":"TNG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TODAY",
+ "name":"TODAY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TOKEN",
+ "name":"TOKEN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TP1",
+ "name":"TP1",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TPC",
+ "name":"TPC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TPG",
+ "name":"TPG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TX",
+ "name":"Transfercoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TRAP",
+ "name":"TRAP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TRICK",
+ "name":"TRICK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TRIG",
+ "name":"TRIG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TROLL",
+ "name":"TROLL",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TRK",
+ "name":"Truckcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TRUMP",
+ "name":"TrumpCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TRUST",
+ "name":"TRUST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TSC",
+ "name":"TSC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TWERK",
+ "name":"TWERK",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TWIST",
+ "name":"TWIST",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"TWO",
+ "name":"TWO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UAE",
+ "name":"UAE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UB",
+ "name":"UB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UFO",
+ "name":"UFO Coin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UIS",
+ "name":"UIS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UAH",
+ "name":"Ukrainian Hryvnia",
+ "statuses":[
+ "secondary"
+ ]
+ },
+ {
+ "code":"UTC",
+ "name":"UltraCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UNB",
+ "name":"UNB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UNC",
+ "name":"UNC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UNF",
+ "name":"Unfed",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UNIQ",
+ "name":"UNIQ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UNIT",
+ "name":"Universal Currency",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"UNO",
+ "name":"Unobtanium",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"URC",
+ "name":"URC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"URO",
+ "name":"Uro",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"USD",
+ "name":"US Dollar",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"USDE",
+ "name":"USDE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XVC",
+ "name":"Vcash",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VCN",
+ "name":"VCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VCOIN",
+ "name":"VCOIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VEC",
+ "name":"VEC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VEG",
+ "name":"VEG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XVG",
+ "name":"Verge",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VRC",
+ "name":"VeriCoin",
+ "statuses":[
+ "primary",
+ "secondary"
+ ]
+ },
+ {
+ "code":"VTC",
+ "name":"Vertcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VIA",
+ "name":"Viacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VIP",
+ "name":"VIP Tokens",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VIRAL",
+ "name":"Viral",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VLT",
+ "name":"VLT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VOOT",
+ "name":"VootCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VOX",
+ "name":"Voxels",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VOYA",
+ "name":"VOYA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VPN",
+ "name":"VPNCoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VRM",
+ "name":"VRM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VRS",
+ "name":"VRS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VTA",
+ "name":"VTA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VTN",
+ "name":"VTN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VTR",
+ "name":"VTR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"VTY",
+ "name":"VTY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WA",
+ "name":"WA",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WAC",
+ "name":"WAC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WARP",
+ "name":"WARP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WASH",
+ "name":"WASH",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WAV",
+ "name":"WAV",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WAVES",
+ "name":"WAVES",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WAY",
+ "name":"WAY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WCN",
+ "name":"WCN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WEX",
+ "name":"WEX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WGC",
+ "name":"WGC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XWC",
+ "name":"Whitecoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WBB",
+ "name":"Wild Beast Block",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WINE",
+ "name":"WINE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WLC",
+ "name":"WLC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WMC",
+ "name":"WMC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"LOG",
+ "name":"Woodcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WDC",
+ "name":"Worldcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"WRP",
+ "name":"WRP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"X2",
+ "name":"X2",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"X2C",
+ "name":"X2C",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XAB",
+ "name":"XAB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XAUR",
+ "name":"XAUR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XAU",
+ "name":"Xaurum",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XBS",
+ "name":"XBS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XBTS",
+ "name":"XBTS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XBU",
+ "name":"XBU",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XCO",
+ "name":"XCO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XC",
+ "name":"XCurrency",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XDB",
+ "name":"XDB",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XDE2",
+ "name":"XDE2",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"MI",
+ "name":"Xiaomicoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XID",
+ "name":"XID",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XJO",
+ "name":"XJO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XLTCG",
+ "name":"XLTCG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XMINE",
+ "name":"XMINE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XMS",
+ "name":"XMS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XNG",
+ "name":"XNG",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XODUS",
+ "name":"XODUS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XPC",
+ "name":"XPC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XPO",
+ "name":"XPO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XPOKE",
+ "name":"XPOKE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XPRO",
+ "name":"XPRO",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XPTX",
+ "name":"XPTX",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XQN",
+ "name":"XQN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XRC",
+ "name":"XRC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XSEED",
+ "name":"XSEED",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XSY",
+ "name":"XSY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XTP",
+ "name":"XTP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XUP",
+ "name":"XUP",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"YAC",
+ "name":"Yacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"YAY",
+ "name":"YAY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"YBC",
+ "name":"Ybcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"YMC",
+ "name":"YMC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"YOC",
+ "name":"YOC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"YOVI",
+ "name":"YOVI",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"YUM",
+ "name":"YUM",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZEC",
+ "name":"Zcash",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZCL",
+ "name":"Zcash classic",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZCC",
+ "name":"ZCC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZCOIN",
+ "name":"ZCOIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"XZC",
+ "name":"Zcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZECD",
+ "name":"ZECD",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZEIT",
+ "name":"Zeitcoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZET2",
+ "name":"ZET2",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZET",
+ "name":"Zetacoin",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZRC",
+ "name":"ZiftrCOIN",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZLQ",
+ "name":"ZLQ",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZMC",
+ "name":"ZMC",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZNE",
+ "name":"ZNE",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZNY",
+ "name":"ZNY",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZS",
+ "name":"ZS",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZUR",
+ "name":"ZUR",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZXT",
+ "name":"ZXT",
+ "statuses":[
+ "primary"
+ ]
+ },
+ {
+ "code":"ZYD",
+ "name":"ZYD",
+ "statuses":[
+ "primary"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/ui/app/css/index.css b/ui/app/css/index.css
index e2be0abd9..4b9b5b67d 100644
--- a/ui/app/css/index.css
+++ b/ui/app/css/index.css
@@ -36,24 +36,40 @@ button {
font-family: 'Montserrat Bold';
outline: none;
cursor: pointer;
- box-shadow: 0px 3px 6px rgba(247, 134, 28, 0.36);
- /*margin: 10px;*/
padding: 8px 12px;
border: none;
- background: #F7861C;
color: white;
transform-origin: center center;
transition: transform 50ms ease-in;
+ /* default orange */
+ background: rgba(247, 134, 28, 1);
+ box-shadow: 0px 3px 6px rgba(247, 134, 28, 0.36);
+}
+
+button.btn-green {
+ background: rgba(106, 195, 96, 1);
+ box-shadow: 0px 3px 6px rgba(106, 195, 96, 0.36);
+}
+
+button.btn-red {
+ background: rgba(254, 35, 17, 1);
+ box-shadow: 0px 3px 6px rgba(254, 35, 17, 0.36);
+}
+
+button[disabled] {
+ cursor: not-allowed;
+ background: rgba(197, 197, 197, 1);
+ box-shadow: 0px 3px 6px rgba(197, 197, 197, 0.36);
}
button.spaced {
margin: 2px;
}
-button:hover {
+button:not([disabled]):hover {
transform: scale(1.1);
}
-button:active {
+button:not([disabled]):active {
transform: scale(0.95);
}
@@ -149,9 +165,6 @@ textarea.twelve-word-phrase {
}
.network-name {
- position: absolute;
- top: 8px;
- left: 60px;
width: 5.2em;
line-height: 9px;
text-rendering: geometricPrecision;
@@ -395,6 +408,16 @@ input.large-input {
.name-label{
}
+
+.unapproved-tx-icon {
+ height: 24px;
+ background: #4dffff;
+ border: solid;
+ border-color: #AEAEAE;
+ border-width: 0.5px;
+ border-radius: 13px;
+}
+
.edit-text {
height: 100%;
visibility: hidden;
@@ -539,8 +562,8 @@ input.large-input {
.marketinfo{
font-family: 'Montserrat light';
color: #AEAEAE;
- font-size: 12px;
- line-height: 14px;
+ font-size: 15px;
+ line-height: 17px;
}
#fromCoin::-webkit-calendar-picker-indicator {
@@ -583,12 +606,18 @@ input.large-input {
margin-left: 5px;
}
+.qr-header {
+ font-size: 25px;
+ margin-top: 40px;
+}
+
.qr-message {
font-size: 12px;
color: #F7861C;
}
div.message-container > div:first-child {
+ margin-top: 18px;
font-size: 15px;
color: #4D4D4D;
}
diff --git a/ui/app/css/lib.css b/ui/app/css/lib.css
index bcd6a4a67..a8df1d115 100644
--- a/ui/app/css/lib.css
+++ b/ui/app/css/lib.css
@@ -23,6 +23,14 @@
flex-direction: column;
}
+.space-between {
+ justify-content: space-between;
+}
+
+.space-around {
+ justify-content: space-around;
+}
+
.flex-column-bottom {
display: flex;
flex-direction: column-reverse;
@@ -196,6 +204,23 @@ hr.horizontal-line {
align-items: center;
justify-content: center;
padding: 4px;
+ z-index: 1;
+}
+
+.keyring-label {
+ z-index: 1;
+ font-size: 11px;
+ background: rgba(255,0,0,0.8);
+ bottom: -47px;
+ color: white;
+ border-radius: 10px;
+ height: 20px;
+ min-width: 20px;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 4px;
}
.ether-balance {
@@ -213,7 +238,7 @@ hr.horizontal-line {
background: rgb(0, 163, 68);
border-radius: 20px;
}
-.morden-icon {
+.testnet-icon {
background: #2465E1;
}
diff --git a/ui/app/eth-store-warning.js b/ui/app/eth-store-warning.js
deleted file mode 100644
index 55274996b..000000000
--- a/ui/app/eth-store-warning.js
+++ /dev/null
@@ -1,90 +0,0 @@
-const connect = require('react-redux').connect
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const actions = require('./actions')
-
-module.exports = connect(mapStateToProps)(EthStoreWarning)
-
-inherits(EthStoreWarning, Component)
-function EthStoreWarning () {
- Component.call(this)
-}
-
-function mapStateToProps (state) {
- return {
- selectedAccount: state.metamask.selectedAccount,
- }
-}
-
-EthStoreWarning.prototype.render = function () {
-
- return (
-
- h('.flex-column', {
- key: 'ethWarning',
- style: {
- paddingTop: '25px',
- marginRight: '30px',
- marginLeft: '30px',
- alignItems: 'center',
- },
- }, [
- h('.warning', {
- style: {
- margin: '10px 10px 10px 10px',
- },
- },
- `The MetaMask team would like to
- remind you that MetaMask is currently in beta - so
- don't store large
- amounts of ether in MetaMask.
- `),
-
- h('i.fa.fa-exclamation-triangle.fa-4', {
- style: {
- fontSize: '152px',
- color: '#AEAEAE',
- textAlign: 'center',
- },
- }),
-
- h('.flex-row', {
- style: {
- marginTop: '25px',
- marginBottom: '10px',
- },
- }, [
- h('input', {
- type: 'checkbox',
- onChange: this.toggleShowWarning.bind(this),
- }),
- h('.warning', {
- style: {
- fontSize: '11px',
- },
-
- }, 'Don\'t show me this message again'),
- ]),
- h('.flex-row', {
- style: {
- width: '100%',
- justifyContent: 'space-around',
- },
- }, [
- h('button', {
- onClick: this.toAccounts.bind(this),
- },
- 'Continue to MetaMask'),
- ]),
- ])
- )
-}
-
-EthStoreWarning.prototype.toggleShowWarning = function () {
- this.props.dispatch(actions.agreeToEthWarning())
-}
-
-EthStoreWarning.prototype.toAccounts = function () {
- this.props.dispatch(actions.showAccountDetail(this.props.account))
-}
diff --git a/ui/app/first-time/create-vault.js b/ui/app/first-time/create-vault.js
deleted file mode 100644
index 33ae62179..000000000
--- a/ui/app/first-time/create-vault.js
+++ /dev/null
@@ -1,129 +0,0 @@
-const inherits = require('util').inherits
-
-const Component = require('react').Component
-const connect = require('react-redux').connect
-const h = require('react-hyperscript')
-const actions = require('../actions')
-
-module.exports = connect(mapStateToProps)(CreateVaultScreen)
-
-inherits(CreateVaultScreen, Component)
-function CreateVaultScreen () {
- Component.call(this)
-}
-
-function mapStateToProps (state) {
- return {
- warning: state.appState.warning,
- }
-}
-
-CreateVaultScreen.prototype.render = function () {
- var state = this.props
- return (
-
- h('.initialize-screen.flex-column.flex-center.flex-grow', [
-
- h('h3.flex-center.text-transform-uppercase', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- marginBottom: 24,
- width: '100%',
- fontSize: '20px',
- padding: 6,
- },
- }, [
- 'Create Vault',
- ]),
-
- // password
- h('input.large-input.letter-spacey', {
- type: 'password',
- id: 'password-box',
- placeholder: 'New Password (min 8 chars)',
- style: {
- width: 260,
- marginTop: 12,
- },
- }),
-
- // confirm password
- h('input.large-input.letter-spacey', {
- type: 'password',
- id: 'password-box-confirm',
- placeholder: 'Confirm Password',
- onKeyPress: this.createVaultOnEnter.bind(this),
- style: {
- width: 260,
- marginTop: 16,
- },
- }),
-
- h('.flex-row.flex-space-between', {
- style: {
- marginTop: 30,
- width: '50%',
- },
- }, [
-
- // cancel
- h('button.primary', {
- onClick: this.showInitializeMenu.bind(this),
- }, 'CANCEL'),
-
- // submit
- h('button.primary', {
- onClick: this.createNewVault.bind(this),
- }, 'OK'),
-
- ]),
-
- (!state.inProgress && state.warning) && (
- h('span.in-progress-notification', state.warning)
- ),
-
- state.inProgress && (
- h('span.in-progress-notification', 'Generating Seed...')
- ),
- ])
- )
-}
-
-CreateVaultScreen.prototype.componentDidMount = function () {
- document.getElementById('password-box').focus()
-}
-
-CreateVaultScreen.prototype.showInitializeMenu = function () {
- this.props.dispatch(actions.showInitializeMenu())
-}
-
-// create vault
-
-CreateVaultScreen.prototype.createVaultOnEnter = function (event) {
- if (event.key === 'Enter') {
- event.preventDefault()
- this.createNewVault()
- }
-}
-
-CreateVaultScreen.prototype.createNewVault = function () {
- var passwordBox = document.getElementById('password-box')
- var password = passwordBox.value
- var passwordConfirmBox = document.getElementById('password-box-confirm')
- var passwordConfirm = passwordConfirmBox.value
- // var entropy = document.getElementById('entropy-text-entry').value
-
- if (password.length < 8) {
- this.warning = 'password not long enough'
- this.props.dispatch(actions.displayWarning(this.warning))
- return
- }
- if (password !== passwordConfirm) {
- this.warning = 'passwords don\'t match'
- this.props.dispatch(actions.displayWarning(this.warning))
- return
- }
-
- this.props.dispatch(actions.createNewVault(password, ''/* entropy*/))
-}
diff --git a/ui/app/first-time/disclaimer.js b/ui/app/first-time/disclaimer.js
deleted file mode 100644
index c6174a220..000000000
--- a/ui/app/first-time/disclaimer.js
+++ /dev/null
@@ -1,77 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const actions = require('../actions')
-const ReactMarkdown = require('react-markdown')
-const fs = require('fs')
-const path = require('path')
-const disclaimer = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'USER_AGREEMENT.md')).toString()
-module.exports = connect(mapStateToProps)(DisclaimerScreen)
-
-function mapStateToProps (state) {
- return {}
-}
-
-inherits(DisclaimerScreen, Component)
-function DisclaimerScreen () {
- Component.call(this)
-}
-
-DisclaimerScreen.prototype.render = function () {
- return (
- h('.flex-column.flex-center.flex-grow', [
-
- h('h3.flex-center.text-transform-uppercase.terms-header', {
- style: {
- background: '#EBEBEB',
- color: '#AEAEAE',
- marginBottom: 24,
- width: '100%',
- fontSize: '20px',
- textAlign: 'center',
- padding: 6,
- },
- }, [
- 'MetaMask Terms & Conditions',
- ]),
-
- h('style', `
-
- .markdown {
- font-family: Times New Roman;
- }
- .markdown h1, .markdown h2, .markdown h3 {
- margin: 10px 0;
- font-family: arial sans-serif;
- font-weight: bold;
- }
-
- `),
-
- h('div.markdown', {
- style: {
- // whiteSpace: 'pre-line',
- background: 'rgb(235, 235, 235)',
- height: '310px',
- padding: '6px',
- width: '80%',
- overflowY: 'scroll',
- },
- }, [
-
- h(ReactMarkdown, {
- source: disclaimer,
- skipHtml: true,
- }),
-
- ]),
-
- h('button', {
- style: { marginTop: '18px' },
- onClick: () => this.props.dispatch(actions.agreeToDisclaimer()),
- }, 'I Agree'),
- ])
- )
-}
-
diff --git a/ui/app/first-time/init-menu.js b/ui/app/first-time/init-menu.js
index e63c30fc5..cc7c51bd3 100644
--- a/ui/app/first-time/init-menu.js
+++ b/ui/app/first-time/init-menu.js
@@ -5,6 +5,8 @@ const connect = require('react-redux').connect
const h = require('react-hyperscript')
const Mascot = require('../components/mascot')
const actions = require('../actions')
+const Tooltip = require('../components/tooltip')
+const getCaretCoordinates = require('textarea-caret')
module.exports = connect(mapStateToProps)(InitializeMenuScreen)
@@ -18,6 +20,7 @@ function mapStateToProps (state) {
return {
// state from plugin
currentView: state.appState.currentView,
+ warning: state.appState.warning,
}
}
@@ -27,7 +30,7 @@ InitializeMenuScreen.prototype.render = function () {
switch (state.currentView.name) {
default:
- return this.renderMenu()
+ return this.renderMenu(state)
}
}
@@ -36,7 +39,7 @@ InitializeMenuScreen.prototype.render = function () {
// document.getElementById('password-box').focus()
// }
-InitializeMenuScreen.prototype.renderMenu = function () {
+InitializeMenuScreen.prototype.renderMenu = function (state) {
return (
h('.initialize-screen.flex-column.flex-center.flex-grow', [
@@ -47,51 +50,130 @@ InitializeMenuScreen.prototype.renderMenu = function () {
h('h1', {
style: {
- fontSize: '1.4em',
+ fontSize: '1.3em',
textTransform: 'uppercase',
color: '#7F8082',
- marginBottom: 20,
+ marginBottom: 10,
},
}, 'MetaMask'),
- h('button.primary', {
- onClick: this.showCreateVault.bind(this),
+
+ h('div', [
+ h('h3', {
+ style: {
+ fontSize: '0.8em',
+ color: '#7F8082',
+ display: 'inline',
+ },
+ }, 'Encrypt your new DEN'),
+
+ h(Tooltip, {
+ title: 'Your DEN is your password-encrypted storage within MetaMask.',
+ }, [
+ h('i.fa.fa-question-circle.pointer', {
+ style: {
+ fontSize: '18px',
+ position: 'relative',
+ color: 'rgb(247, 134, 28)',
+ top: '2px',
+ marginLeft: '4px',
+ },
+ }),
+ ]),
+ ]),
+
+ h('span.in-progress-notification', state.warning),
+
+ // password
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'New Password (min 8 chars)',
+ onInput: this.inputChanged.bind(this),
style: {
- margin: 12,
+ width: 260,
+ marginTop: 12,
+ },
+ }),
+
+ // confirm password
+ h('input.large-input.letter-spacey', {
+ type: 'password',
+ id: 'password-box-confirm',
+ placeholder: 'Confirm Password',
+ onKeyPress: this.createVaultOnEnter.bind(this),
+ onInput: this.inputChanged.bind(this),
+ style: {
+ width: 260,
+ marginTop: 16,
},
- }, 'Create New Vault'),
+ }),
- h('.flex-row.flex-center.flex-grow', [
- h('hr'),
- h('div', 'OR'),
- h('hr'),
- ]),
h('button.primary', {
- onClick: this.showRestoreVault.bind(this),
+ onClick: this.createNewVaultAndKeychain.bind(this),
style: {
margin: 12,
},
- }, 'Restore Existing Vault'),
+ }, 'Create'),
- ])
+ h('.flex-row.flex-center.flex-grow', [
+ h('p.pointer', {
+ onClick: this.showRestoreVault.bind(this),
+ style: {
+ fontSize: '0.8em',
+ color: 'rgb(247, 134, 28)',
+ textDecoration: 'underline',
+ },
+ }, 'Import Existing DEN'),
+ ]),
+ ])
)
}
-// InitializeMenuScreen.prototype.splitWor = function() {
-// this.props.dispatch(actions.showInitializeMenu())
-// }
-
-InitializeMenuScreen.prototype.showInitializeMenu = function () {
- this.props.dispatch(actions.showInitializeMenu())
+InitializeMenuScreen.prototype.createVaultOnEnter = function (event) {
+ if (event.key === 'Enter') {
+ event.preventDefault()
+ this.createNewVaultAndKeychain()
+ }
}
-InitializeMenuScreen.prototype.showCreateVault = function () {
- this.props.dispatch(actions.showCreateVault())
+InitializeMenuScreen.prototype.componentDidMount = function () {
+ document.getElementById('password-box').focus()
}
InitializeMenuScreen.prototype.showRestoreVault = function () {
this.props.dispatch(actions.showRestoreVault())
}
+InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () {
+ var passwordBox = document.getElementById('password-box')
+ var password = passwordBox.value
+ var passwordConfirmBox = document.getElementById('password-box-confirm')
+ var passwordConfirm = passwordConfirmBox.value
+
+ if (password.length < 8) {
+ this.warning = 'password not long enough'
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
+ if (password !== passwordConfirm) {
+ this.warning = 'passwords don\'t match'
+ this.props.dispatch(actions.displayWarning(this.warning))
+ return
+ }
+
+ this.props.dispatch(actions.createNewVaultAndKeychain(password))
+}
+
+InitializeMenuScreen.prototype.inputChanged = function (event) {
+ // tell mascot to look at page action
+ var element = event.target
+ var boundingRect = element.getBoundingClientRect()
+ var coordinates = getCaretCoordinates(element, element.selectionEnd)
+ this.animationEventEmitter.emit('point', {
+ x: boundingRect.left + coordinates.left - element.scrollLeft,
+ y: boundingRect.top + coordinates.top - element.scrollTop,
+ })
+}
diff --git a/ui/app/info.js b/ui/app/info.js
index 4e540bd03..e79580be4 100644
--- a/ui/app/info.js
+++ b/ui/app/info.js
@@ -56,6 +56,41 @@ InfoScreen.prototype.render = function () {
}, `Version: ${manifest.version}`),
]),
+ h('div', {
+ style: {
+ marginBottom: '10px',
+ }},
+ [
+ h('div', [
+ h('a', {
+ href: 'https://metamask.io/privacy.html',
+ target: '_blank',
+ onClick (event) { this.navigateTo(event.target.href) },
+ }, [
+ h('div.info', 'Privacy Policy'),
+ ]),
+ ]),
+ h('div', [
+ h('a', {
+ href: 'https://metamask.io/terms.html',
+ target: '_blank',
+ onClick (event) { this.navigateTo(event.target.href) },
+ }, [
+ h('div.info', 'Terms of Use'),
+ ]),
+ ]),
+ h('div', [
+ h('a', {
+ href: 'https://metamask.io/attributions.html',
+ target: '_blank',
+ onClick (event) { this.navigateTo(event.target.href) },
+ }, [
+ h('div.info', 'Attributions'),
+ ]),
+ ]),
+ ]
+ ),
+
h('hr', {
style: {
margin: '20px 0 ',
@@ -63,12 +98,6 @@ InfoScreen.prototype.render = function () {
},
}),
- h('.info',
- `For more information on MetaMask
- you can visit our web site. If you want to
- contact us with questions or just
- say 'Hi', you can find us on theise platforms:`),
-
h('div', {
style: {
paddingLeft: '30px',
@@ -81,7 +110,11 @@ InfoScreen.prototype.render = function () {
onClick (event) { this.navigateTo(event.target.href) },
}, [
h('img.icon-size', {
- src: manifest.icons[128],
+ src: manifest.icons['128'],
+ style: {
+ filter: 'grayscale(100%)', /* IE6-9 */
+ WebkitFilter: 'grayscale(100%)', /* Microsoft Edge and Firefox 35+ */
+ },
}),
h('div.info', 'Visit our web site'),
]),
@@ -107,15 +140,15 @@ InfoScreen.prototype.render = function () {
target: '_blank',
style: { width: '85vw' },
onClick () { extension.tabs.create({url: 'mailto:help@metamask.io?subject=Feedback'}) },
- }, 'Email us any questions or comments!'),
+ }, 'Email us!'),
]),
h('div.fa.fa-github', [
h('a.info', {
- href: 'https://github.com/metamask/talk/issues',
+ href: 'https://github.com/MetaMask/metamask-plugin/issues',
target: '_blank',
onClick (event) { this.navigateTo(event.target.href) },
- }, 'Start a thread on Github'),
+ }, 'Start a thread on GitHub'),
]),
]),
]),
diff --git a/ui/app/first-time/create-vault-complete.js b/ui/app/keychains/hd/create-vault-complete.js
index 0ca0a1b22..7272ebdbd 100644
--- a/ui/app/first-time/create-vault-complete.js
+++ b/ui/app/keychains/hd/create-vault-complete.js
@@ -2,7 +2,7 @@ const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
-const actions = require('../actions')
+const actions = require('../../actions')
module.exports = connect(mapStateToProps)(CreateVaultCompleteScreen)
@@ -61,6 +61,7 @@ CreateVaultCompleteScreen.prototype.render = function () {
onClick: () => this.confirmSeedWords(),
style: {
margin: '24px',
+ fontSize: '0.9em',
},
}, 'I\'ve copied it somewhere safe'),
])
@@ -70,4 +71,3 @@ CreateVaultCompleteScreen.prototype.render = function () {
CreateVaultCompleteScreen.prototype.confirmSeedWords = function () {
this.props.dispatch(actions.confirmSeedWords())
}
-
diff --git a/ui/app/recover-seed/confirmation.js b/ui/app/keychains/hd/recover-seed/confirmation.js
index 55b18025f..56ac461ea 100644
--- a/ui/app/recover-seed/confirmation.js
+++ b/ui/app/keychains/hd/recover-seed/confirmation.js
@@ -3,12 +3,12 @@ const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
-const actions = require('../actions')
+const actions = require('../../../actions')
-module.exports = connect(mapStateToProps)(RevealSeedConfirmatoin)
+module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
-inherits(RevealSeedConfirmatoin, Component)
-function RevealSeedConfirmatoin () {
+inherits(RevealSeedConfirmation, Component)
+function RevealSeedConfirmation () {
Component.call(this)
}
@@ -18,9 +18,9 @@ function mapStateToProps (state) {
}
}
-RevealSeedConfirmatoin.prototype.confirmationPhrase = 'I understand'
+RevealSeedConfirmation.prototype.confirmationPhrase = 'I understand'
-RevealSeedConfirmatoin.prototype.render = function () {
+RevealSeedConfirmation.prototype.render = function () {
const props = this.props
const state = this.state
@@ -68,7 +68,7 @@ RevealSeedConfirmatoin.prototype.render = function () {
style: {
marginTop: '12px',
},
- }, 'Enter the phrase "I understand" to proceed.'),
+ }, `Enter the phrase "${this.confirmationPhrase}" to proceed.`),
// confirm confirmation
h('input.large-input.letter-spacey', {
@@ -116,24 +116,24 @@ RevealSeedConfirmatoin.prototype.render = function () {
)
}
-RevealSeedConfirmatoin.prototype.componentDidMount = function () {
+RevealSeedConfirmation.prototype.componentDidMount = function () {
document.getElementById('password-box').focus()
}
-RevealSeedConfirmatoin.prototype.goHome = function () {
+RevealSeedConfirmation.prototype.goHome = function () {
this.props.dispatch(actions.showConfigPage(false))
}
// create vault
-RevealSeedConfirmatoin.prototype.checkConfirmation = function (event) {
+RevealSeedConfirmation.prototype.checkConfirmation = function (event) {
if (event.key === 'Enter') {
event.preventDefault()
this.revealSeedWords()
}
}
-RevealSeedConfirmatoin.prototype.revealSeedWords = function () {
+RevealSeedConfirmation.prototype.revealSeedWords = function () {
this.setState({ confirmationWrong: false })
const confirmBox = document.getElementById('confirm-box')
diff --git a/ui/app/first-time/restore-vault.js b/ui/app/keychains/hd/restore-vault.js
index 684781e50..06e51d9b3 100644
--- a/ui/app/first-time/restore-vault.js
+++ b/ui/app/keychains/hd/restore-vault.js
@@ -1,24 +1,27 @@
const inherits = require('util').inherits
-const Component = require('react').Component
+const PersistentForm = require('../../../lib/persistent-form')
const connect = require('react-redux').connect
const h = require('react-hyperscript')
-const actions = require('../actions')
+const actions = require('../../actions')
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
-inherits(RestoreVaultScreen, Component)
+inherits(RestoreVaultScreen, PersistentForm)
function RestoreVaultScreen () {
- Component.call(this)
+ PersistentForm.call(this)
}
function mapStateToProps (state) {
return {
warning: state.appState.warning,
+ forgottenPassword: state.appState.forgottenPassword,
}
}
RestoreVaultScreen.prototype.render = function () {
var state = this.props
+ this.persistentFormParentId = 'restore-vault-form'
+
return (
h('.initialize-screen.flex-column.flex-center.flex-grow', [
@@ -39,6 +42,9 @@ RestoreVaultScreen.prototype.render = function () {
// wallet seed entry
h('h3', 'Wallet Seed'),
h('textarea.twelve-word-phrase.letter-spacey', {
+ dataset: {
+ persistentFormId: 'wallet-seed',
+ },
placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
}),
@@ -47,6 +53,9 @@ RestoreVaultScreen.prototype.render = function () {
type: 'password',
id: 'password-box',
placeholder: 'New Password (min 8 chars)',
+ dataset: {
+ persistentFormId: 'password',
+ },
style: {
width: 260,
marginTop: 12,
@@ -58,7 +67,10 @@ RestoreVaultScreen.prototype.render = function () {
type: 'password',
id: 'password-box-confirm',
placeholder: 'Confirm Password',
- onKeyPress: this.onMaybeCreate.bind(this),
+ onKeyPress: this.createOnEnter.bind(this),
+ dataset: {
+ persistentFormId: 'password-confirmation',
+ },
style: {
width: 260,
marginTop: 16,
@@ -85,27 +97,30 @@ RestoreVaultScreen.prototype.render = function () {
// submit
h('button.primary', {
- onClick: this.restoreVault.bind(this),
+ onClick: this.createNewVaultAndRestore.bind(this),
}, 'OK'),
]),
-
])
)
}
RestoreVaultScreen.prototype.showInitializeMenu = function () {
- this.props.dispatch(actions.showInitializeMenu())
+ if (this.props.forgottenPassword) {
+ this.props.dispatch(actions.backToUnlockView())
+ } else {
+ this.props.dispatch(actions.showInitializeMenu())
+ }
}
-RestoreVaultScreen.prototype.onMaybeCreate = function (event) {
+RestoreVaultScreen.prototype.createOnEnter = function (event) {
if (event.key === 'Enter') {
- this.restoreVault()
+ this.createNewVaultAndRestore()
}
}
-RestoreVaultScreen.prototype.restoreVault = function () {
+RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
// check password
var passwordBox = document.getElementById('password-box')
var password = passwordBox.value
@@ -133,5 +148,5 @@ RestoreVaultScreen.prototype.restoreVault = function () {
// submit
this.warning = null
this.props.dispatch(actions.displayWarning(this.warning))
- this.props.dispatch(actions.recoverFromSeed(password, seed))
+ this.props.dispatch(actions.createNewVaultAndRestore(password, seed))
}
diff --git a/ui/app/new-keychain.js b/ui/app/new-keychain.js
new file mode 100644
index 000000000..cc9633166
--- /dev/null
+++ b/ui/app/new-keychain.js
@@ -0,0 +1,29 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const h = require('react-hyperscript')
+const connect = require('react-redux').connect
+
+module.exports = connect(mapStateToProps)(NewKeychain)
+
+function mapStateToProps (state) {
+ return {}
+}
+
+inherits(NewKeychain, Component)
+function NewKeychain () {
+ Component.call(this)
+}
+
+NewKeychain.prototype.render = function () {
+ // const props = this.props
+
+ return (
+ h('div', {
+ style: {
+ background: 'blue',
+ },
+ }, [
+ h('h1', `Here's a list!!!!`),
+ ])
+ )
+}
diff --git a/ui/app/reducers.js b/ui/app/reducers.js
index a691cf614..4d10e2b39 100644
--- a/ui/app/reducers.js
+++ b/ui/app/reducers.js
@@ -41,7 +41,7 @@ function rootReducer (state, action) {
return state
}
-window.logState = function() {
+window.logState = function () {
var stateString = JSON.stringify(window.METAMASK_CACHED_LOG_STATE, null, 2)
console.log(stateString)
}
diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js
index 8c2696877..de6536c2e 100644
--- a/ui/app/reducers/app.js
+++ b/ui/app/reducers/app.js
@@ -1,15 +1,16 @@
const extend = require('xtend')
const actions = require('../actions')
const txHelper = require('../../lib/tx-helper')
+const notification = require('../../../app/scripts/lib/notifications')
module.exports = reduceApp
function reduceApp (state, action) {
// clone and defaults
- const selectedAccount = state.metamask.selectedAccount
+ const selectedAddress = state.metamask.selectedAddress
const pendingTxs = hasPendingTxs(state)
let name = 'accounts'
- if (selectedAccount) {
+ if (selectedAddress) {
name = 'accountDetail'
}
if (pendingTxs) {
@@ -19,7 +20,7 @@ function reduceApp (state, action) {
var defaultView = {
name,
detailView: null,
- context: selectedAccount,
+ context: selectedAddress,
}
// confirm seed words
@@ -28,17 +29,13 @@ function reduceApp (state, action) {
name: 'createVaultComplete',
seedWords,
}
- var ethStoreWarning = {
- name: 'EthStoreWarning',
- }
var appState = extend({
menuOpen: false,
- currentView: seedWords ? seedConfView : !state.metamask.isEthConfirmed ? ethStoreWarning : defaultView,
+ currentView: seedWords ? seedConfView : defaultView,
accountDetail: {
subview: 'transactions',
},
- currentDomain: 'example.com',
transForward: true, // Used to render transition direction
isLoading: false, // Used to display loading indicator
warning: null, // Used to display error text
@@ -46,7 +43,19 @@ function reduceApp (state, action) {
switch (action.type) {
- // intialize
+ // transition methods
+
+ case actions.TRANSITION_FORWARD:
+ return extend(appState, {
+ transForward: true,
+ })
+
+ case actions.TRANSITION_BACKWARD:
+ return extend(appState, {
+ transForward: false,
+ })
+
+ // intialize
case actions.SHOW_CREATE_VAULT:
return extend(appState, {
@@ -63,6 +72,16 @@ function reduceApp (state, action) {
name: 'restoreVault',
},
transForward: true,
+ forgottenPassword: true,
+ })
+
+ case actions.FORGOT_PASSWORD:
+ return extend(appState, {
+ currentView: {
+ name: 'restoreVault',
+ },
+ transForward: false,
+ forgottenPassword: true,
})
case actions.SHOW_INIT_MENU:
@@ -80,6 +99,14 @@ function reduceApp (state, action) {
transForward: action.value,
})
+ case actions.SHOW_IMPORT_PAGE:
+ return extend(appState, {
+ currentView: {
+ name: 'import-menu',
+ },
+ transForward: true,
+ })
+
case actions.SHOW_INFO_PAGE:
return extend(appState, {
currentView: {
@@ -109,6 +136,15 @@ function reduceApp (state, action) {
isLoading: false,
})
+ case actions.NEW_ACCOUNT_SCREEN:
+ return extend(appState, {
+ currentView: {
+ name: 'new-account',
+ context: appState.currentView.context,
+ },
+ transForward: true,
+ })
+
case actions.SHOW_SEND_PAGE:
return extend(appState, {
currentView: {
@@ -119,10 +155,20 @@ function reduceApp (state, action) {
warning: null,
})
+ case actions.SHOW_NEW_KEYCHAIN:
+ return extend(appState, {
+ currentView: {
+ name: 'newKeychain',
+ context: appState.currentView.context,
+ },
+ transForward: true,
+ })
+
// unlock
case actions.UNLOCK_METAMASK:
return extend(appState, {
+ forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null,
detailView: {},
transForward: true,
isLoading: false,
@@ -136,6 +182,25 @@ function reduceApp (state, action) {
warning: null,
})
+ case actions.BACK_TO_INIT_MENU:
+ return extend(appState, {
+ warning: null,
+ transForward: false,
+ forgottenPassword: true,
+ currentView: {
+ name: 'InitMenu',
+ },
+ })
+
+ case actions.BACK_TO_UNLOCK_VIEW:
+ return extend(appState, {
+ warning: null,
+ transForward: true,
+ forgottenPassword: false,
+ currentView: {
+ name: 'UnlockScreen',
+ },
+ })
// reveal seed words
case actions.REVEAL_SEED_CONFIRMATION:
@@ -170,6 +235,7 @@ function reduceApp (state, action) {
case actions.SHOW_ACCOUNT_DETAIL:
return extend(appState, {
+ forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null,
currentView: {
name: 'accountDetail',
context: action.value,
@@ -206,6 +272,13 @@ function reduceApp (state, action) {
isLoading: false,
warning: null,
scrollToBottom: false,
+ forgottenPassword: false,
+ })
+
+ case actions.SHOW_NOTICE:
+ return extend(appState, {
+ transForward: true,
+ isLoading: false,
})
case actions.REVEAL_ACCOUNT:
@@ -219,7 +292,7 @@ function reduceApp (state, action) {
name: 'confTx',
context: 0,
},
- transForward: true,
+ transForward: action.transForward,
warning: null,
})
@@ -234,10 +307,11 @@ function reduceApp (state, action) {
})
case actions.COMPLETED_TX:
- var unconfTxs = state.metamask.unconfTxs
- var unconfMsgs = state.metamask.unconfMsgs
+ var unapprovedTxs = state.metamask.unapprovedTxs
+ var unapprovedMsgs = state.metamask.unapprovedMsgs
+ var network = state.metamask.network
- var unconfTxList = txHelper(unconfTxs, unconfMsgs)
+ var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, network)
.filter(tx => tx !== tx.id)
if (unconfTxList && unconfTxList.length > 0) {
@@ -250,6 +324,8 @@ function reduceApp (state, action) {
warning: null,
})
} else {
+ notification.closePopup()
+
return extend(appState, {
transForward: false,
warning: null,
@@ -304,12 +380,13 @@ function reduceApp (state, action) {
case actions.UNLOCK_FAILED:
return extend(appState, {
- warning: 'Incorrect password. Try again.',
+ warning: action.value || 'Incorrect password. Try again.',
})
case actions.SHOW_LOADING:
return extend(appState, {
isLoading: true,
+ loadingMessage: action.value,
})
case actions.HIDE_LOADING:
@@ -383,11 +460,11 @@ function reduceApp (state, action) {
transForward: true,
currentView: {
name: 'buyEth',
- context: appState.currentView.context,
+ context: appState.currentView.name,
},
buyView: {
subview: 'buyForm',
- amount: '5.00',
+ amount: '15.00',
buyAddress: action.value,
formView: {
coinbase: true,
@@ -470,9 +547,9 @@ function reduceApp (state, action) {
return extend(appState, {
qrRequested: true,
transForward: true,
+
Qr: {
message: action.value.message,
- image: action.value.qr,
data: action.value.data,
},
})
@@ -486,7 +563,6 @@ function reduceApp (state, action) {
transForward: true,
Qr: {
message: action.value.message,
- image: action.value.qr,
data: action.value.data,
},
})
@@ -496,16 +572,18 @@ function reduceApp (state, action) {
}
function hasPendingTxs (state) {
- var unconfTxs = state.metamask.unconfTxs
- var unconfMsgs = state.metamask.unconfMsgs
- var unconfTxList = txHelper(unconfTxs, unconfMsgs)
+ var unapprovedTxs = state.metamask.unapprovedTxs
+ var unapprovedMsgs = state.metamask.unapprovedMsgs
+ var network = state.metamask.network
+ var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, network)
return unconfTxList.length > 0
}
function indexForPending (state, txId) {
- var unconfTxs = state.metamask.unconfTxs
- var unconfMsgs = state.metamask.unconfMsgs
- var unconfTxList = txHelper(unconfTxs, unconfMsgs)
+ var unapprovedTxs = state.metamask.unapprovedTxs
+ var unapprovedMsgs = state.metamask.unapprovedMsgs
+ var network = state.metamask.network
+ var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, network)
let idx
unconfTxList.forEach((tx, i) => {
if (tx.id === txId) {
@@ -514,5 +592,3 @@ function indexForPending (state, txId) {
})
return idx
}
-
-
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 7f18480cb..3875cf6d1 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -10,14 +10,14 @@ function reduceMetamask (state, action) {
var metamaskState = extend({
isInitialized: false,
isUnlocked: false,
- isEthConfirmed: false,
- currentDomain: 'example.com',
rpcTarget: 'https://rawtestrpc.metamask.io/',
identities: {},
- unconfTxs: {},
+ unapprovedTxs: {},
currentFiat: 'USD',
conversionRate: 0,
conversionDate: 'N/A',
+ noActiveNotices: true,
+ lastUnreadNotice: undefined,
}, state.metamask)
switch (action.type) {
@@ -27,24 +27,25 @@ function reduceMetamask (state, action) {
delete newState.seedWords
return newState
- case actions.UPDATE_METAMASK_STATE:
- return extend(metamaskState, action.value)
-
- case actions.AGREE_TO_DISCLAIMER:
+ case actions.SHOW_NOTICE:
return extend(metamaskState, {
- isConfirmed: true,
+ noActiveNotices: false,
+ lastUnreadNotice: action.value,
})
- case actions.AGREE_TO_ETH_WARNING:
+ case actions.CLEAR_NOTICES:
return extend(metamaskState, {
- isEthConfirmed: !metamaskState.isEthConfirmed,
+ noActiveNotices: true,
})
+ case actions.UPDATE_METAMASK_STATE:
+ return extend(metamaskState, action.value)
+
case actions.UNLOCK_METAMASK:
return extend(metamaskState, {
isUnlocked: true,
isInitialized: true,
- selectedAccount: action.value,
+ selectedAddress: action.value,
})
case actions.LOCK_METAMASK:
@@ -70,17 +71,17 @@ function reduceMetamask (state, action) {
case actions.COMPLETED_TX:
var stringId = String(action.id)
newState = extend(metamaskState, {
- unconfTxs: {},
- unconfMsgs: {},
+ unapprovedTxs: {},
+ unapprovedMsgs: {},
})
- for (const id in metamaskState.unconfTxs) {
+ for (const id in metamaskState.unapprovedTxs) {
if (id !== stringId) {
- newState.unconfTxs[id] = metamaskState.unconfTxs[id]
+ newState.unapprovedTxs[id] = metamaskState.unapprovedTxs[id]
}
}
- for (const id in metamaskState.unconfMsgs) {
+ for (const id in metamaskState.unapprovedMsgs) {
if (id !== stringId) {
- newState.unconfMsgs[id] = metamaskState.unconfMsgs[id]
+ newState.unapprovedMsgs[id] = metamaskState.unapprovedMsgs[id]
}
}
return newState
@@ -95,7 +96,7 @@ function reduceMetamask (state, action) {
newState = extend(metamaskState, {
isUnlocked: true,
isInitialized: true,
- selectedAccount: action.value,
+ selectedAddress: action.value,
})
delete newState.seedWords
return newState
@@ -104,7 +105,6 @@ function reduceMetamask (state, action) {
newState = extend(metamaskState, {
isUnlocked: true,
isInitialized: true,
- selectedAccount: action.value,
selectedAddress: action.value,
})
delete newState.seedWords
diff --git a/ui/app/send.js b/ui/app/send.js
index 06ea199f4..d16270b41 100644
--- a/ui/app/send.js
+++ b/ui/app/send.js
@@ -1,5 +1,5 @@
const inherits = require('util').inherits
-const Component = require('react').Component
+const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const Identicon = require('./components/identicon')
@@ -7,14 +7,16 @@ const actions = require('./actions')
const util = require('./util')
const numericBalance = require('./util').numericBalance
const addressSummary = require('./util').addressSummary
-const EtherBalance = require('./components/eth-balance')
+const isHex = require('./util').isHex
+const EthBalance = require('./components/eth-balance')
const ethUtil = require('ethereumjs-util')
-
+const RangeSlider = require('./components/range-slider')
+const Tooltip = require('./components/tooltip')
module.exports = connect(mapStateToProps)(SendTransactionScreen)
function mapStateToProps (state) {
var result = {
- address: state.metamask.selectedAccount,
+ address: state.metamask.selectedAddress,
accounts: state.metamask.accounts,
identities: state.metamask.identities,
warning: state.appState.warning,
@@ -29,12 +31,14 @@ function mapStateToProps (state) {
return result
}
-inherits(SendTransactionScreen, Component)
+inherits(SendTransactionScreen, PersistentForm)
function SendTransactionScreen () {
- Component.call(this)
+ PersistentForm.call(this)
}
SendTransactionScreen.prototype.render = function () {
+ this.persistentFormParentId = 'send-tx-form'
+
var state = this.props
var address = state.address
var account = state.account
@@ -48,7 +52,7 @@ SendTransactionScreen.prototype.render = function () {
// Sender Profile
//
- h('.account-data-subsection.flex-column.flex-grow', {
+ h('.account-data-subsection.flex-row.flex-grow', {
style: {
margin: '0 20px',
},
@@ -57,10 +61,9 @@ SendTransactionScreen.prototype.render = function () {
// header - identicon + nav
h('.flex-row.flex-space-between', {
style: {
- marginTop: 28,
+ marginTop: '15px',
},
}, [
-
// back button
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
onClick: this.back.bind(this),
@@ -75,43 +78,53 @@ SendTransactionScreen.prototype.render = function () {
]),
// invisible place holder
- h('i.fa.fa-users.fa-lg.invisible'),
+ h('i.fa.fa-users.fa-lg.invisible', {
+ style: {
+ marginTop: '28px',
+ },
+ }),
]),
// account label
- h('h2.font-medium.color-forest.flex-center', {
- style: {
- paddingTop: 8,
- marginBottom: 8,
- },
- }, identity && identity.name),
- // address and getter actions
- h('.flex-row.flex-center', {
+ h('.flex-column', {
style: {
- marginBottom: 8,
+ marginTop: '10px',
+ alignItems: 'flex-start',
},
}, [
+ h('h2.font-medium.color-forest.flex-center', {
+ style: {
+ paddingTop: '8px',
+ marginBottom: '8px',
+ },
+ }, identity && identity.name),
- h('div', {
+ // address and getter actions
+ h('.flex-row.flex-center', {
style: {
- lineHeight: '16px',
+ marginBottom: '8px',
},
- }, addressSummary(address)),
+ }, [
- ]),
+ h('div', {
+ style: {
+ lineHeight: '16px',
+ },
+ }, addressSummary(address)),
- // balance
- h('.flex-row.flex-center', [
+ ]),
- // h('div', formatBalance(account && account.balance)),
- h(EtherBalance, {
- value: account && account.balance,
- }),
+ // balance
+ h('.flex-row.flex-center', [
- ]),
+ h(EthBalance, {
+ value: account && account.balance,
+ }),
+ ]),
+ ]),
]),
//
@@ -122,8 +135,8 @@ SendTransactionScreen.prototype.render = function () {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
- marginTop: 32,
- marginBottom: 16,
+ marginTop: '15px',
+ marginBottom: '16px',
},
}, [
'Send Transaction',
@@ -137,6 +150,9 @@ SendTransactionScreen.prototype.render = function () {
h('input.large-input', {
name: 'address',
placeholder: 'Recipient Address',
+ dataset: {
+ persistentFormId: 'recipient-address',
+ },
}),
]),
@@ -148,7 +164,10 @@ SendTransactionScreen.prototype.render = function () {
placeholder: 'Amount',
type: 'number',
style: {
- marginRight: 6,
+ marginRight: '6px',
+ },
+ dataset: {
+ persistentFormId: 'tx-amount',
},
}),
@@ -164,20 +183,19 @@ SendTransactionScreen.prototype.render = function () {
//
// Optional Fields
//
-
h('h3.flex-center.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
- marginTop: 16,
- marginBottom: 16,
+ marginTop: '16px',
+ marginBottom: '16px',
},
}, [
- 'Transactional Data (optional)',
+ 'Transaction Data (optional)',
]),
// 'data' field
- h('section.flex-row.flex-center', [
+ h('section.flex-column.flex-center', [
h('input.large-input', {
name: 'txData',
placeholder: '0x01234',
@@ -185,11 +203,79 @@ SendTransactionScreen.prototype.render = function () {
width: '100%',
resize: 'none',
},
+ dataset: {
+ persistentFormId: 'tx-data',
+ },
}),
]),
+ // custom gasPrice field
+ h('h3.flex-center.text-transform-uppercase', {
+ style: {
+ background: '#EBEBEB',
+ color: '#AEAEAE',
+ marginBottom: '5px',
+ },
+ }, [
+ 'Transaction Fee (optional)',
+ h(Tooltip, {
+ title: `
+ This is used to set the transaction's gas price.
+ Setting it to 100% will use the full recommended value. `,
+ }, [
+ h('i.fa.fa-question-circle', {
+ style: {
+ marginLeft: '5px',
+ },
+ }),
+ ]),
+ ]),
- ])
+ h('section.flex-column.flex-center', [
+ h('.flex-row', [
+ h(RangeSlider, {
+ name: 'gasInput',
+ options: {
+ mirrorInput: true,
+ defaultValue: 100,
+ min: 80,
+ max: 220,
+ },
+ style: {
+ container: {
+ marginBottom: '16px',
+ },
+ range: {
+ width: '68vw',
+ },
+ input: {
+ width: '5em',
+ marginLeft: '5px',
+ },
+ },
+ }),
+ h('div', {
+ style: {
+ fontSize: '12px',
+ paddingTop: '8px',
+ paddingLeft: '5px',
+ },
+ }, '%'),
+ ]),
+ h('.flex-row', {
+ style: {
+ justifyContent: 'space-between',
+ width: '243px',
+ position: 'relative',
+ fontSize: '12px',
+ right: '42px',
+ bottom: '30px',
+ },
+ }, [
+ h('span', 'Cheaper'), h('span', 'Faster'),
+ ]),
+ ]),
+ ])
)
}
@@ -203,11 +289,12 @@ SendTransactionScreen.prototype.back = function () {
this.props.dispatch(actions.backToAccountDetail(address))
}
-SendTransactionScreen.prototype.onSubmit = function () {
+SendTransactionScreen.prototype.onSubmit = function (gasPrice) {
const recipient = document.querySelector('input[name="address"]').value
const input = document.querySelector('input[name="amount"]').value
const value = util.normalizeEthStringToWei(input)
const txData = document.querySelector('input[name="txData"]').value
+ const gasMultiplier = document.querySelector('input[name="gasInput"]').value
const balance = this.props.balance
let message
@@ -226,12 +313,17 @@ SendTransactionScreen.prototype.onSubmit = function () {
return this.props.dispatch(actions.displayWarning(message))
}
+ if (!isHex(ethUtil.stripHexPrefix(txData)) && txData) {
+ message = 'Transaction data must be hex string.'
+ return this.props.dispatch(actions.displayWarning(message))
+ }
+
this.props.dispatch(actions.hideWarning())
- this.props.dispatch(actions.showLoadingIndication())
var txParams = {
from: this.props.address,
value: '0x' + value.toString(16),
+ gasMultiplier: gasMultiplier * 0.01,
}
if (recipient) txParams.to = ethUtil.addHexPrefix(recipient)
diff --git a/ui/app/store.js b/ui/app/store.js
index 8d891bdc9..ba9e58b49 100644
--- a/ui/app/store.js
+++ b/ui/app/store.js
@@ -4,7 +4,7 @@ const thunkMiddleware = require('redux-thunk')
const rootReducer = require('./reducers')
const createLogger = require('redux-logger')
-global.METAMASK_DEBUG = false
+global.METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
module.exports = configureStore
diff --git a/ui/app/unlock.js b/ui/app/unlock.js
index 7cc1699c3..1aee3c5d0 100644
--- a/ui/app/unlock.js
+++ b/ui/app/unlock.js
@@ -26,47 +26,59 @@ UnlockScreen.prototype.render = function () {
const state = this.props
const warning = state.warning
return (
-
- h('.unlock-screen.flex-column.flex-center.flex-grow', [
-
- h(Mascot, {
- animationEventEmitter: this.animationEventEmitter,
- }),
-
- h('h1', {
- style: {
- fontSize: '1.4em',
- textTransform: 'uppercase',
- color: '#7F8082',
- },
- }, 'MetaMask'),
-
- h('input.large-input', {
- type: 'password',
- id: 'password-box',
- placeholder: 'enter password',
- style: {
-
- },
- onKeyPress: this.onKeyPress.bind(this),
- onInput: this.inputChanged.bind(this),
- }),
-
- h('.error', {
- style: {
- display: warning ? 'block' : 'none',
- },
- }, warning),
-
- h('button.primary.cursor-pointer', {
- onClick: this.onSubmit.bind(this),
- style: {
- margin: 10,
- },
- }, 'Unlock'),
-
+ h('.flex-column', [
+ h('.unlock-screen.flex-column.flex-center.flex-grow', [
+
+ h(Mascot, {
+ animationEventEmitter: this.animationEventEmitter,
+ }),
+
+ h('h1', {
+ style: {
+ fontSize: '1.4em',
+ textTransform: 'uppercase',
+ color: '#7F8082',
+ },
+ }, 'MetaMask'),
+
+ h('input.large-input', {
+ type: 'password',
+ id: 'password-box',
+ placeholder: 'enter password',
+ style: {
+
+ },
+ onKeyPress: this.onKeyPress.bind(this),
+ onInput: this.inputChanged.bind(this),
+ }),
+
+ h('.error', {
+ style: {
+ display: warning ? 'block' : 'none',
+ padding: '0 20px',
+ textAlign: 'center',
+ },
+ }, warning),
+
+ h('button.primary.cursor-pointer', {
+ onClick: this.onSubmit.bind(this),
+ style: {
+ margin: 10,
+ },
+ }, 'Unlock'),
+ ]),
+
+ h('.flex-row.flex-center.flex-grow', [
+ h('p.pointer', {
+ onClick: () => this.props.dispatch(actions.forgotPassword()),
+ style: {
+ fontSize: '0.8em',
+ color: 'rgb(247, 134, 28)',
+ textDecoration: 'underline',
+ },
+ }, 'I forgot my password.'),
+ ]),
])
-
)
}
@@ -104,7 +116,3 @@ UnlockScreen.prototype.inputChanged = function (event) {
y: boundingRect.top + coordinates.top - element.scrollTop,
})
}
-
-UnlockScreen.prototype.emitAnim = function (name, a, b, c) {
- this.animationEventEmitter.emit(name, a, b, c)
-}
diff --git a/ui/app/util.js b/ui/app/util.js
index e4b77e2bc..7a56bf6a0 100644
--- a/ui/app/util.js
+++ b/ui/app/util.js
@@ -35,6 +35,7 @@ module.exports = {
normalizeNumberToWei: normalizeNumberToWei,
valueTable: valueTable,
bnTable: bnTable,
+ isHex: isHex,
}
function valuesFor (obj) {
@@ -209,3 +210,7 @@ function readableDate (ms) {
var time = `${hours}:${minutes.substr(-2)}:${seconds.substr(-2)}`
return `${dateStr} ${time}`
}
+
+function isHex (str) {
+ return Boolean(str.match(/^(0x)?[0-9a-fA-F]+$/))
+}
diff --git a/ui/css.js b/ui/css.js
index 01f317acd..043363cd7 100644
--- a/ui/css.js
+++ b/ui/css.js
@@ -10,6 +10,7 @@ var cssFiles = {
'index.css': fs.readFileSync(path.join(__dirname, '/app/css/index.css'), 'utf8'),
'transitions.css': fs.readFileSync(path.join(__dirname, '/app/css/transitions.css'), 'utf8'),
'react-tooltip-component.css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-tooltip-component', 'dist', 'react-tooltip-component.css'), 'utf8'),
+ 'react-css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-select', 'dist', 'react-select.css'), 'utf8'),
}
function bundleCss () {
diff --git a/ui/example.js b/ui/example.js
index f4126438c..4627c0e9c 100644
--- a/ui/example.js
+++ b/ui/example.js
@@ -29,7 +29,7 @@ var identities = {
},
}
-var unconfTxs = {}
+var unapprovedTxs = {}
addUnconfTx({
from: '0x222462427bcc9133bb46e88bcbe39cd7ef0e7222',
to: '0x1113462427bcc9133bb46e88bcbe39cd7ef0e111',
@@ -45,7 +45,7 @@ addUnconfTx({
function addUnconfTx (txParams) {
var time = (new Date()).getTime()
var id = createRandomId()
- unconfTxs[id] = {
+ unapprovedTxs[id] = {
id: id,
txParams: txParams,
time: time,
@@ -53,14 +53,14 @@ function addUnconfTx (txParams) {
}
var isUnlocked = false
-var selectedAddress = null
+var selectedAccount = null
function getState () {
return {
isUnlocked: isUnlocked,
identities: isUnlocked ? identities : {},
- unconfTxs: isUnlocked ? unconfTxs : {},
- selectedAddress: selectedAddress,
+ unapprovedTxs: isUnlocked ? unapprovedTxs : {},
+ selectedAccount: selectedAccount,
}
}
@@ -85,8 +85,8 @@ accountManager.submitPassword = function (password, cb) {
}
}
-accountManager.setSelectedAddress = function (address, cb) {
- selectedAddress = address
+accountManager.setSelectedAccount = function (address, cb) {
+ selectedAccount = address
cb(null, getState())
this._didUpdate()
}
diff --git a/ui/index.js b/ui/index.js
index 8cf74f6ee..8855064f6 100644
--- a/ui/index.js
+++ b/ui/index.js
@@ -3,12 +3,12 @@ const h = require('react-hyperscript')
const Root = require('./app/root')
const actions = require('./app/actions')
const configureStore = require('./app/store')
-
+const txHelper = require('./lib/tx-helper')
module.exports = launchApp
function launchApp (opts) {
var accountManager = opts.accountManager
- actions._setAccountManager(accountManager)
+ actions._setBackgroundConnection(accountManager)
// check if we are unlocked first
accountManager.getState(function (err, metamaskState) {
@@ -25,16 +25,15 @@ function startApp (metamaskState, accountManager, opts) {
metamask: metamaskState,
// appState represents the current tab's popup state
- appState: {
- currentDomain: opts.currentDomain,
- },
+ appState: {},
// Which blockchain we are using:
networkVersion: opts.networkVersion,
})
// if unconfirmed txs, start on txConf page
- if (Object.keys(metamaskState.unconfTxs || {}).length) {
+ var unapprovedTxsAll = txHelper(metamaskState.unapprovedTxs, metamaskState.unapprovedMsgs, metamaskState.network)
+ if (unapprovedTxsAll.length > 0) {
store.dispatch(actions.showConfTxPage())
}
diff --git a/ui/lib/account-link.js b/ui/lib/account-link.js
index eb958e22d..77db0851d 100644
--- a/ui/lib/account-link.js
+++ b/ui/lib/account-link.js
@@ -1,4 +1,4 @@
-module.exports = function(address, network) {
+module.exports = function (address, network) {
const net = parseInt(network)
let link
@@ -7,6 +7,9 @@ module.exports = function(address, network) {
link = `http://etherscan.io/address/${address}`
break
case 2: // morden test net
+ link = `http://morden.etherscan.io/address/${address}`
+ break
+ case 3: // ropsten test net
link = `http://testnet.etherscan.io/address/${address}`
break
default:
diff --git a/ui/lib/contract-namer.js b/ui/lib/contract-namer.js
index c99d44de6..a94c62b62 100644
--- a/ui/lib/contract-namer.js
+++ b/ui/lib/contract-namer.js
@@ -8,23 +8,22 @@
// Nickname keys must be stored in lower case.
const nicknames = {}
-module.exports = function(addr, identities = {}) {
-
+module.exports = function (addr, identities = {}) {
const address = addr.toLowerCase()
const ids = hashFromIdentities(identities)
return addrFromHash(address, ids) || addrFromHash(address, nicknames)
}
-function hashFromIdentities(identities) {
+function hashFromIdentities (identities) {
const result = {}
- for (let key in identities) {
+ for (const key in identities) {
result[key] = identities[key].name
}
return result
}
-function addrFromHash(addr, hash) {
+function addrFromHash (addr, hash) {
const address = addr.toLowerCase()
return hash[address] || null
}
diff --git a/ui/lib/explorer-link.js b/ui/lib/explorer-link.js
index 2993d1cf1..dc6be2984 100644
--- a/ui/lib/explorer-link.js
+++ b/ui/lib/explorer-link.js
@@ -5,7 +5,7 @@ module.exports = function (hash, network) {
case 1: // main net
prefix = ''
break
- case 2: // morden test net
+ case 3: // morden test net
prefix = 'testnet.'
break
default:
diff --git a/ui/lib/icon-factory.js b/ui/lib/icon-factory.js
index a30041114..82cc839d6 100644
--- a/ui/lib/icon-factory.js
+++ b/ui/lib/icon-factory.js
@@ -55,6 +55,6 @@ function jsNumberForAddress (address) {
return seed
}
-function toDataUri(identiconSrc){
+function toDataUri (identiconSrc) {
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(identiconSrc)
-} \ No newline at end of file
+}
diff --git a/ui/lib/lost-accounts-notice.js b/ui/lib/lost-accounts-notice.js
new file mode 100644
index 000000000..948b13db6
--- /dev/null
+++ b/ui/lib/lost-accounts-notice.js
@@ -0,0 +1,23 @@
+const summary = require('../app/util').addressSummary
+
+module.exports = function (lostAccounts) {
+ return {
+ date: new Date().toDateString(),
+ title: 'Account Problem Caught',
+ body: `MetaMask has fixed a bug where some accounts were previously mis-generated. This was a rare issue, but you were affected!
+
+We have successfully imported the accounts that were mis-generated, but they will no longer be recovered with your normal seed phrase.
+
+We have marked the affected accounts as "Loose", and recommend you transfer ether and tokens away from those accounts, or export & back them up elsewhere.
+
+Your affected accounts are:
+${lostAccounts.map(acct => ` - ${summary(acct)}`).join('\n')}
+
+These accounts have been marked as "Loose" so they will be easy to recognize in the account list.
+
+For more information, please read [our blog post.][1]
+
+[1]: https://medium.com/metamask/metamask-3-migration-guide-914b79533cdd#.7d8ktj4h3
+ `,
+ }
+}
diff --git a/ui/lib/persistent-form.js b/ui/lib/persistent-form.js
new file mode 100644
index 000000000..d4dc20b03
--- /dev/null
+++ b/ui/lib/persistent-form.js
@@ -0,0 +1,61 @@
+const inherits = require('util').inherits
+const Component = require('react').Component
+const defaultKey = 'persistent-form-default'
+const eventName = 'keyup'
+
+module.exports = PersistentForm
+
+function PersistentForm () {
+ Component.call(this)
+}
+
+inherits(PersistentForm, Component)
+
+PersistentForm.prototype.componentDidMount = function () {
+ const fields = document.querySelectorAll('[data-persistent-formid]')
+ const store = this.getPersistentStore()
+
+ for (var i = 0; i < fields.length; i++) {
+ const field = fields[i]
+ const key = field.getAttribute('data-persistent-formid')
+ const cached = store[key]
+ if (cached !== undefined) {
+ field.value = cached
+ }
+
+ field.addEventListener(eventName, this.persistentFieldDidUpdate.bind(this))
+ }
+}
+
+PersistentForm.prototype.getPersistentStore = function () {
+ let store = window.localStorage[this.persistentFormParentId || defaultKey]
+ if (store && store !== 'null') {
+ store = JSON.parse(store)
+ } else {
+ store = {}
+ }
+ return store
+}
+
+PersistentForm.prototype.setPersistentStore = function (newStore) {
+ window.localStorage[this.persistentFormParentId || defaultKey] = JSON.stringify(newStore)
+}
+
+PersistentForm.prototype.persistentFieldDidUpdate = function (event) {
+ const field = event.target
+ const store = this.getPersistentStore()
+ const key = field.getAttribute('data-persistent-formid')
+ const val = field.value
+ store[key] = val
+ this.setPersistentStore(store)
+}
+
+PersistentForm.prototype.componentWillUnmount = function () {
+ const fields = document.querySelectorAll('[data-persistent-formid]')
+ for (var i = 0; i < fields.length; i++) {
+ const field = fields[i]
+ field.removeEventListener(eventName, this.persistentFieldDidUpdate.bind(this))
+ }
+ this.setPersistentStore({})
+}
+
diff --git a/ui/lib/tx-helper.js b/ui/lib/tx-helper.js
index 8f15cd3cc..fa7a94cdc 100644
--- a/ui/lib/tx-helper.js
+++ b/ui/lib/tx-helper.js
@@ -1,8 +1,8 @@
const valuesFor = require('../app/util').valuesFor
-module.exports = function (unconfTxs, unconfMsgs) {
- var txValues = valuesFor(unconfTxs)
- var msgValues = valuesFor(unconfMsgs)
+module.exports = function (unapprovedTxs, unapprovedMsgs, network) {
+ var txValues = network ? valuesFor(unapprovedTxs).filter(tx => tx.txParams.metamaskNetworkId === network) : valuesFor(unapprovedTxs)
+ var msgValues = valuesFor(unapprovedMsgs)
var allValues = txValues.concat(msgValues)
return allValues.sort(tx => tx.time)
}