mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 09:23:21 +01:00
Merge branch 'develop' into i3725-refactor-send-component-
This commit is contained in:
commit
ae55e8a3c1
12
CHANGELOG.md
12
CHANGELOG.md
@ -2,7 +2,17 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
- Fixes issue where old nicknames were kept around causing errors.
|
## 4.7.4 Tue Jun 05 2018
|
||||||
|
|
||||||
|
- Add diagnostic reporting for users with multiple HD keyrings
|
||||||
|
- Throw explicit error when selected account is unset
|
||||||
|
|
||||||
|
## 4.7.3 Mon Jun 04 2018
|
||||||
|
|
||||||
|
- Hide token now uses new modal
|
||||||
|
- Indicate the current selected account on the popup account view
|
||||||
|
- Reduce height of notice container in onboarding
|
||||||
|
- Fixes issue where old nicknames were kept around causing errors
|
||||||
|
|
||||||
## 4.7.2 Sun Jun 03 2018
|
## 4.7.2 Sun Jun 03 2018
|
||||||
|
|
||||||
|
@ -62,6 +62,9 @@
|
|||||||
"message": " $1以上 $2以下にして下さい。",
|
"message": " $1以上 $2以下にして下さい。",
|
||||||
"description": "helper for inputting hex as decimal input"
|
"description": "helper for inputting hex as decimal input"
|
||||||
},
|
},
|
||||||
|
"blockiesIdenticon": {
|
||||||
|
"message": "Blockies Identicon を使用"
|
||||||
|
},
|
||||||
"borrowDharma": {
|
"borrowDharma": {
|
||||||
"message": "Dharmaで借りる(ベータ版)"
|
"message": "Dharmaで借りる(ベータ版)"
|
||||||
},
|
},
|
||||||
@ -95,6 +98,9 @@
|
|||||||
"confirmTransaction": {
|
"confirmTransaction": {
|
||||||
"message": "トランザクションの確認"
|
"message": "トランザクションの確認"
|
||||||
},
|
},
|
||||||
|
"continue": {
|
||||||
|
"message": "続行"
|
||||||
|
},
|
||||||
"continueToCoinbase": {
|
"continueToCoinbase": {
|
||||||
"message": "Coinbaseを開く"
|
"message": "Coinbaseを開く"
|
||||||
},
|
},
|
||||||
@ -359,6 +365,9 @@
|
|||||||
"likeToAddTokens": {
|
"likeToAddTokens": {
|
||||||
"message": "トークンを追加しますか?"
|
"message": "トークンを追加しますか?"
|
||||||
},
|
},
|
||||||
|
"links": {
|
||||||
|
"message": "リンク"
|
||||||
|
},
|
||||||
"limit": {
|
"limit": {
|
||||||
"message": "リミット"
|
"message": "リミット"
|
||||||
},
|
},
|
||||||
@ -371,12 +380,18 @@
|
|||||||
"localhost": {
|
"localhost": {
|
||||||
"message": "Localhost 8545"
|
"message": "Localhost 8545"
|
||||||
},
|
},
|
||||||
|
"login": {
|
||||||
|
"message": "ログイン"
|
||||||
|
},
|
||||||
"logout": {
|
"logout": {
|
||||||
"message": "ログアウト"
|
"message": "ログアウト"
|
||||||
},
|
},
|
||||||
"loose": {
|
"loose": {
|
||||||
"message": "外部秘密鍵"
|
"message": "外部秘密鍵"
|
||||||
},
|
},
|
||||||
|
"max": {
|
||||||
|
"message": "最大"
|
||||||
|
},
|
||||||
"mainnet": {
|
"mainnet": {
|
||||||
"message": "Ethereumメインネットワーク"
|
"message": "Ethereumメインネットワーク"
|
||||||
},
|
},
|
||||||
@ -417,7 +432,7 @@
|
|||||||
"message": "新規コントラクト"
|
"message": "新規コントラクト"
|
||||||
},
|
},
|
||||||
"newPassword": {
|
"newPassword": {
|
||||||
"message": "新規パスワード(最低8文字)"
|
"message": "新規パスワード(最低8文字)"
|
||||||
},
|
},
|
||||||
"newRecipient": {
|
"newRecipient": {
|
||||||
"message": "新規受取人"
|
"message": "新規受取人"
|
||||||
@ -453,6 +468,9 @@
|
|||||||
"message": "または",
|
"message": "または",
|
||||||
"description": "choice between creating or importing a new account"
|
"description": "choice between creating or importing a new account"
|
||||||
},
|
},
|
||||||
|
"password": {
|
||||||
|
"message": "パスワード"
|
||||||
|
},
|
||||||
"passwordMismatch": {
|
"passwordMismatch": {
|
||||||
"message": "パスワードが一致しません。",
|
"message": "パスワードが一致しません。",
|
||||||
"description": "in password creation process, the two new password fields did not match"
|
"description": "in password creation process, the two new password fields did not match"
|
||||||
@ -474,6 +492,9 @@
|
|||||||
"popularTokens": {
|
"popularTokens": {
|
||||||
"message": "人気のトークン"
|
"message": "人気のトークン"
|
||||||
},
|
},
|
||||||
|
"privacyMsg": {
|
||||||
|
"message": "プライバシーポリシー"
|
||||||
|
},
|
||||||
"privateKey": {
|
"privateKey": {
|
||||||
"message": "秘密鍵",
|
"message": "秘密鍵",
|
||||||
"description": "select this type of file to use to import an account"
|
"description": "select this type of file to use to import an account"
|
||||||
@ -546,6 +567,12 @@
|
|||||||
"message": "ファイルとして保存",
|
"message": "ファイルとして保存",
|
||||||
"description": "Account export process"
|
"description": "Account export process"
|
||||||
},
|
},
|
||||||
|
"search": {
|
||||||
|
"message": "検索"
|
||||||
|
},
|
||||||
|
"searchResults": {
|
||||||
|
"message": "検索結果"
|
||||||
|
},
|
||||||
"selectService": {
|
"selectService": {
|
||||||
"message": "サービスを選択"
|
"message": "サービスを選択"
|
||||||
},
|
},
|
||||||
@ -575,7 +602,7 @@
|
|||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"message": "情報"
|
"message": "情報"
|
||||||
},
|
},
|
||||||
"shapeshiftBuy": {
|
"shapeshiftBuy": {
|
||||||
"message": "Shapeshiftで交換"
|
"message": "Shapeshiftで交換"
|
||||||
},
|
},
|
||||||
@ -609,6 +636,9 @@
|
|||||||
"takesTooLong": {
|
"takesTooLong": {
|
||||||
"message": "送信に時間がかかりますか?"
|
"message": "送信に時間がかかりますか?"
|
||||||
},
|
},
|
||||||
|
"terms": {
|
||||||
|
"message": "利用規約"
|
||||||
|
},
|
||||||
"testFaucet": {
|
"testFaucet": {
|
||||||
"message": "Faucetをテスト"
|
"message": "Faucetをテスト"
|
||||||
},
|
},
|
||||||
@ -619,6 +649,9 @@
|
|||||||
"message": "ShapeShiftで $1をETHにする",
|
"message": "ShapeShiftで $1をETHにする",
|
||||||
"description": "system will fill in deposit type in start of message"
|
"description": "system will fill in deposit type in start of message"
|
||||||
},
|
},
|
||||||
|
"token": {
|
||||||
|
"message": "トークン"
|
||||||
|
},
|
||||||
"tokenAddress": {
|
"tokenAddress": {
|
||||||
"message": "トークンアドレス"
|
"message": "トークンアドレス"
|
||||||
},
|
},
|
||||||
@ -690,6 +723,12 @@
|
|||||||
"warning": {
|
"warning": {
|
||||||
"message": "警告"
|
"message": "警告"
|
||||||
},
|
},
|
||||||
|
"welcomeBack": {
|
||||||
|
"message": "おかえりなさい!"
|
||||||
|
},
|
||||||
|
"welcomeBeta": {
|
||||||
|
"message": "MetaMask ベータ版へようこそ!"
|
||||||
|
},
|
||||||
"whatsThis": {
|
"whatsThis": {
|
||||||
"message": "この機能について"
|
"message": "この機能について"
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_appName__",
|
"name": "__MSG_appName__",
|
||||||
"short_name": "__MSG_appName__",
|
"short_name": "__MSG_appName__",
|
||||||
"version": "4.7.2",
|
"version": "4.7.4",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"author": "https://metamask.io",
|
"author": "https://metamask.io",
|
||||||
"description": "__MSG_appDescription__",
|
"description": "__MSG_appDescription__",
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const normalizeAddress = require('eth-sig-util').normalize
|
const normalizeAddress = require('eth-sig-util').normalize
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const notifier = require('../lib/bug-notifier')
|
|
||||||
const log = require('loglevel')
|
|
||||||
const { version } = require('../../manifest.json')
|
|
||||||
|
|
||||||
class PreferencesController {
|
class PreferencesController {
|
||||||
|
|
||||||
@ -34,8 +32,7 @@ class PreferencesController {
|
|||||||
lostIdentities: {},
|
lostIdentities: {},
|
||||||
}, opts.initState)
|
}, opts.initState)
|
||||||
|
|
||||||
this.getFirstTimeInfo = opts.getFirstTimeInfo || null
|
this.diagnostics = opts.diagnostics
|
||||||
this.notifier = opts.notifier || notifier
|
|
||||||
|
|
||||||
this.store = new ObservableStore(initState)
|
this.store = new ObservableStore(initState)
|
||||||
}
|
}
|
||||||
@ -128,17 +125,9 @@ class PreferencesController {
|
|||||||
if (Object.keys(newlyLost).length > 0) {
|
if (Object.keys(newlyLost).length > 0) {
|
||||||
|
|
||||||
// Notify our servers:
|
// Notify our servers:
|
||||||
const uri = 'https://diagnostics.metamask.io/v1/orphanedAccounts'
|
if (this.diagnostics) this.diagnostics.reportOrphans(newlyLost)
|
||||||
const firstTimeInfo = this.getFirstTimeInfo ? this.getFirstTimeInfo() : {}
|
|
||||||
this.notifier.notify(uri, {
|
|
||||||
accounts: Object.keys(newlyLost),
|
|
||||||
metadata: {
|
|
||||||
version,
|
|
||||||
firstTimeInfo,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.catch(log.error)
|
|
||||||
|
|
||||||
|
// store lost accounts
|
||||||
for (let key in newlyLost) {
|
for (let key in newlyLost) {
|
||||||
lostIdentities[key] = newlyLost[key]
|
lostIdentities[key] = newlyLost[key]
|
||||||
}
|
}
|
||||||
@ -258,6 +247,7 @@ class PreferencesController {
|
|||||||
* @return {Promise<string>}
|
* @return {Promise<string>}
|
||||||
*/
|
*/
|
||||||
setAccountLabel (account, label) {
|
setAccountLabel (account, label) {
|
||||||
|
if (!account) throw new Error('setAccountLabel requires a valid address, got ' + String(account))
|
||||||
const address = normalizeAddress(account)
|
const address = normalizeAddress(account)
|
||||||
const {identities} = this.store.getState()
|
const {identities} = this.store.getState()
|
||||||
identities[address] = identities[address] || {}
|
identities[address] = identities[address] || {}
|
||||||
|
@ -10,6 +10,7 @@ const NonceTracker = require('./nonce-tracker')
|
|||||||
const txUtils = require('./lib/util')
|
const txUtils = require('./lib/util')
|
||||||
const cleanErrorStack = require('../../lib/cleanErrorStack')
|
const cleanErrorStack = require('../../lib/cleanErrorStack')
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
|
const recipientBlacklistChecker = require('./lib/recipient-blacklist-checker')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Transaction Controller is an aggregate of sub-controllers and trackers
|
Transaction Controller is an aggregate of sub-controllers and trackers
|
||||||
@ -157,8 +158,11 @@ class TransactionController extends EventEmitter {
|
|||||||
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
|
let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams })
|
||||||
this.addTx(txMeta)
|
this.addTx(txMeta)
|
||||||
this.emit('newUnapprovedTx', txMeta)
|
this.emit('newUnapprovedTx', txMeta)
|
||||||
// add default tx params
|
|
||||||
try {
|
try {
|
||||||
|
// check whether recipient account is blacklisted
|
||||||
|
recipientBlacklistChecker.checkAccount(txMeta.metamaskNetworkId, normalizedTxParams.to)
|
||||||
|
// add default tx params
|
||||||
txMeta = await this.addTxGasDefaults(txMeta)
|
txMeta = await this.addTxGasDefaults(txMeta)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
const Config = require('./recipient-blacklist-config.json')
|
||||||
|
|
||||||
|
/** @module*/
|
||||||
|
module.exports = {
|
||||||
|
checkAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a specified account on a specified network is blacklisted.
|
||||||
|
@param networkId {number}
|
||||||
|
@param account {string}
|
||||||
|
*/
|
||||||
|
function checkAccount (networkId, account) {
|
||||||
|
|
||||||
|
const mainnetId = 1
|
||||||
|
if (networkId !== mainnetId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountToCheck = account.toLowerCase()
|
||||||
|
if (Config.blacklist.includes(accountToCheck)) {
|
||||||
|
throw new Error('Recipient is a public account')
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"blacklist": [
|
||||||
|
"0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||||
|
"0xf17f52151ebef6c7334fad080c5704d77216b732",
|
||||||
|
"0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef",
|
||||||
|
"0x821aea9a577a9b44299b9c15c88cf3087f3b5544",
|
||||||
|
"0x0d1d4e623d10f9fba5db95830f7d3839406c6af2",
|
||||||
|
"0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e",
|
||||||
|
"0x2191ef87e392377ec08e7c08eb105ef5448eced5",
|
||||||
|
"0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5",
|
||||||
|
"0x6330a553fc93768f612722bb8c2ec78ac90b3bbc",
|
||||||
|
"0x5aeda56215b167893e80b4fe645ba6d5bab767de"
|
||||||
|
]
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
class BugNotifier {
|
|
||||||
notify (uri, message) {
|
|
||||||
return postData(uri, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function postData(uri, data) {
|
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const notifier = new BugNotifier()
|
|
||||||
|
|
||||||
module.exports = notifier
|
|
||||||
|
|
71
app/scripts/lib/diagnostics-reporter.js
Normal file
71
app/scripts/lib/diagnostics-reporter.js
Normal file
@ -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
|
@ -46,6 +46,7 @@ const GWEI_BN = new BN('1000000000')
|
|||||||
const percentile = require('percentile')
|
const percentile = require('percentile')
|
||||||
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
|
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
|
||||||
const cleanErrorStack = require('./lib/cleanErrorStack')
|
const cleanErrorStack = require('./lib/cleanErrorStack')
|
||||||
|
const DiagnosticsReporter = require('./lib/diagnostics-reporter')
|
||||||
const log = require('loglevel')
|
const log = require('loglevel')
|
||||||
|
|
||||||
module.exports = class MetamaskController extends EventEmitter {
|
module.exports = class MetamaskController extends EventEmitter {
|
||||||
@ -64,6 +65,12 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
const initState = opts.initState || {}
|
const initState = opts.initState || {}
|
||||||
this.recordFirstTimeInfo(initState)
|
this.recordFirstTimeInfo(initState)
|
||||||
|
|
||||||
|
// metamask diagnostics reporter
|
||||||
|
this.diagnostics = opts.diagnostics || new DiagnosticsReporter({
|
||||||
|
firstTimeInfo: initState.firstTimeInfo,
|
||||||
|
version,
|
||||||
|
})
|
||||||
|
|
||||||
// platform-specific api
|
// platform-specific api
|
||||||
this.platform = opts.platform
|
this.platform = opts.platform
|
||||||
|
|
||||||
@ -85,7 +92,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.preferencesController = new PreferencesController({
|
this.preferencesController = new PreferencesController({
|
||||||
initState: initState.PreferencesController,
|
initState: initState.PreferencesController,
|
||||||
initLangCode: opts.initLangCode,
|
initLangCode: opts.initLangCode,
|
||||||
getFirstTimeInfo: () => initState.firstTimeInfo,
|
diagnostics: this.diagnostics,
|
||||||
})
|
})
|
||||||
|
|
||||||
// currency controller
|
// currency controller
|
||||||
@ -489,6 +496,12 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
await this.keyringController.submitPassword(password)
|
await this.keyringController.submitPassword(password)
|
||||||
const accounts = await this.keyringController.getAccounts()
|
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)
|
await this.preferencesController.syncAddresses(accounts)
|
||||||
return this.keyringController.fullUpdate()
|
return this.keyringController.fullUpdate()
|
||||||
}
|
}
|
||||||
|
@ -75,9 +75,9 @@
|
|||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -115,9 +115,9 @@
|
|||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -76,9 +76,9 @@
|
|||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -94,9 +94,9 @@
|
|||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -76,9 +76,9 @@
|
|||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -83,9 +83,9 @@
|
|||||||
{
|
{
|
||||||
"type": "HD Key Tree",
|
"type": "HD Key Tree",
|
||||||
"accounts": [
|
"accounts": [
|
||||||
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -23,9 +23,10 @@ class AccountDropdowns extends Component {
|
|||||||
|
|
||||||
renderAccounts () {
|
renderAccounts () {
|
||||||
const { identities, selected, keyrings } = this.props
|
const { identities, selected, keyrings } = this.props
|
||||||
|
const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
|
||||||
|
|
||||||
return Object.keys(identities).map((key, index) => {
|
return accountOrder.map((address, index) => {
|
||||||
const identity = identities[key]
|
const identity = identities[address]
|
||||||
const isSelected = identity.address === selected
|
const isSelected = identity.address === selected
|
||||||
|
|
||||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||||
|
@ -53,6 +53,9 @@ describe('MetaMaskController', function () {
|
|||||||
},
|
},
|
||||||
initState: clone(firstTimeState),
|
initState: clone(firstTimeState),
|
||||||
})
|
})
|
||||||
|
// disable diagnostics
|
||||||
|
metamaskController.diagnostics = null
|
||||||
|
// add sinon method spies
|
||||||
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
||||||
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
|
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
|
||||||
})
|
})
|
||||||
@ -72,11 +75,6 @@ describe('MetaMaskController', function () {
|
|||||||
it('removes any identities that do not correspond to known accounts.', async function () {
|
it('removes any identities that do not correspond to known accounts.', async function () {
|
||||||
const fakeAddress = '0xbad0'
|
const fakeAddress = '0xbad0'
|
||||||
metamaskController.preferencesController.addAddresses([fakeAddress])
|
metamaskController.preferencesController.addAddresses([fakeAddress])
|
||||||
metamaskController.preferencesController.notifier = {
|
|
||||||
notify: async () => {
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await metamaskController.submitPassword(password)
|
await metamaskController.submitPassword(password)
|
||||||
|
|
||||||
const identities = Object.keys(metamaskController.preferencesController.store.getState().identities)
|
const identities = Object.keys(metamaskController.preferencesController.store.getState().identities)
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
const assert = require('assert')
|
||||||
|
const recipientBlackListChecker = require('../../../../../app/scripts/controllers/transactions/lib/recipient-blacklist-checker')
|
||||||
|
const {
|
||||||
|
ROPSTEN_CODE,
|
||||||
|
RINKEYBY_CODE,
|
||||||
|
KOVAN_CODE,
|
||||||
|
} = require('../../../../../app/scripts/controllers/network/enums')
|
||||||
|
|
||||||
|
const KeyringController = require('eth-keyring-controller')
|
||||||
|
|
||||||
|
describe('Recipient Blacklist Checker', function () {
|
||||||
|
|
||||||
|
let publicAccounts
|
||||||
|
|
||||||
|
before(async function () {
|
||||||
|
const damnedMnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'
|
||||||
|
const keyringController = new KeyringController({})
|
||||||
|
const Keyring = keyringController.getKeyringClassForType('HD Key Tree')
|
||||||
|
const opts = {
|
||||||
|
mnemonic: damnedMnemonic,
|
||||||
|
numberOfAccounts: 10,
|
||||||
|
}
|
||||||
|
const keyring = new Keyring(opts)
|
||||||
|
publicAccounts = await keyring.getAccounts()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#checkAccount', function () {
|
||||||
|
it('does not fail on test networks', function () {
|
||||||
|
let callCount = 0
|
||||||
|
const networks = [ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE]
|
||||||
|
for (let networkId in networks) {
|
||||||
|
publicAccounts.forEach((account) => {
|
||||||
|
recipientBlackListChecker.checkAccount(networkId, account)
|
||||||
|
callCount++
|
||||||
|
})
|
||||||
|
}
|
||||||
|
assert.equal(callCount, 30)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails on mainnet', function () {
|
||||||
|
const mainnetId = 1
|
||||||
|
let callCount = 0
|
||||||
|
publicAccounts.forEach((account) => {
|
||||||
|
try {
|
||||||
|
recipientBlackListChecker.checkAccount(mainnetId, account)
|
||||||
|
assert.fail('function should have thrown an error')
|
||||||
|
} catch (err) {
|
||||||
|
assert.equal(err.message, 'Recipient is a public account')
|
||||||
|
}
|
||||||
|
callCount++
|
||||||
|
})
|
||||||
|
assert.equal(callCount, 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails for public account - uppercase', function () {
|
||||||
|
const mainnetId = 1
|
||||||
|
const publicAccount = '0X0D1D4E623D10F9FBA5DB95830F7D3839406C6AF2'
|
||||||
|
try {
|
||||||
|
recipientBlackListChecker.checkAccount(mainnetId, publicAccount)
|
||||||
|
assert.fail('function should have thrown an error')
|
||||||
|
} catch (err) {
|
||||||
|
assert.equal(err.message, 'Recipient is a public account')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('fails for public account - lowercase', async function () {
|
||||||
|
const mainnetId = 1
|
||||||
|
const publicAccount = '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2'
|
||||||
|
try {
|
||||||
|
await recipientBlackListChecker.checkAccount(mainnetId, publicAccount)
|
||||||
|
assert.fail('function should have thrown an error')
|
||||||
|
} catch (err) {
|
||||||
|
assert.equal(err.message, 'Recipient is a public account')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -185,6 +185,23 @@ describe('Transaction Controller', function () {
|
|||||||
.catch(done)
|
.catch(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should fail if recipient is public', function (done) {
|
||||||
|
txController.networkStore = new ObservableStore(1)
|
||||||
|
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.message === 'Recipient is a public account') done()
|
||||||
|
else done(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not fail if recipient is public but not on mainnet', function (done) {
|
||||||
|
txController.once('newUnapprovedTx', (txMetaFromEmit) => {
|
||||||
|
assert(txMetaFromEmit, 'txMeta is falsey')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
txController.addUnapprovedTransaction({ from: '0x1678a085c290ebd122dc42cba69373b5953b831d', to: '0x0d1d4e623D10F9FBA5Db95830F7d3839406C6AF2' })
|
||||||
|
.catch(done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#addTxGasDefaults', function () {
|
describe('#addTxGasDefaults', function () {
|
||||||
|
@ -558,10 +558,12 @@ function importNewAccount (strategy, args) {
|
|||||||
}
|
}
|
||||||
dispatch(actions.hideLoadingIndication())
|
dispatch(actions.hideLoadingIndication())
|
||||||
dispatch(actions.updateMetamaskState(newState))
|
dispatch(actions.updateMetamaskState(newState))
|
||||||
dispatch({
|
if (newState.selectedAddress) {
|
||||||
type: actions.SHOW_ACCOUNT_DETAIL,
|
dispatch({
|
||||||
value: newState.selectedAddress,
|
type: actions.SHOW_ACCOUNT_DETAIL,
|
||||||
})
|
value: newState.selectedAddress,
|
||||||
|
})
|
||||||
|
}
|
||||||
return newState
|
return newState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ class App extends Component {
|
|||||||
} = this.props
|
} = this.props
|
||||||
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
|
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
|
||||||
const loadMessage = loadingMessage || isLoadingNetwork ?
|
const loadMessage = loadingMessage || isLoadingNetwork ?
|
||||||
this.getConnectingLabel() : null
|
this.getConnectingLabel(loadingMessage) : null
|
||||||
log.debug('Main ui render function')
|
log.debug('Main ui render function')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -210,7 +210,10 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getConnectingLabel = function () {
|
getConnectingLabel = function (loadingMessage) {
|
||||||
|
if (loadingMessage) {
|
||||||
|
return loadingMessage
|
||||||
|
}
|
||||||
const { provider } = this.props
|
const { provider } = this.props
|
||||||
const providerName = provider.type
|
const providerName = provider.type
|
||||||
|
|
||||||
|
@ -135,11 +135,12 @@ AccountMenu.prototype.renderAccounts = function () {
|
|||||||
showAccountDetail,
|
showAccountDetail,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return Object.keys(identities).map((key, index) => {
|
const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
|
||||||
const identity = identities[key]
|
return accountOrder.map((address) => {
|
||||||
|
const identity = identities[address]
|
||||||
const isSelected = identity.address === selectedAddress
|
const isSelected = identity.address === selectedAddress
|
||||||
|
|
||||||
const balanceValue = accounts[key] ? accounts[key].balance : ''
|
const balanceValue = accounts[address] ? accounts[address].balance : ''
|
||||||
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
|
const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...'
|
||||||
const simpleAddress = identity.address.substring(2).toLowerCase()
|
const simpleAddress = identity.address.substring(2).toLowerCase()
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ class AddToken extends Component {
|
|||||||
<div className="add-token__custom-token-form">
|
<div className="add-token__custom-token-form">
|
||||||
<TextField
|
<TextField
|
||||||
id="custom-address"
|
id="custom-address"
|
||||||
label="Token Address"
|
label={this.context.t('tokenAddress')}
|
||||||
type="text"
|
type="text"
|
||||||
value={customAddress}
|
value={customAddress}
|
||||||
onChange={e => this.handleCustomAddressChange(e.target.value)}
|
onChange={e => this.handleCustomAddressChange(e.target.value)}
|
||||||
@ -241,7 +241,7 @@ class AddToken extends Component {
|
|||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
id="custom-symbol"
|
id="custom-symbol"
|
||||||
label="Token Symbol"
|
label={this.context.t('tokenSymbol')}
|
||||||
type="text"
|
type="text"
|
||||||
value={customSymbol}
|
value={customSymbol}
|
||||||
onChange={e => this.handleCustomSymbolChange(e.target.value)}
|
onChange={e => this.handleCustomSymbolChange(e.target.value)}
|
||||||
@ -252,7 +252,7 @@ class AddToken extends Component {
|
|||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
id="custom-decimals"
|
id="custom-decimals"
|
||||||
label="Decimals of Precision"
|
label={this.context.t('decimal')}
|
||||||
type="number"
|
type="number"
|
||||||
value={customDecimals}
|
value={customDecimals}
|
||||||
onChange={e => this.handleCustomDecimalsChange(e.target.value)}
|
onChange={e => this.handleCustomDecimalsChange(e.target.value)}
|
||||||
|
@ -82,18 +82,19 @@ class JsonImportSubview extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createNewKeychain () {
|
createNewKeychain () {
|
||||||
|
const { firstAddress, displayWarning, importNewJsonAccount, setSelectedAddress } = this.props
|
||||||
const state = this.state
|
const state = this.state
|
||||||
|
|
||||||
if (!state) {
|
if (!state) {
|
||||||
const message = this.context.t('validFileImport')
|
const message = this.context.t('validFileImport')
|
||||||
return this.props.displayWarning(message)
|
return displayWarning(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { fileContents } = state
|
const { fileContents } = state
|
||||||
|
|
||||||
if (!fileContents) {
|
if (!fileContents) {
|
||||||
const message = this.context.t('needImportFile')
|
const message = this.context.t('needImportFile')
|
||||||
return this.props.displayWarning(message)
|
return displayWarning(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
const passwordInput = document.getElementById('json-password-box')
|
const passwordInput = document.getElementById('json-password-box')
|
||||||
@ -101,12 +102,19 @@ class JsonImportSubview extends Component {
|
|||||||
|
|
||||||
if (!password) {
|
if (!password) {
|
||||||
const message = this.context.t('needImportPassword')
|
const message = this.context.t('needImportPassword')
|
||||||
return this.props.displayWarning(message)
|
return displayWarning(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.importNewJsonAccount([ fileContents, password ])
|
importNewJsonAccount([ fileContents, password ])
|
||||||
// JS runtime requires caught rejections but failures are handled by Redux
|
.then(({ selectedAddress }) => {
|
||||||
.catch()
|
if (selectedAddress) {
|
||||||
|
history.push(DEFAULT_ROUTE)
|
||||||
|
} else {
|
||||||
|
displayWarning('Error importing account.')
|
||||||
|
setSelectedAddress(firstAddress)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => displayWarning(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,14 +122,17 @@ JsonImportSubview.propTypes = {
|
|||||||
error: PropTypes.string,
|
error: PropTypes.string,
|
||||||
goHome: PropTypes.func,
|
goHome: PropTypes.func,
|
||||||
displayWarning: PropTypes.func,
|
displayWarning: PropTypes.func,
|
||||||
|
firstAddress: PropTypes.string,
|
||||||
importNewJsonAccount: PropTypes.func,
|
importNewJsonAccount: PropTypes.func,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
|
setSelectedAddress: PropTypes.func,
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
error: state.appState.warning,
|
error: state.appState.warning,
|
||||||
|
firstAddress: Object.keys(state.metamask.accounts)[0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,6 +141,7 @@ const mapDispatchToProps = dispatch => {
|
|||||||
goHome: () => dispatch(actions.goHome()),
|
goHome: () => dispatch(actions.goHome()),
|
||||||
displayWarning: warning => dispatch(actions.displayWarning(warning)),
|
displayWarning: warning => dispatch(actions.displayWarning(warning)),
|
||||||
importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)),
|
importNewJsonAccount: options => dispatch(actions.importNewAccount('JSON File', options)),
|
||||||
|
setSelectedAddress: (address) => dispatch(actions.setSelectedAddress(address)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ module.exports = compose(
|
|||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
return {
|
return {
|
||||||
error: state.appState.warning,
|
error: state.appState.warning,
|
||||||
|
firstAddress: Object.keys(state.metamask.accounts)[0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +30,8 @@ function mapDispatchToProps (dispatch) {
|
|||||||
importNewAccount: (strategy, [ privateKey ]) => {
|
importNewAccount: (strategy, [ privateKey ]) => {
|
||||||
return dispatch(actions.importNewAccount(strategy, [ privateKey ]))
|
return dispatch(actions.importNewAccount(strategy, [ privateKey ]))
|
||||||
},
|
},
|
||||||
displayWarning: () => dispatch(actions.displayWarning(null)),
|
displayWarning: (message) => dispatch(actions.displayWarning(message || null)),
|
||||||
|
setSelectedAddress: (address) => dispatch(actions.setSelectedAddress(address)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ function PrivateKeyImportView () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrivateKeyImportView.prototype.render = function () {
|
PrivateKeyImportView.prototype.render = function () {
|
||||||
const { error } = this.props
|
const { error, displayWarning } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
h('div.new-account-import-form__private-key', [
|
h('div.new-account-import-form__private-key', [
|
||||||
@ -60,7 +62,10 @@ PrivateKeyImportView.prototype.render = function () {
|
|||||||
h('div.new-account-import-form__buttons', {}, [
|
h('div.new-account-import-form__buttons', {}, [
|
||||||
|
|
||||||
h('button.btn-default.btn--large.new-account-create-form__button', {
|
h('button.btn-default.btn--large.new-account-create-form__button', {
|
||||||
onClick: () => this.props.history.push(DEFAULT_ROUTE),
|
onClick: () => {
|
||||||
|
displayWarning(null)
|
||||||
|
this.props.history.push(DEFAULT_ROUTE)
|
||||||
|
},
|
||||||
}, [
|
}, [
|
||||||
this.context.t('cancel'),
|
this.context.t('cancel'),
|
||||||
]),
|
]),
|
||||||
@ -88,10 +93,16 @@ PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
|
|||||||
PrivateKeyImportView.prototype.createNewKeychain = function () {
|
PrivateKeyImportView.prototype.createNewKeychain = function () {
|
||||||
const input = document.getElementById('private-key-box')
|
const input = document.getElementById('private-key-box')
|
||||||
const privateKey = input.value
|
const privateKey = input.value
|
||||||
const { importNewAccount, history } = this.props
|
const { importNewAccount, history, displayWarning, setSelectedAddress, firstAddress } = this.props
|
||||||
|
|
||||||
importNewAccount('Private Key', [ privateKey ])
|
importNewAccount('Private Key', [ privateKey ])
|
||||||
// JS runtime requires caught rejections but failures are handled by Redux
|
.then(({ selectedAddress }) => {
|
||||||
.catch()
|
if (selectedAddress) {
|
||||||
.then(() => history.push(DEFAULT_ROUTE))
|
history.push(DEFAULT_ROUTE)
|
||||||
|
} else {
|
||||||
|
displayWarning('Error importing account.')
|
||||||
|
setSelectedAddress(firstAddress)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => displayWarning(err))
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,9 @@ class CreateAccountPage extends Component {
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
onClick: () => history.push(NEW_ACCOUNT_ROUTE),
|
onClick: () => history.push(NEW_ACCOUNT_ROUTE),
|
||||||
}, 'Create'),
|
}, [
|
||||||
|
this.context.t('create'),
|
||||||
|
]),
|
||||||
|
|
||||||
h('div.new-account__tabs__tab', {
|
h('div.new-account__tabs__tab', {
|
||||||
className: classnames('new-account__tabs__tab', {
|
className: classnames('new-account__tabs__tab', {
|
||||||
@ -31,14 +33,16 @@ class CreateAccountPage extends Component {
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
onClick: () => history.push(IMPORT_ACCOUNT_ROUTE),
|
onClick: () => history.push(IMPORT_ACCOUNT_ROUTE),
|
||||||
}, 'Import'),
|
}, [
|
||||||
|
this.context.t('import'),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
return h('div.new-account', {}, [
|
return h('div.new-account', {}, [
|
||||||
h('div.new-account__header', [
|
h('div.new-account__header', [
|
||||||
h('div.new-account__title', 'New Account'),
|
h('div.new-account__title', this.context.t('newAccount') ),
|
||||||
this.renderTabs(),
|
this.renderTabs(),
|
||||||
]),
|
]),
|
||||||
h('div.new-account__form', [
|
h('div.new-account__form', [
|
||||||
@ -62,6 +66,11 @@ class CreateAccountPage extends Component {
|
|||||||
CreateAccountPage.propTypes = {
|
CreateAccountPage.propTypes = {
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateAccountPage.contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
@ -14,8 +14,8 @@ class Config extends Component {
|
|||||||
return h('div.settings__tabs', [
|
return h('div.settings__tabs', [
|
||||||
h(TabBar, {
|
h(TabBar, {
|
||||||
tabs: [
|
tabs: [
|
||||||
{ content: 'Settings', key: SETTINGS_ROUTE },
|
{ content: this.context.t('settings'), key: SETTINGS_ROUTE },
|
||||||
{ content: 'Info', key: INFO_ROUTE },
|
{ content: this.context.t('info'), key: INFO_ROUTE },
|
||||||
],
|
],
|
||||||
isActive: key => matchPath(location.pathname, { path: key, exact: true }),
|
isActive: key => matchPath(location.pathname, { path: key, exact: true }),
|
||||||
onSelect: key => history.push(key),
|
onSelect: key => history.push(key),
|
||||||
@ -54,6 +54,11 @@ class Config extends Component {
|
|||||||
Config.propTypes = {
|
Config.propTypes = {
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Config
|
module.exports = Config
|
||||||
|
@ -120,7 +120,7 @@ class UnlockPage extends Component {
|
|||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
id="password"
|
id="password"
|
||||||
label="Password"
|
label={this.context.t('password')}
|
||||||
type="password"
|
type="password"
|
||||||
value={this.state.password}
|
value={this.state.password}
|
||||||
onChange={event => this.handleInputChange(event)}
|
onChange={event => this.handleInputChange(event)}
|
||||||
|
@ -36,7 +36,6 @@ function mapStateToProps (state) {
|
|||||||
tokens: state.metamask.tokens,
|
tokens: state.metamask.tokens,
|
||||||
keyrings: state.metamask.keyrings,
|
keyrings: state.metamask.keyrings,
|
||||||
selectedAddress: selectors.getSelectedAddress(state),
|
selectedAddress: selectors.getSelectedAddress(state),
|
||||||
selectedIdentity: selectors.getSelectedIdentity(state),
|
|
||||||
selectedAccount: selectors.getSelectedAccount(state),
|
selectedAccount: selectors.getSelectedAccount(state),
|
||||||
selectedTokenAddress: state.metamask.selectedTokenAddress,
|
selectedTokenAddress: state.metamask.selectedTokenAddress,
|
||||||
}
|
}
|
||||||
@ -99,21 +98,24 @@ WalletView.prototype.render = function () {
|
|||||||
const {
|
const {
|
||||||
responsiveDisplayClassname,
|
responsiveDisplayClassname,
|
||||||
selectedAddress,
|
selectedAddress,
|
||||||
selectedIdentity,
|
|
||||||
keyrings,
|
keyrings,
|
||||||
showAccountDetailModal,
|
showAccountDetailModal,
|
||||||
sidebarOpen,
|
sidebarOpen,
|
||||||
hideSidebar,
|
hideSidebar,
|
||||||
history,
|
history,
|
||||||
|
identities,
|
||||||
} = this.props
|
} = this.props
|
||||||
// temporary logs + fake extra wallets
|
// temporary logs + fake extra wallets
|
||||||
// console.log('walletview, selectedAccount:', selectedAccount)
|
// console.log('walletview, selectedAccount:', selectedAccount)
|
||||||
|
|
||||||
const checksummedAddress = checksumAddress(selectedAddress)
|
const checksummedAddress = checksumAddress(selectedAddress)
|
||||||
|
|
||||||
|
if (!selectedAddress) {
|
||||||
|
throw new Error('selectedAddress should not be ' + String(selectedAddress))
|
||||||
|
}
|
||||||
|
|
||||||
const keyring = keyrings.find((kr) => {
|
const keyring = keyrings.find((kr) => {
|
||||||
return kr.accounts.includes(selectedAddress) ||
|
return kr.accounts.includes(selectedAddress)
|
||||||
kr.accounts.includes(selectedIdentity.address)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const type = keyring.type
|
const type = keyring.type
|
||||||
@ -145,7 +147,7 @@ WalletView.prototype.render = function () {
|
|||||||
h('span.account-name', {
|
h('span.account-name', {
|
||||||
style: {},
|
style: {},
|
||||||
}, [
|
}, [
|
||||||
selectedIdentity.name,
|
identities[selectedAddress].name,
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('button.btn-clear.wallet-view__details-button.allcaps', this.context.t('details')),
|
h('button.btn-clear.wallet-view__details-button.allcaps', this.context.t('details')),
|
||||||
|
@ -4,7 +4,6 @@ const copyToClipboard = require('copy-to-clipboard')
|
|||||||
//
|
//
|
||||||
// Sub-Reducers take in the complete state and return their sub-state
|
// Sub-Reducers take in the complete state and return their sub-state
|
||||||
//
|
//
|
||||||
const reduceIdentities = require('./reducers/identities')
|
|
||||||
const reduceMetamask = require('./reducers/metamask')
|
const reduceMetamask = require('./reducers/metamask')
|
||||||
const reduceApp = require('./reducers/app')
|
const reduceApp = require('./reducers/app')
|
||||||
const reduceLocale = require('./reducers/locale')
|
const reduceLocale = require('./reducers/locale')
|
||||||
@ -22,12 +21,6 @@ function rootReducer (state, action) {
|
|||||||
return action.value
|
return action.value
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Identities
|
|
||||||
//
|
|
||||||
|
|
||||||
state.identities = reduceIdentities(state, action)
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// MetaMask
|
// MetaMask
|
||||||
//
|
//
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
const extend = require('xtend')
|
|
||||||
|
|
||||||
module.exports = reduceIdentities
|
|
||||||
|
|
||||||
function reduceIdentities (state, action) {
|
|
||||||
// clone + defaults
|
|
||||||
var idState = extend({
|
|
||||||
|
|
||||||
}, state.identities)
|
|
||||||
|
|
||||||
switch (action.type) {
|
|
||||||
default:
|
|
||||||
return idState
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,6 +14,11 @@ class WelcomeScreen extends Component {
|
|||||||
closeWelcomeScreen: PropTypes.func.isRequired,
|
closeWelcomeScreen: PropTypes.func.isRequired,
|
||||||
welcomeScreenSeen: PropTypes.bool,
|
welcomeScreenSeen: PropTypes.bool,
|
||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
|
t: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
t: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@ -45,16 +50,15 @@ class WelcomeScreen extends Component {
|
|||||||
height: '225',
|
height: '225',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('div.welcome-screen__info__header', 'Welcome to MetaMask Beta'),
|
h('div.welcome-screen__info__header', this.context.t('welcomeBeta')),
|
||||||
|
|
||||||
h('div.welcome-screen__info__copy', 'MetaMask is a secure identity vault for Ethereum.'),
|
h('div.welcome-screen__info__copy', this.context.t('metamaskDescription')),
|
||||||
|
|
||||||
h('div.welcome-screen__info__copy', `It allows you to hold ether & tokens,
|
h('div.welcome-screen__info__copy', this.context.t('holdEther')),
|
||||||
and serves as your bridge to decentralized applications.`),
|
|
||||||
|
|
||||||
h('button.welcome-screen__button', {
|
h('button.welcome-screen__button', {
|
||||||
onClick: this.initiateAccountCreation,
|
onClick: this.initiateAccountCreation,
|
||||||
}, 'Continue'),
|
}, this.context.t('continue')),
|
||||||
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user