aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/_locales/en/messages.json11
-rw-r--r--app/_locales/ja/messages.json43
-rw-r--r--app/manifest.json6
-rw-r--r--app/scripts/controllers/preferences.js81
-rw-r--r--app/scripts/controllers/user-actions.js17
-rw-r--r--app/scripts/lib/createErrorMiddleware.js1
-rw-r--r--app/scripts/lib/diagnostics-reporter.js71
-rw-r--r--app/scripts/lib/setupRaven.js2
-rw-r--r--app/scripts/metamask-controller.js38
-rw-r--r--app/scripts/migrations/026.js2
10 files changed, 261 insertions, 11 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 4851508a3..40d362f51 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -523,6 +523,9 @@
"networks": {
"message": "Networks"
},
+ "nevermind": {
+ "message": "Nevermind"
+ },
"newAccount": {
"message": "New Account"
},
@@ -637,9 +640,15 @@
"rejected": {
"message": "Rejected"
},
+ "reset": {
+ "message": "Reset"
+ },
"resetAccount": {
"message": "Reset Account"
},
+ "resetAccountDescription": {
+ "message": "Resetting your account will clear your transaction history."
+ },
"restoreFromSeed": {
"message": "Restore account?"
},
@@ -897,7 +906,7 @@
"message": "Welcome to the New UI (Beta)"
},
"uiWelcomeMessage": {
- "message": "You are now using the new Metamask UI. Take a look around, try out new features like sending tokens, and let us know if you have any issues."
+ "message": "You are now using the new Metamask UI."
},
"unapproved": {
"message": "Unapproved"
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index 3a664ec00..75deeaddf 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -62,6 +62,9 @@
"message": " $1以上 $2以下にして下さい。",
"description": "helper for inputting hex as decimal input"
},
+ "blockiesIdenticon": {
+ "message": "Blockies Identicon を使用"
+ },
"borrowDharma": {
"message": "Dharmaで借りる(ベータ版)"
},
@@ -95,6 +98,9 @@
"confirmTransaction": {
"message": "トランザクションの確認"
},
+ "continue": {
+ "message": "続行"
+ },
"continueToCoinbase": {
"message": "Coinbaseを開く"
},
@@ -359,6 +365,9 @@
"likeToAddTokens": {
"message": "トークンを追加しますか?"
},
+ "links": {
+ "message": "リンク"
+ },
"limit": {
"message": "リミット"
},
@@ -371,12 +380,18 @@
"localhost": {
"message": "Localhost 8545"
},
+ "login": {
+ "message": "ログイン"
+ },
"logout": {
"message": "ログアウト"
},
"loose": {
"message": "外部秘密鍵"
},
+ "max": {
+ "message": "最大"
+ },
"mainnet": {
"message": "Ethereumメインネットワーク"
},
@@ -417,7 +432,7 @@
"message": "新規コントラクト"
},
"newPassword": {
- "message": "新規パスワード(最低8文字)"
+ "message": "新規パスワード(最低8文字)"
},
"newRecipient": {
"message": "新規受取人"
@@ -453,6 +468,9 @@
"message": "または",
"description": "choice between creating or importing a new account"
},
+ "password": {
+ "message": "パスワード"
+ },
"passwordMismatch": {
"message": "パスワードが一致しません。",
"description": "in password creation process, the two new password fields did not match"
@@ -474,6 +492,9 @@
"popularTokens": {
"message": "人気のトークン"
},
+ "privacyMsg": {
+ "message": "プライバシーポリシー"
+ },
"privateKey": {
"message": "秘密鍵",
"description": "select this type of file to use to import an account"
@@ -546,6 +567,12 @@
"message": "ファイルとして保存",
"description": "Account export process"
},
+ "search": {
+ "message": "検索"
+ },
+ "searchResults": {
+ "message": "検索結果"
+ },
"selectService": {
"message": "サービスを選択"
},
@@ -575,7 +602,7 @@
},
"info": {
"message": "情報"
- },
+ },
"shapeshiftBuy": {
"message": "Shapeshiftで交換"
},
@@ -609,6 +636,9 @@
"takesTooLong": {
"message": "送信に時間がかかりますか?"
},
+ "terms": {
+ "message": "利用規約"
+ },
"testFaucet": {
"message": "Faucetをテスト"
},
@@ -619,6 +649,9 @@
"message": "ShapeShiftで $1をETHにする",
"description": "system will fill in deposit type in start of message"
},
+ "token": {
+ "message": "トークン"
+ },
"tokenAddress": {
"message": "トークンアドレス"
},
@@ -690,6 +723,12 @@
"warning": {
"message": "警告"
},
+ "welcomeBack": {
+ "message": "おかえりなさい!"
+ },
+ "welcomeBeta": {
+ "message": "MetaMask ベータ版へようこそ!"
+ },
"whatsThis": {
"message": "この機能について"
},
diff --git a/app/manifest.json b/app/manifest.json
index 141026d10..e3a7fd963 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "4.6.1",
+ "version": "4.7.4",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
@@ -68,6 +68,8 @@
"matches": [
"https://metamask.io/*"
],
- "ids": ["*"]
+ "ids": [
+ "*"
+ ]
}
} \ No newline at end of file
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index a4ff1207e..8411e3a28 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -2,6 +2,7 @@ const ObservableStore = require('obs-store')
const normalizeAddress = require('eth-sig-util').normalize
const extend = require('xtend')
+
class PreferencesController {
/**
@@ -28,7 +29,11 @@ class PreferencesController {
featureFlags: {},
currentLocale: opts.initLangCode,
identities: {},
+ lostIdentities: {},
}, opts.initState)
+
+ this.diagnostics = opts.diagnostics
+
this.store = new ObservableStore(initState)
}
// PUBLIC METHODS
@@ -63,6 +68,13 @@ class PreferencesController {
this.store.updateState({ currentLocale: key })
}
+ /**
+ * Updates identities to only include specified addresses. Removes identities
+ * not included in addresses array
+ *
+ * @param {string[]} addresses An array of hex addresses
+ *
+ */
setAddresses (addresses) {
const oldIdentities = this.store.getState().identities
const identities = addresses.reduce((ids, address, index) => {
@@ -74,6 +86,68 @@ class PreferencesController {
}
/**
+ * Adds addresses to the identities object without removing identities
+ *
+ * @param {string[]} addresses An array of hex addresses
+ *
+ */
+ addAddresses (addresses) {
+ const identities = this.store.getState().identities
+ addresses.forEach((address) => {
+ // skip if already exists
+ if (identities[address]) return
+ // add missing identity
+ const identityCount = Object.keys(identities).length
+ identities[address] = { name: `Account ${identityCount + 1}`, address }
+ })
+ this.store.updateState({ identities })
+ }
+
+ /*
+ * Synchronizes identity entries with known accounts.
+ * Removes any unknown identities, and returns the resulting selected address.
+ *
+ * @param {Array<string>} addresses known to the vault.
+ * @returns {Promise<string>} selectedAddress the selected address.
+ */
+ syncAddresses (addresses) {
+ let { identities, lostIdentities } = this.store.getState()
+
+ let newlyLost = {}
+ Object.keys(identities).forEach((identity) => {
+ if (!addresses.includes(identity)) {
+ newlyLost[identity] = identities[identity]
+ delete identities[identity]
+ }
+ })
+
+ // Identities are no longer present.
+ if (Object.keys(newlyLost).length > 0) {
+
+ // Notify our servers:
+ if (this.diagnostics) this.diagnostics.reportOrphans(newlyLost)
+
+ // store lost accounts
+ for (let key in newlyLost) {
+ lostIdentities[key] = newlyLost[key]
+ }
+ }
+
+ this.store.updateState({ identities, lostIdentities })
+ this.addAddresses(addresses)
+
+ // If the selected account is no longer valid,
+ // select an arbitrary other account:
+ let selected = this.getSelectedAddress()
+ if (!addresses.includes(selected)) {
+ selected = addresses[0]
+ this.setSelectedAddress(selected)
+ }
+
+ return selected
+ }
+
+ /**
* Setter for the `selectedAddress` property
*
* @param {string} _address A new hex address for an account
@@ -111,7 +185,7 @@ class PreferencesController {
/**
* Adds a new token to the token array, or updates the token if passed an address that already exists.
* Modifies the existing tokens array from the store. All objects in the tokens array array AddedToken objects.
- * @see AddedToken {@link AddedToken}
+ * @see AddedToken {@link AddedToken}
*
* @param {string} rawAddress Hex address of the token contract. May or may not be a checksum address.
* @param {string} symbol The symbol of the token
@@ -173,6 +247,7 @@ class PreferencesController {
* @return {Promise<string>}
*/
setAccountLabel (account, label) {
+ if (!account) throw new Error('setAccountLabel requires a valid address, got ' + String(account))
const address = normalizeAddress(account)
const {identities} = this.store.getState()
identities[address] = identities[address] || {}
@@ -197,7 +272,7 @@ class PreferencesController {
}
/**
- * Setter for the `currentAccountTab` property
+ * Setter for the `currentAccountTab` property
*
* @param {string} currentAccountTab Specifies the new tab to be marked as current
* @returns {Promise<void>} Promise resolves with undefined
@@ -215,7 +290,7 @@ class PreferencesController {
* The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the
* end of the list. The current list is modified and returned as a promise.
*
- * @param {string} _url The rpc url to add to the frequentRpcList.
+ * @param {string} _url The rpc url to add to the frequentRpcList.
* @returns {Promise<array>} The updated frequentRpcList.
*
*/
diff --git a/app/scripts/controllers/user-actions.js b/app/scripts/controllers/user-actions.js
new file mode 100644
index 000000000..f777054b8
--- /dev/null
+++ b/app/scripts/controllers/user-actions.js
@@ -0,0 +1,17 @@
+const MessageManager = require('./lib/message-manager')
+const PersonalMessageManager = require('./lib/personal-message-manager')
+const TypedMessageManager = require('./lib/typed-message-manager')
+
+class UserActionController {
+
+ constructor (opts = {}) {
+
+ this.messageManager = new MessageManager()
+ this.personalMessageManager = new PersonalMessageManager()
+ this.typedMessageManager = new TypedMessageManager()
+
+ }
+
+}
+
+module.exports = UserActionController
diff --git a/app/scripts/lib/createErrorMiddleware.js b/app/scripts/lib/createErrorMiddleware.js
index baed99e45..c70beddfd 100644
--- a/app/scripts/lib/createErrorMiddleware.js
+++ b/app/scripts/lib/createErrorMiddleware.js
@@ -59,6 +59,7 @@ function createErrorMiddleware ({ override = true } = {}) {
if (!error) { return done() }
sanitizeRPCError(error)
log.error(`MetaMask - RPC Error: ${error.message}`, error)
+ done()
})
}
}
diff --git a/app/scripts/lib/diagnostics-reporter.js b/app/scripts/lib/diagnostics-reporter.js
new file mode 100644
index 000000000..aa4ca6e26
--- /dev/null
+++ b/app/scripts/lib/diagnostics-reporter.js
@@ -0,0 +1,71 @@
+class DiagnosticsReporter {
+
+ constructor ({ firstTimeInfo, version }) {
+ this.firstTimeInfo = firstTimeInfo
+ this.version = version
+ }
+
+ async reportOrphans(orphans) {
+ try {
+ return await this.submit({
+ accounts: Object.keys(orphans),
+ metadata: {
+ type: 'orphans',
+ },
+ })
+ } catch (err) {
+ console.error('DiagnosticsReporter - "reportOrphans" encountered an error:')
+ console.error(err)
+ }
+ }
+
+ async reportMultipleKeyrings(rawKeyrings) {
+ try {
+ const keyrings = await Promise.all(rawKeyrings.map(async (keyring, index) => {
+ return {
+ index,
+ type: keyring.type,
+ accounts: await keyring.getAccounts(),
+ }
+ }))
+ return await this.submit({
+ accounts: [],
+ metadata: {
+ type: 'keyrings',
+ keyrings,
+ },
+ })
+ } catch (err) {
+ console.error('DiagnosticsReporter - "reportMultipleKeyrings" encountered an error:')
+ console.error(err)
+ }
+ }
+
+ async submit (message) {
+ try {
+ // add metadata
+ message.metadata.version = this.version
+ message.metadata.firstTimeInfo = this.firstTimeInfo
+ return await postData(message)
+ } catch (err) {
+ console.error('DiagnosticsReporter - "submit" encountered an error:')
+ throw err
+ }
+ }
+
+}
+
+function postData(data) {
+ const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts'
+ return fetch(uri, {
+ body: JSON.stringify(data), // must match 'Content-Type' header
+ credentials: 'same-origin', // include, same-origin, *omit
+ headers: {
+ 'content-type': 'application/json',
+ },
+ method: 'POST', // *GET, POST, PUT, DELETE, etc.
+ mode: 'cors', // no-cors, cors, *same-origin
+ })
+}
+
+module.exports = DiagnosticsReporter
diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js
index d164827ab..77aefb00a 100644
--- a/app/scripts/lib/setupRaven.js
+++ b/app/scripts/lib/setupRaven.js
@@ -66,7 +66,7 @@ function simplifyErrorMessages(report) {
function rewriteErrorMessages(report, rewriteFn) {
// rewrite top level message
- report.message = rewriteFn(report.message)
+ if (report.message) report.message = rewriteFn(report.message)
// rewrite each exception message
if (report.exception && report.exception.values) {
report.exception.values.forEach(item => {
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index a570f2567..1bb0af5ee 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -46,6 +46,7 @@ const GWEI_BN = new BN('1000000000')
const percentile = require('percentile')
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
const cleanErrorStack = require('./lib/cleanErrorStack')
+const DiagnosticsReporter = require('./lib/diagnostics-reporter')
const log = require('loglevel')
module.exports = class MetamaskController extends EventEmitter {
@@ -64,6 +65,12 @@ module.exports = class MetamaskController extends EventEmitter {
const initState = opts.initState || {}
this.recordFirstTimeInfo(initState)
+ // metamask diagnostics reporter
+ this.diagnostics = opts.diagnostics || new DiagnosticsReporter({
+ firstTimeInfo: initState.firstTimeInfo,
+ version,
+ })
+
// platform-specific api
this.platform = opts.platform
@@ -85,6 +92,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.preferencesController = new PreferencesController({
initState: initState.PreferencesController,
initLangCode: opts.initLangCode,
+ diagnostics: this.diagnostics,
})
// currency controller
@@ -139,6 +147,8 @@ module.exports = class MetamaskController extends EventEmitter {
const address = addresses[0]
this.preferencesController.setSelectedAddress(address)
}
+ // ensure preferences + identities controller know about all addresses
+ this.preferencesController.addAddresses(addresses)
this.accountTracker.syncWithAddresses(addresses)
})
@@ -354,7 +364,7 @@ module.exports = class MetamaskController extends EventEmitter {
importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
// vault management
- submitPassword: nodeify(keyringController.submitPassword, keyringController),
+ submitPassword: nodeify(this.submitPassword, this),
// network management
setProviderType: nodeify(networkController.setProviderType, networkController),
@@ -456,7 +466,11 @@ module.exports = class MetamaskController extends EventEmitter {
async createNewVaultAndRestore (password, seed) {
const release = await this.createVaultMutex.acquire()
try {
+ // clear known identities
+ this.preferencesController.setAddresses([])
+ // create new vault
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
+ // set new identities
const accounts = await this.keyringController.getAccounts()
this.preferencesController.setAddresses(accounts)
this.selectFirstIdentity()
@@ -468,6 +482,28 @@ module.exports = class MetamaskController extends EventEmitter {
}
}
+ /*
+ * Submits the user's password and attempts to unlock the vault.
+ * Also synchronizes the preferencesController, to ensure its schema
+ * is up to date with known accounts once the vault is decrypted.
+ *
+ * @param {string} password - The user's password
+ * @returns {Promise<object>} - The keyringController update.
+ */
+ async submitPassword (password) {
+ await this.keyringController.submitPassword(password)
+ const accounts = await this.keyringController.getAccounts()
+
+ // verify keyrings
+ const nonSimpleKeyrings = this.keyringController.keyrings.filter(keyring => keyring.type !== 'Simple Key Pair')
+ if (nonSimpleKeyrings.length > 1 && this.diagnostics) {
+ await this.diagnostics.reportMultipleKeyrings(nonSimpleKeyrings)
+ }
+
+ await this.preferencesController.syncAddresses(accounts)
+ return this.keyringController.fullUpdate()
+ }
+
/**
* @type Identity
* @property {string} name - The account nickname.
diff --git a/app/scripts/migrations/026.js b/app/scripts/migrations/026.js
index 1b8a91a45..4e907e09c 100644
--- a/app/scripts/migrations/026.js
+++ b/app/scripts/migrations/026.js
@@ -27,7 +27,7 @@ module.exports = {
function transformState (state) {
if (!state.KeyringController || !state.PreferencesController) {
- return
+ return state
}
if (!state.KeyringController.walletNicknames) {