mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-23 02:10:12 +01:00
Merge branch 'dev' into i831-AddRopsten-Dev
This commit is contained in:
commit
7ab9d40820
@ -1,2 +1 @@
|
||||
app/scripts/lib/extension-instance.js
|
||||
ui/app/conversion-util.js
|
||||
|
@ -127,9 +127,9 @@
|
||||
"no-whitespace-before-property": 2,
|
||||
"no-with": 2,
|
||||
"one-var": [2, { "initialized": "never" }],
|
||||
"operator-linebreak": [1, "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"],
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,4 @@
|
||||
dist
|
||||
|
||||
node_modules
|
||||
temp
|
||||
.tmp
|
||||
@ -7,7 +6,6 @@ temp
|
||||
app/bower_components
|
||||
test/bower_components
|
||||
package
|
||||
|
||||
.DS_Store
|
||||
builds/
|
||||
notes.txt
|
||||
@ -15,3 +13,4 @@ app/.DS_Store
|
||||
development/bundle.js
|
||||
builds.zip
|
||||
test/integration/bundle.js
|
||||
npm-debug.log
|
||||
|
@ -90,6 +90,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.
|
||||
|
@ -10,6 +10,7 @@ const MetamaskController = require('./metamask-controller')
|
||||
const extension = require('./lib/extension')
|
||||
|
||||
const STORAGE_KEY = 'metamask-config'
|
||||
const METAMASK_DEBUG = 'GULP_METAMASK_DEBUG'
|
||||
var popupIsOpen = false
|
||||
|
||||
const controller = new MetamaskController({
|
||||
@ -21,7 +22,7 @@ const controller = new MetamaskController({
|
||||
setData,
|
||||
loadData,
|
||||
})
|
||||
const idStore = controller.idStore
|
||||
const keyringController = controller.keyringController
|
||||
|
||||
function triggerUi () {
|
||||
if (!popupIsOpen) notification.show()
|
||||
@ -29,7 +30,7 @@ function triggerUi () {
|
||||
// On first install, open a window to MetaMask website to how-it-works.
|
||||
|
||||
extension.runtime.onInstalled.addListener(function (details) {
|
||||
if (details.reason === 'install') {
|
||||
if ((details.reason === 'install') && (!METAMASK_DEBUG)) {
|
||||
extension.tabs.create({url: 'https://metamask.io/#how-it-works'})
|
||||
}
|
||||
})
|
||||
@ -82,7 +83,7 @@ function setupControllerConnection (stream) {
|
||||
// push updates to popup
|
||||
controller.ethStore.on('update', controller.sendUpdate.bind(controller))
|
||||
controller.listeners.push(remote)
|
||||
idStore.on('update', controller.sendUpdate.bind(controller))
|
||||
keyringController.on('update', controller.sendUpdate.bind(controller))
|
||||
|
||||
// teardown on disconnect
|
||||
eos(stream, () => {
|
||||
@ -96,9 +97,9 @@ function setupControllerConnection (stream) {
|
||||
// plugin badge text
|
||||
//
|
||||
|
||||
idStore.on('update', updateBadge)
|
||||
keyringController.on('update', updateBadge)
|
||||
|
||||
function updateBadge (state) {
|
||||
function updateBadge () {
|
||||
var label = ''
|
||||
var unconfTxs = controller.configManager.unconfirmedTxs()
|
||||
var unconfTxLen = Object.keys(unconfTxs).length
|
||||
|
@ -6,7 +6,7 @@ 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
|
||||
@ -20,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')
|
||||
@ -31,14 +30,12 @@ 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',
|
||||
@ -65,14 +62,13 @@ function setupStreams(){
|
||||
mx.ignoreStream('provider')
|
||||
mx.ignoreStream('publicConfig')
|
||||
mx.ignoreStream('reload')
|
||||
|
||||
}
|
||||
|
||||
function shouldInjectWeb3(){
|
||||
function shouldInjectWeb3 () {
|
||||
return isAllowedSuffix(window.location.href)
|
||||
}
|
||||
|
||||
function isAllowedSuffix(testCase) {
|
||||
function isAllowedSuffix (testCase) {
|
||||
var prohibitedTypes = ['xml', 'pdf']
|
||||
var currentUrl = window.location.href
|
||||
var currentRegex
|
||||
|
@ -43,7 +43,7 @@ reloadStream.once('data', triggerReload)
|
||||
var pingChannel = inpageProvider.multiStream.createStream('pingpong')
|
||||
var pingStream = new PingStream({ objectMode: true })
|
||||
// wait for first successful reponse
|
||||
metamaskStream.once('_data', function(){
|
||||
metamaskStream.once('_data', function () {
|
||||
pingStream.pipe(pingChannel).pipe(pingStream)
|
||||
})
|
||||
endOfStream(pingStream, triggerReload)
|
||||
|
567
app/scripts/keyring-controller.js
Normal file
567
app/scripts/keyring-controller.js
Normal file
@ -0,0 +1,567 @@
|
||||
const async = require('async')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const ethBinToOps = require('eth-bin-to-ops')
|
||||
const EthQuery = require('eth-query')
|
||||
const bip39 = require('bip39')
|
||||
const Transaction = require('ethereumjs-tx')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
|
||||
const normalize = require('./lib/sig-util').normalize
|
||||
const encryptor = require('./lib/encryptor')
|
||||
const messageManager = require('./lib/message-manager')
|
||||
const autoFaucet = require('./lib/auto-faucet')
|
||||
const IdStoreMigrator = require('./lib/idStore-migrator')
|
||||
const BN = ethUtil.BN
|
||||
|
||||
// Keyrings:
|
||||
const SimpleKeyring = require('./keyrings/simple')
|
||||
const HdKeyring = require('./keyrings/hd')
|
||||
const keyringTypes = [
|
||||
SimpleKeyring,
|
||||
HdKeyring,
|
||||
]
|
||||
|
||||
const createId = require('./lib/random-id')
|
||||
|
||||
module.exports = class KeyringController extends EventEmitter {
|
||||
|
||||
constructor (opts) {
|
||||
super()
|
||||
this.configManager = opts.configManager
|
||||
this.ethStore = opts.ethStore
|
||||
this.encryptor = encryptor
|
||||
this.keyringTypes = keyringTypes
|
||||
|
||||
this.keyrings = []
|
||||
this.identities = {} // Essentially a name hash
|
||||
|
||||
this._unconfTxCbs = {}
|
||||
this._unconfMsgCbs = {}
|
||||
|
||||
this.getNetwork = opts.getNetwork
|
||||
|
||||
// TEMPORARY UNTIL FULL DEPRECATION:
|
||||
this.idStoreMigrator = new IdStoreMigrator({
|
||||
configManager: this.configManager,
|
||||
})
|
||||
}
|
||||
|
||||
getState () {
|
||||
const configManager = this.configManager
|
||||
const address = configManager.getSelectedAccount()
|
||||
const wallet = configManager.getWallet() // old style vault
|
||||
const vault = configManager.getVault() // new style vault
|
||||
|
||||
return {
|
||||
seedWords: this.configManager.getSeedWords(),
|
||||
isInitialized: (!!wallet || !!vault),
|
||||
isUnlocked: !!this.key,
|
||||
isDisclaimerConfirmed: this.configManager.getConfirmedDisclaimer(), // AUDIT this.configManager.getConfirmedDisclaimer(),
|
||||
unconfTxs: this.configManager.unconfirmedTxs(),
|
||||
transactions: this.configManager.getTxList(),
|
||||
unconfMsgs: messageManager.unconfirmedMsgs(),
|
||||
messages: messageManager.getMsgList(),
|
||||
selectedAddress: address,
|
||||
selectedAccount: address,
|
||||
shapeShiftTxList: this.configManager.getShapeShiftTxList(),
|
||||
currentFiat: this.configManager.getCurrentFiat(),
|
||||
conversionRate: this.configManager.getConversionRate(),
|
||||
conversionDate: this.configManager.getConversionDate(),
|
||||
keyringTypes: this.keyringTypes.map((krt) => krt.type()),
|
||||
identities: this.identities,
|
||||
}
|
||||
}
|
||||
|
||||
setStore (ethStore) {
|
||||
this.ethStore = ethStore
|
||||
}
|
||||
|
||||
createNewVaultAndKeychain (password, entropy, cb) {
|
||||
this.createNewVault(password, entropy, (err) => {
|
||||
if (err) return cb(err)
|
||||
this.createFirstKeyTree(password, cb)
|
||||
})
|
||||
}
|
||||
|
||||
createNewVaultAndRestore (password, seed, cb) {
|
||||
if (typeof password !== 'string') {
|
||||
return cb('Password must be text.')
|
||||
}
|
||||
|
||||
if (!bip39.validateMnemonic(seed)) {
|
||||
return cb('Seed phrase is invalid.')
|
||||
}
|
||||
|
||||
this.clearKeyrings()
|
||||
|
||||
this.createNewVault(password, '', (err) => {
|
||||
if (err) return cb(err)
|
||||
this.addNewKeyring('HD Key Tree', {
|
||||
mnemonic: seed,
|
||||
numberOfAccounts: 1,
|
||||
}, (err) => {
|
||||
if (err) return cb(err)
|
||||
const firstKeyring = this.keyrings[0]
|
||||
const accounts = firstKeyring.getAccounts()
|
||||
const firstAccount = accounts[0]
|
||||
const hexAccount = normalize(firstAccount)
|
||||
this.configManager.setSelectedAccount(hexAccount)
|
||||
this.setupAccounts(accounts)
|
||||
|
||||
this.emit('update')
|
||||
cb()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
migrateAndGetKey (password) {
|
||||
let key
|
||||
const shouldMigrate = !!this.configManager.getWallet() && !this.configManager.getVault()
|
||||
return this.loadKey(password)
|
||||
.then((derivedKey) => {
|
||||
key = derivedKey
|
||||
this.key = key
|
||||
return this.idStoreMigrator.oldSeedForPassword(password)
|
||||
})
|
||||
.then((serialized) => {
|
||||
if (serialized && shouldMigrate) {
|
||||
const keyring = this.restoreKeyring(serialized)
|
||||
this.keyrings.push(keyring)
|
||||
this.configManager.setSelectedAccount(keyring.getAccounts()[0])
|
||||
return this.persistAllKeyrings()
|
||||
.then(() => { return key })
|
||||
}
|
||||
return key
|
||||
})
|
||||
}
|
||||
|
||||
createNewVault (password, entropy, cb) {
|
||||
const configManager = this.configManager
|
||||
const salt = this.encryptor.generateSalt()
|
||||
configManager.setSalt(salt)
|
||||
|
||||
return this.migrateAndGetKey(password)
|
||||
.then(() => {
|
||||
return this.persistAllKeyrings()
|
||||
})
|
||||
.then(() => {
|
||||
cb()
|
||||
})
|
||||
.catch((err) => {
|
||||
cb(err)
|
||||
})
|
||||
}
|
||||
|
||||
createFirstKeyTree (password, cb) {
|
||||
this.clearKeyrings()
|
||||
this.addNewKeyring('HD Key Tree', {numberOfAccounts: 1}, (err) => {
|
||||
const accounts = this.keyrings[0].getAccounts()
|
||||
const firstAccount = accounts[0]
|
||||
const hexAccount = normalize(firstAccount)
|
||||
this.configManager.setSelectedAccount(firstAccount)
|
||||
|
||||
this.placeSeedWords()
|
||||
autoFaucet(hexAccount)
|
||||
this.setupAccounts(accounts)
|
||||
this.persistAllKeyrings()
|
||||
.then(() => {
|
||||
cb(err)
|
||||
})
|
||||
.catch((reason) => {
|
||||
cb(reason)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
placeSeedWords () {
|
||||
const firstKeyring = this.keyrings[0]
|
||||
const seedWords = firstKeyring.serialize().mnemonic
|
||||
this.configManager.setSeedWords(seedWords)
|
||||
}
|
||||
|
||||
submitPassword (password, cb) {
|
||||
this.migrateAndGetKey(password)
|
||||
.then((key) => {
|
||||
return this.unlockKeyrings(key)
|
||||
})
|
||||
.then((keyrings) => {
|
||||
this.keyrings = keyrings
|
||||
this.setupAccounts()
|
||||
this.emit('update')
|
||||
cb(null, this.getState())
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
cb(err)
|
||||
})
|
||||
}
|
||||
|
||||
loadKey (password) {
|
||||
const salt = this.configManager.getSalt() || this.encryptor.generateSalt()
|
||||
return this.encryptor.keyFromPassword(password + salt)
|
||||
.then((key) => {
|
||||
this.key = key
|
||||
this.configManager.setSalt(salt)
|
||||
return key
|
||||
})
|
||||
}
|
||||
|
||||
addNewKeyring (type, opts, cb) {
|
||||
const Keyring = this.getKeyringClassForType(type)
|
||||
const keyring = new Keyring(opts)
|
||||
const accounts = keyring.getAccounts()
|
||||
|
||||
this.keyrings.push(keyring)
|
||||
this.setupAccounts(accounts)
|
||||
this.persistAllKeyrings()
|
||||
.then(() => {
|
||||
cb()
|
||||
})
|
||||
.catch((reason) => {
|
||||
cb(reason)
|
||||
})
|
||||
}
|
||||
|
||||
addNewAccount (keyRingNum = 0, cb) {
|
||||
const ring = this.keyrings[keyRingNum]
|
||||
const accounts = ring.addAccounts(1)
|
||||
this.setupAccounts(accounts)
|
||||
this.persistAllKeyrings()
|
||||
.then(() => {
|
||||
cb()
|
||||
})
|
||||
.catch((reason) => {
|
||||
cb(reason)
|
||||
})
|
||||
}
|
||||
|
||||
setupAccounts (accounts) {
|
||||
var arr = accounts || this.getAccounts()
|
||||
arr.forEach((account) => {
|
||||
this.getBalanceAndNickname(account)
|
||||
})
|
||||
}
|
||||
|
||||
// Takes an account address and an iterator representing
|
||||
// the current number of named accounts.
|
||||
getBalanceAndNickname (account) {
|
||||
const address = normalize(account)
|
||||
this.ethStore.addAccount(address)
|
||||
this.createNickname(address)
|
||||
}
|
||||
|
||||
createNickname (address) {
|
||||
const hexAddress = normalize(address)
|
||||
var i = Object.keys(this.identities).length
|
||||
const oldNickname = this.configManager.nicknameForWallet(address)
|
||||
const name = oldNickname || `Account ${++i}`
|
||||
this.identities[hexAddress] = {
|
||||
address: hexAddress,
|
||||
name,
|
||||
}
|
||||
return this.saveAccountLabel(hexAddress, name)
|
||||
}
|
||||
|
||||
saveAccountLabel (account, label, cb) {
|
||||
const address = normalize(account)
|
||||
const configManager = this.configManager
|
||||
configManager.setNicknameForWallet(address, label)
|
||||
this.identities[address].name = label
|
||||
if (cb) {
|
||||
cb(null, label)
|
||||
} else {
|
||||
return label
|
||||
}
|
||||
}
|
||||
|
||||
persistAllKeyrings () {
|
||||
const serialized = this.keyrings.map((keyring) => {
|
||||
return {
|
||||
type: keyring.type,
|
||||
data: keyring.serialize(),
|
||||
}
|
||||
})
|
||||
return this.encryptor.encryptWithKey(this.key, serialized)
|
||||
.then((encryptedString) => {
|
||||
this.configManager.setVault(encryptedString)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
unlockKeyrings (key) {
|
||||
const encryptedVault = this.configManager.getVault()
|
||||
return this.encryptor.decryptWithKey(key, encryptedVault)
|
||||
.then((vault) => {
|
||||
vault.forEach(this.restoreKeyring.bind(this))
|
||||
return this.keyrings
|
||||
})
|
||||
}
|
||||
|
||||
restoreKeyring (serialized) {
|
||||
const { type, data } = serialized
|
||||
const Keyring = this.getKeyringClassForType(type)
|
||||
const keyring = new Keyring()
|
||||
keyring.deserialize(data)
|
||||
|
||||
const accounts = keyring.getAccounts()
|
||||
this.setupAccounts(accounts)
|
||||
|
||||
this.keyrings.push(keyring)
|
||||
return keyring
|
||||
}
|
||||
|
||||
getKeyringClassForType (type) {
|
||||
const Keyring = this.keyringTypes.reduce((res, kr) => {
|
||||
if (kr.type() === type) {
|
||||
return kr
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
})
|
||||
return Keyring
|
||||
}
|
||||
|
||||
getAccounts () {
|
||||
const keyrings = this.keyrings || []
|
||||
return keyrings.map(kr => kr.getAccounts())
|
||||
.reduce((res, arr) => {
|
||||
return res.concat(arr)
|
||||
}, [])
|
||||
}
|
||||
|
||||
setSelectedAddress (address, cb) {
|
||||
var addr = normalize(address)
|
||||
this.configManager.setSelectedAccount(addr)
|
||||
cb(null, addr)
|
||||
}
|
||||
|
||||
addUnconfirmedTransaction (txParams, onTxDoneCb, cb) {
|
||||
var self = this
|
||||
const configManager = this.configManager
|
||||
|
||||
// create txData obj with parameters and meta data
|
||||
var time = (new Date()).getTime()
|
||||
var txId = createId()
|
||||
txParams.metamaskId = txId
|
||||
txParams.metamaskNetworkId = this.getNetwork()
|
||||
var txData = {
|
||||
id: txId,
|
||||
txParams: txParams,
|
||||
time: time,
|
||||
status: 'unconfirmed',
|
||||
gasMultiplier: configManager.getGasMultiplier() || 1,
|
||||
metamaskNetworkId: this.getNetwork(),
|
||||
}
|
||||
|
||||
|
||||
// keep the onTxDoneCb around for after approval/denial (requires user interaction)
|
||||
// This onTxDoneCb fires completion to the Dapp's write operation.
|
||||
this._unconfTxCbs[txId] = onTxDoneCb
|
||||
|
||||
var provider = this.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 = self.addGasBuffer(result)
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
function didComplete (err) {
|
||||
if (err) return cb(err)
|
||||
configManager.addTx(txData)
|
||||
// signal update
|
||||
self.emit('update')
|
||||
// signal completion of add tx
|
||||
cb(null, txData)
|
||||
}
|
||||
}
|
||||
|
||||
addUnconfirmedMessage (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.emit('update')
|
||||
return msgId
|
||||
}
|
||||
|
||||
approveTransaction (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.emit('update')
|
||||
}
|
||||
|
||||
cancelTransaction (txId, cb) {
|
||||
const configManager = this.configManager
|
||||
var approvalCb = this._unconfTxCbs[txId] || noop
|
||||
|
||||
// reject tx
|
||||
approvalCb(null, false)
|
||||
// clean up
|
||||
configManager.rejectTx(txId)
|
||||
delete this._unconfTxCbs[txId]
|
||||
|
||||
if (cb && typeof cb === 'function') {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
signTransaction (txParams, cb) {
|
||||
try {
|
||||
const address = normalize(txParams.from)
|
||||
const keyring = this.getKeyringForAccount(address)
|
||||
|
||||
// Handle gas pricing
|
||||
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 = 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)
|
||||
|
||||
let tx = new Transaction(txParams)
|
||||
tx = keyring.signTransaction(address, tx)
|
||||
|
||||
// Add the tx hash to the persisted meta-tx object
|
||||
var txHash = ethUtil.bufferToHex(tx.hash())
|
||||
var metaTx = this.configManager.getTx(txParams.metamaskId)
|
||||
metaTx.hash = txHash
|
||||
this.configManager.updateTx(metaTx)
|
||||
|
||||
// return raw serialized tx
|
||||
var rawTx = ethUtil.bufferToHex(tx.serialize())
|
||||
cb(null, rawTx)
|
||||
} catch (e) {
|
||||
cb(e)
|
||||
}
|
||||
}
|
||||
|
||||
signMessage (msgParams, cb) {
|
||||
try {
|
||||
const keyring = this.getKeyringForAccount(msgParams.from)
|
||||
const address = normalize(msgParams.from)
|
||||
const rawSig = keyring.signMessage(address, msgParams.data)
|
||||
cb(null, rawSig)
|
||||
} catch (e) {
|
||||
cb(e)
|
||||
}
|
||||
}
|
||||
|
||||
getKeyringForAccount (address) {
|
||||
const hexed = normalize(address)
|
||||
return this.keyrings.find((ring) => {
|
||||
return ring.getAccounts()
|
||||
.map(normalize)
|
||||
.includes(hexed)
|
||||
})
|
||||
}
|
||||
|
||||
cancelMessage (msgId, cb) {
|
||||
if (cb && typeof cb === 'function') {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
setLocked (cb) {
|
||||
this.key = null
|
||||
this.keyrings = []
|
||||
this.emit('update')
|
||||
cb()
|
||||
}
|
||||
|
||||
exportAccount (address, cb) {
|
||||
try {
|
||||
const keyring = this.getKeyringForAccount(address)
|
||||
const privateKey = keyring.exportAccount(normalize(address))
|
||||
cb(null, privateKey)
|
||||
} catch (e) {
|
||||
cb(e)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
clearSeedWordCache (cb) {
|
||||
this.configManager.setSeedWords(null)
|
||||
cb(null, this.configManager.getSelectedAccount())
|
||||
}
|
||||
|
||||
clearKeyrings () {
|
||||
let accounts
|
||||
try {
|
||||
accounts = Object.keys(this.ethStore._currentState.accounts)
|
||||
} catch (e) {
|
||||
accounts = []
|
||||
}
|
||||
accounts.forEach((address) => {
|
||||
this.ethStore.removeAccount(address)
|
||||
})
|
||||
|
||||
this.keyrings = []
|
||||
this.identities = {}
|
||||
this.configManager.setSelectedAccount()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function noop () {}
|
101
app/scripts/keyrings/hd.js
Normal file
101
app/scripts/keyrings/hd.js
Normal file
@ -0,0 +1,101 @@
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const hdkey = require('ethereumjs-wallet/hdkey')
|
||||
const bip39 = require('bip39')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const sigUtil = require('../lib/sig-util')
|
||||
|
||||
const type = 'HD Key Tree'
|
||||
|
||||
const hdPathString = `m/44'/60'/0'/0`
|
||||
|
||||
module.exports = class HdKeyring extends EventEmitter {
|
||||
|
||||
static type () {
|
||||
return type
|
||||
}
|
||||
|
||||
constructor (opts = {}) {
|
||||
super()
|
||||
this.type = type
|
||||
this.deserialize(opts)
|
||||
}
|
||||
|
||||
deserialize (opts = {}) {
|
||||
this.opts = opts || {}
|
||||
this.wallets = []
|
||||
this.mnemonic = null
|
||||
this.root = null
|
||||
|
||||
if ('mnemonic' in opts) {
|
||||
this.initFromMnemonic(opts.mnemonic)
|
||||
}
|
||||
|
||||
if ('numberOfAccounts' in opts) {
|
||||
this.addAccounts(opts.numberOfAccounts)
|
||||
}
|
||||
}
|
||||
|
||||
initFromMnemonic (mnemonic) {
|
||||
this.mnemonic = mnemonic
|
||||
const seed = bip39.mnemonicToSeed(mnemonic)
|
||||
this.hdWallet = hdkey.fromMasterSeed(seed)
|
||||
this.root = this.hdWallet.derivePath(hdPathString)
|
||||
}
|
||||
|
||||
serialize () {
|
||||
return {
|
||||
mnemonic: this.mnemonic,
|
||||
numberOfAccounts: this.wallets.length,
|
||||
}
|
||||
}
|
||||
|
||||
exportAccount (address) {
|
||||
const wallet = this.getWalletForAccount(address)
|
||||
return wallet.getPrivateKey().toString('hex')
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return newWallets.map(w => w.getAddress().toString('hex'))
|
||||
}
|
||||
|
||||
getAccounts () {
|
||||
return 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 tx
|
||||
}
|
||||
|
||||
// For eth_sign, we need to sign transactions:
|
||||
signMessage (withAccount, data) {
|
||||
const wallet = this.getWalletForAccount(withAccount)
|
||||
const message = ethUtil.removeHexPrefix(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 rawMsgSig
|
||||
}
|
||||
|
||||
getWalletForAccount (account) {
|
||||
return this.wallets.find((w) => {
|
||||
const address = w.getAddress().toString('hex')
|
||||
return ((address === account) || (sigUtil.normalize(address) === account))
|
||||
})
|
||||
}
|
||||
}
|
67
app/scripts/keyrings/simple.js
Normal file
67
app/scripts/keyrings/simple.js
Normal file
@ -0,0 +1,67 @@
|
||||
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')
|
||||
|
||||
module.exports = class SimpleKeyring extends EventEmitter {
|
||||
|
||||
static type () {
|
||||
return type
|
||||
}
|
||||
|
||||
constructor (opts) {
|
||||
super()
|
||||
this.type = type
|
||||
this.opts = opts || {}
|
||||
this.wallets = []
|
||||
}
|
||||
|
||||
serialize () {
|
||||
return this.wallets.map(w => w.getPrivateKey().toString('hex'))
|
||||
}
|
||||
|
||||
deserialize (wallets = []) {
|
||||
this.wallets = wallets.map((w) => {
|
||||
var b = new Buffer(w, 'hex')
|
||||
const wallet = Wallet.fromPrivateKey(b)
|
||||
return wallet
|
||||
})
|
||||
}
|
||||
|
||||
addAccounts (n = 1) {
|
||||
var newWallets = []
|
||||
for (var i = 0; i < n; i++) {
|
||||
newWallets.push(Wallet.generate())
|
||||
}
|
||||
this.wallets = this.wallets.concat(newWallets)
|
||||
return newWallets.map(w => w.getAddress().toString('hex'))
|
||||
}
|
||||
|
||||
getAccounts () {
|
||||
return 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 tx
|
||||
}
|
||||
|
||||
// For eth_sign, we need to sign transactions:
|
||||
signMessage (withAccount, data) {
|
||||
const wallet = this.getWalletForAccount(withAccount)
|
||||
const message = ethUtil.removeHexPrefix(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 rawMsgSig
|
||||
}
|
||||
|
||||
getWalletForAccount (account) {
|
||||
return this.wallets.find(w => w.getAddress().toString('hex') === account)
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -18,17 +18,16 @@ function setupDappAutoReload (web3) {
|
||||
|
||||
return handleResetRequest
|
||||
|
||||
function handleResetRequest() {
|
||||
function handleResetRequest () {
|
||||
resetWasRequested = true
|
||||
// ignore if web3 was not used
|
||||
if (!pageIsUsingWeb3) return
|
||||
// reload after short timeout
|
||||
setTimeout(triggerReset, 500)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// reload the page
|
||||
function triggerReset () {
|
||||
global.location.reload()
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ 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 TESTNET_RPC = MetamaskConfig.network.testnet
|
||||
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
||||
@ -111,6 +112,27 @@ ConfigManager.prototype.setWallet = function (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 ('vault' in data) && data.vault
|
||||
}
|
||||
|
||||
ConfigManager.prototype.getKeychains = function () {
|
||||
return this.migrator.getData().keychains || []
|
||||
}
|
||||
|
||||
ConfigManager.prototype.setKeychains = function (keychains) {
|
||||
var data = this.migrator.getData()
|
||||
data.keychains = keychains
|
||||
this.setData(data)
|
||||
}
|
||||
|
||||
ConfigManager.prototype.getSelectedAccount = function () {
|
||||
var config = this.getConfig()
|
||||
return config.selectedAccount
|
||||
@ -118,7 +140,7 @@ ConfigManager.prototype.getSelectedAccount = function () {
|
||||
|
||||
ConfigManager.prototype.setSelectedAccount = function (address) {
|
||||
var config = this.getConfig()
|
||||
config.selectedAccount = address
|
||||
config.selectedAccount = ethUtil.addHexPrefix(address)
|
||||
this.setConfig(config)
|
||||
}
|
||||
|
||||
@ -133,11 +155,23 @@ ConfigManager.prototype.setShowSeedWords = function (should) {
|
||||
this.setData(data)
|
||||
}
|
||||
|
||||
|
||||
ConfigManager.prototype.getShouldShowSeedWords = function () {
|
||||
var data = this.migrator.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 ('seedWords' in data) && data.seedWords
|
||||
}
|
||||
|
||||
ConfigManager.prototype.getCurrentRpcAddress = function () {
|
||||
var provider = this.getProvider()
|
||||
if (!provider) return null
|
||||
@ -239,13 +273,15 @@ ConfigManager.prototype.getWalletNicknames = function () {
|
||||
}
|
||||
|
||||
ConfigManager.prototype.nicknameForWallet = function (account) {
|
||||
const address = ethUtil.addHexPrefix(account.toLowerCase())
|
||||
const nicknames = this.getWalletNicknames()
|
||||
return nicknames[account]
|
||||
return nicknames[address]
|
||||
}
|
||||
|
||||
ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
|
||||
const address = ethUtil.addHexPrefix(account.toLowerCase())
|
||||
const nicknames = this.getWalletNicknames()
|
||||
nicknames[account] = nickname
|
||||
nicknames[address] = nickname
|
||||
var data = this.getData()
|
||||
data.walletNicknames = nicknames
|
||||
this.setData(data)
|
||||
@ -253,6 +289,17 @@ ConfigManager.prototype.setNicknameForWallet = function (account, nickname) {
|
||||
|
||||
// observable
|
||||
|
||||
ConfigManager.prototype.getSalt = function () {
|
||||
var data = this.getData()
|
||||
return ('salt' in data) && 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)
|
||||
@ -270,15 +317,15 @@ ConfigManager.prototype._emitUpdates = function (state) {
|
||||
})
|
||||
}
|
||||
|
||||
ConfigManager.prototype.setConfirmed = function (confirmed) {
|
||||
ConfigManager.prototype.setConfirmedDisclaimer = function (confirmed) {
|
||||
var data = this.getData()
|
||||
data.isConfirmed = confirmed
|
||||
data.isDisclaimerConfirmed = confirmed
|
||||
this.setData(data)
|
||||
}
|
||||
|
||||
ConfigManager.prototype.getConfirmed = function () {
|
||||
ConfigManager.prototype.getConfirmedDisclaimer = function () {
|
||||
var data = this.getData()
|
||||
return ('isConfirmed' in data) && data.isConfirmed
|
||||
return ('isDisclaimerConfirmed' in data) && data.isDisclaimerConfirmed
|
||||
}
|
||||
|
||||
ConfigManager.prototype.setTOSHash = function (hash) {
|
||||
@ -315,7 +362,6 @@ ConfigManager.prototype.updateConversionRate = function () {
|
||||
this.setConversionPrice(0)
|
||||
this.setConversionDate('N/A')
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
ConfigManager.prototype.setConversionPrice = function (price) {
|
||||
@ -340,21 +386,6 @@ ConfigManager.prototype.getConversionDate = function () {
|
||||
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 : []
|
||||
|
149
app/scripts/lib/encryptor.js
Normal file
149
app/scripts/lib/encryptor.js
Normal file
@ -0,0 +1,149 @@
|
||||
var ethUtil = require('ethereumjs-util')
|
||||
|
||||
module.exports = {
|
||||
|
||||
// Simple encryption methods:
|
||||
encrypt,
|
||||
decrypt,
|
||||
|
||||
// More advanced encryption methods:
|
||||
keyFromPassword,
|
||||
encryptWithKey,
|
||||
decryptWithKey,
|
||||
|
||||
// Buffer <-> String methods
|
||||
convertArrayBufferViewtoString,
|
||||
convertStringToArrayBufferView,
|
||||
|
||||
// Buffer <-> Hex string methods
|
||||
serializeBufferForStorage,
|
||||
serializeBufferFromStorage,
|
||||
|
||||
// Buffer <-> base64 string methods
|
||||
encodeBufferToBase64,
|
||||
decodeBase64ToBuffer,
|
||||
|
||||
generateSalt,
|
||||
}
|
||||
|
||||
// Takes a Pojo, returns cypher text.
|
||||
function encrypt (password, dataObj) {
|
||||
return keyFromPassword(password)
|
||||
.then(function (passwordDerivedKey) {
|
||||
return encryptWithKey(passwordDerivedKey, dataObj)
|
||||
})
|
||||
}
|
||||
|
||||
function encryptWithKey (key, dataObj) {
|
||||
var data = JSON.stringify(dataObj)
|
||||
var dataBuffer = convertStringToArrayBufferView(data)
|
||||
var vector = global.crypto.getRandomValues(new Uint8Array(16))
|
||||
|
||||
return global.crypto.subtle.encrypt({
|
||||
name: 'AES-GCM',
|
||||
iv: vector,
|
||||
}, key, dataBuffer).then(function (buf) {
|
||||
var buffer = new Uint8Array(buf)
|
||||
var vectorStr = encodeBufferToBase64(vector)
|
||||
var vaultStr = encodeBufferToBase64(buffer)
|
||||
return `${vaultStr}\\${vectorStr}`
|
||||
})
|
||||
}
|
||||
|
||||
// Takes encrypted text, returns the restored Pojo.
|
||||
function decrypt (password, text) {
|
||||
return keyFromPassword(password)
|
||||
.then(function (key) {
|
||||
return decryptWithKey(key, text)
|
||||
})
|
||||
}
|
||||
|
||||
function decryptWithKey (key, text) {
|
||||
const parts = text.split('\\')
|
||||
const encryptedData = decodeBase64ToBuffer(parts[0])
|
||||
const vector = decodeBase64ToBuffer(parts[1])
|
||||
return crypto.subtle.decrypt({name: 'AES-GCM', iv: vector}, key, encryptedData)
|
||||
.then(function (result) {
|
||||
const decryptedData = new Uint8Array(result)
|
||||
const decryptedStr = convertArrayBufferViewtoString(decryptedData)
|
||||
const decryptedObj = JSON.parse(decryptedStr)
|
||||
return decryptedObj
|
||||
})
|
||||
.catch(function (reason) {
|
||||
throw new Error('Incorrect password')
|
||||
})
|
||||
}
|
||||
|
||||
function convertStringToArrayBufferView (str) {
|
||||
var bytes = new Uint8Array(str.length)
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
bytes[i] = str.charCodeAt(i)
|
||||
}
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
function convertArrayBufferViewtoString (buffer) {
|
||||
var str = ''
|
||||
for (var i = 0; i < buffer.byteLength; i++) {
|
||||
str += String.fromCharCode(buffer[i])
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
function keyFromPassword (password) {
|
||||
var passBuffer = convertStringToArrayBufferView(password)
|
||||
return global.crypto.subtle.digest('SHA-256', passBuffer)
|
||||
.then(function (passHash) {
|
||||
return global.crypto.subtle.importKey('raw', passHash, {name: 'AES-GCM'}, false, ['encrypt', 'decrypt'])
|
||||
})
|
||||
}
|
||||
|
||||
function serializeBufferFromStorage (str) {
|
||||
str = ethUtil.stripHexPrefix(str)
|
||||
var buf = new Uint8Array(str.length / 2)
|
||||
for (var i = 0; i < str.length; i += 2) {
|
||||
var seg = str.substr(i, 2)
|
||||
buf[i / 2] = parseInt(seg, 16)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// Should return a string, ready for storage, in hex format.
|
||||
function serializeBufferForStorage (buffer) {
|
||||
var result = '0x'
|
||||
var len = buffer.length || buffer.byteLength
|
||||
for (var i = 0; i < len; i++) {
|
||||
result += unprefixedHex(buffer[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function unprefixedHex (num) {
|
||||
var hex = num.toString(16)
|
||||
while (hex.length < 2) {
|
||||
hex = '0' + hex
|
||||
}
|
||||
return hex
|
||||
}
|
||||
|
||||
function encodeBufferToBase64 (buf) {
|
||||
var b64encoded = btoa(String.fromCharCode.apply(null, buf))
|
||||
return b64encoded
|
||||
}
|
||||
|
||||
function decodeBase64ToBuffer (base64) {
|
||||
var buf = new Uint8Array(atob(base64).split('')
|
||||
.map(function (c) {
|
||||
return c.charCodeAt(0)
|
||||
}))
|
||||
return buf
|
||||
}
|
||||
|
||||
function generateSalt (byteCount = 32) {
|
||||
var view = new Uint8Array(byteCount)
|
||||
global.crypto.getRandomValues(view)
|
||||
var b64encoded = btoa(String.fromCharCode.apply(null, view))
|
||||
return b64encoded
|
||||
}
|
52
app/scripts/lib/idStore-migrator.js
Normal file
52
app/scripts/lib/idStore-migrator.js
Normal file
@ -0,0 +1,52 @@
|
||||
const IdentityStore = require('./idStore')
|
||||
|
||||
|
||||
module.exports = class IdentityStoreMigrator {
|
||||
|
||||
constructor ({ configManager }) {
|
||||
this.configManager = configManager
|
||||
const hasOldVault = this.hasOldVault()
|
||||
if (!hasOldVault) {
|
||||
this.idStore = new IdentityStore({ configManager })
|
||||
}
|
||||
}
|
||||
|
||||
oldSeedForPassword (password) {
|
||||
const hasOldVault = this.hasOldVault()
|
||||
const configManager = this.configManager
|
||||
|
||||
if (!this.idStore) {
|
||||
this.idStore = new IdentityStore({ configManager })
|
||||
}
|
||||
|
||||
if (!hasOldVault) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.idStore.submitPassword(password, (err) => {
|
||||
if (err) return reject(err)
|
||||
try {
|
||||
resolve(this.serializeVault())
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
serializeVault () {
|
||||
const mnemonic = this.idStore._idmgmt.getSeed()
|
||||
const numberOfAccounts = this.idStore._getAddresses().length
|
||||
|
||||
return {
|
||||
type: 'HD Key Tree',
|
||||
data: { mnemonic, numberOfAccounts },
|
||||
}
|
||||
}
|
||||
|
||||
hasOldVault () {
|
||||
const wallet = this.configManager.getWallet()
|
||||
return wallet
|
||||
}
|
||||
}
|
@ -102,8 +102,7 @@ IdentityStore.prototype.getState = function () {
|
||||
isInitialized: !!configManager.getWallet() && !seedWords,
|
||||
isUnlocked: this._isUnlocked(),
|
||||
seedWords: seedWords,
|
||||
isConfirmed: configManager.getConfirmed(),
|
||||
isEthConfirmed: configManager.getShouldntShowWarning(),
|
||||
isDisclaimerConfirmed: configManager.getConfirmedDisclaimer(),
|
||||
unconfTxs: configManager.unconfirmedTxs(),
|
||||
transactions: configManager.getTxList(),
|
||||
unconfMsgs: messageManager.unconfirmedMsgs(),
|
||||
@ -114,7 +113,6 @@ IdentityStore.prototype.getState = function () {
|
||||
conversionRate: configManager.getConversionRate(),
|
||||
conversionDate: configManager.getConversionDate(),
|
||||
gasMultiplier: configManager.getGasMultiplier(),
|
||||
|
||||
}))
|
||||
}
|
||||
|
||||
@ -245,7 +243,7 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone
|
||||
], didComplete)
|
||||
|
||||
// perform static analyis on the target contract code
|
||||
function analyzeForDelegateCall(cb){
|
||||
function analyzeForDelegateCall (cb) {
|
||||
if (txParams.to) {
|
||||
query.getCode(txParams.to, (err, result) => {
|
||||
if (err) return cb(err.message || err)
|
||||
@ -258,16 +256,16 @@ IdentityStore.prototype.addUnconfirmedTransaction = function (txParams, onTxDone
|
||||
}
|
||||
}
|
||||
|
||||
function estimateGas(cb){
|
||||
function estimateGas (cb) {
|
||||
var estimationParams = extend(txParams)
|
||||
// 1 billion gas for estimation
|
||||
var gasLimit = '0x3b9aca00'
|
||||
estimationParams.gas = gasLimit
|
||||
query.estimateGas(estimationParams, function(err, result){
|
||||
query.estimateGas(estimationParams, function (err, result) {
|
||||
if (err) return cb(err.message || err)
|
||||
if (result === estimationParams.gas) {
|
||||
txData.simulationFails = true
|
||||
query.getBlockByNumber('latest', true, function(err, block){
|
||||
query.getBlockByNumber('latest', true, function (err, block) {
|
||||
if (err) return cb(err)
|
||||
txData.estimatedGas = block.gasLimit
|
||||
txData.txParams.gas = block.gasLimit
|
||||
@ -440,7 +438,9 @@ IdentityStore.prototype._loadIdentities = function () {
|
||||
var addresses = this._getAddresses()
|
||||
addresses.forEach((address, i) => {
|
||||
// // add to ethStore
|
||||
this._ethStore.addAccount(ethUtil.addHexPrefix(address))
|
||||
if (this._ethStore) {
|
||||
this._ethStore.addAccount(ethUtil.addHexPrefix(address))
|
||||
}
|
||||
// add to identities
|
||||
const defaultLabel = 'Account ' + (i + 1)
|
||||
const nickname = configManager.nicknameForWallet(address)
|
||||
|
@ -40,7 +40,7 @@ function MetamaskInpageProvider (connectionStream) {
|
||||
|
||||
self.idMap = {}
|
||||
// handle sendAsync requests via asyncProvider
|
||||
self.sendAsync = function(payload, cb){
|
||||
self.sendAsync = function (payload, cb) {
|
||||
// rewrite request ids
|
||||
var request = eachJsonMessage(payload, (message) => {
|
||||
var newId = createRandomId()
|
||||
@ -49,7 +49,7 @@ function MetamaskInpageProvider (connectionStream) {
|
||||
return message
|
||||
})
|
||||
// forward to asyncProvider
|
||||
asyncProvider.sendAsync(request, function(err, res){
|
||||
asyncProvider.sendAsync(request, function (err, res) {
|
||||
if (err) return cb(err)
|
||||
// transform messages to original ids
|
||||
eachJsonMessage(res, (message) => {
|
||||
@ -120,7 +120,7 @@ function remoteStoreWithLocalStorageCache (storageKey) {
|
||||
return store
|
||||
}
|
||||
|
||||
function eachJsonMessage(payload, transformFn){
|
||||
function eachJsonMessage (payload, transformFn) {
|
||||
if (Array.isArray(payload)) {
|
||||
return payload.map(transformFn)
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
module.exports = function isPopupOrNotification() {
|
||||
module.exports = function isPopupOrNotification () {
|
||||
const url = window.location.href
|
||||
if (url.match(/popup.html$/)) {
|
||||
return 'popup'
|
||||
|
@ -15,12 +15,9 @@ function show () {
|
||||
if (err) throw err
|
||||
|
||||
if (popup) {
|
||||
|
||||
// bring focus to existing popup
|
||||
extension.windows.update(popup.id, { focused: true })
|
||||
|
||||
} else {
|
||||
|
||||
// create new popup
|
||||
extension.windows.create({
|
||||
url: 'notification.html',
|
||||
@ -29,12 +26,11 @@ function show () {
|
||||
width,
|
||||
height,
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getWindows(cb) {
|
||||
function getWindows (cb) {
|
||||
// Ignore in test environment
|
||||
if (!extension.windows) {
|
||||
return cb()
|
||||
@ -45,14 +41,14 @@ function getWindows(cb) {
|
||||
})
|
||||
}
|
||||
|
||||
function getPopup(cb) {
|
||||
function getPopup (cb) {
|
||||
getWindows((err, windows) => {
|
||||
if (err) throw err
|
||||
cb(null, getPopupIn(windows))
|
||||
})
|
||||
}
|
||||
|
||||
function getPopupIn(windows) {
|
||||
function getPopupIn (windows) {
|
||||
return windows ? windows.find((win) => {
|
||||
return (win && win.type === 'popup' &&
|
||||
win.height === height &&
|
||||
@ -60,7 +56,7 @@ function getPopupIn(windows) {
|
||||
}) : null
|
||||
}
|
||||
|
||||
function closePopup() {
|
||||
function closePopup () {
|
||||
getPopup((err, popup) => {
|
||||
if (err) throw err
|
||||
if (!popup) return
|
||||
|
@ -1,7 +1,7 @@
|
||||
const MAX = 1000000000
|
||||
const MAX = Number.MAX_SAFE_INTEGER
|
||||
|
||||
let idCounter = Math.round( Math.random() * MAX )
|
||||
function createRandomId() {
|
||||
let idCounter = Math.round(Math.random() * MAX)
|
||||
function createRandomId () {
|
||||
idCounter = idCounter % MAX
|
||||
return idCounter++
|
||||
}
|
||||
|
28
app/scripts/lib/sig-util.js
Normal file
28
app/scripts/lib/sig-util.js
Normal file
@ -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
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
const extend = require('xtend')
|
||||
const EthStore = require('eth-store')
|
||||
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
||||
const IdentityStore = require('./lib/idStore')
|
||||
const KeyringController = require('./keyring-controller')
|
||||
const messageManager = require('./lib/message-manager')
|
||||
const HostStore = require('./lib/remote-store.js').HostStore
|
||||
const Web3 = require('web3')
|
||||
@ -11,15 +11,18 @@ const extension = require('./lib/extension')
|
||||
module.exports = class MetamaskController {
|
||||
|
||||
constructor (opts) {
|
||||
this.state = { network: 'loading' }
|
||||
this.opts = opts
|
||||
this.listeners = []
|
||||
this.configManager = new ConfigManager(opts)
|
||||
this.idStore = new IdentityStore({
|
||||
this.keyringController = new KeyringController({
|
||||
configManager: this.configManager,
|
||||
getNetwork: this.getStateNetwork.bind(this),
|
||||
})
|
||||
this.provider = this.initializeProvider(opts)
|
||||
this.ethStore = new EthStore(this.provider)
|
||||
this.idStore.setStore(this.ethStore)
|
||||
this.keyringController.setStore(this.ethStore)
|
||||
this.getNetwork()
|
||||
this.messageManager = messageManager
|
||||
this.publicConfigStore = this.initPublicConfigStore()
|
||||
|
||||
@ -30,19 +33,19 @@ module.exports = class MetamaskController {
|
||||
this.checkTOSChange()
|
||||
|
||||
this.scheduleConversionInterval()
|
||||
|
||||
}
|
||||
|
||||
getState () {
|
||||
return extend(
|
||||
this.state,
|
||||
this.ethStore.getState(),
|
||||
this.idStore.getState(),
|
||||
this.configManager.getConfig()
|
||||
this.configManager.getConfig(),
|
||||
this.keyringController.getState()
|
||||
)
|
||||
}
|
||||
|
||||
getApi () {
|
||||
const idStore = this.idStore
|
||||
const keyringController = this.keyringController
|
||||
|
||||
return {
|
||||
getState: (cb) => { cb(null, this.getState()) },
|
||||
@ -52,27 +55,26 @@ module.exports = class MetamaskController {
|
||||
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
|
||||
resetDisclaimer: this.resetDisclaimer.bind(this),
|
||||
setCurrentFiat: this.setCurrentFiat.bind(this),
|
||||
agreeToEthWarning: this.agreeToEthWarning.bind(this),
|
||||
setTOSHash: this.setTOSHash.bind(this),
|
||||
checkTOSChange: this.checkTOSChange.bind(this),
|
||||
setGasMultiplier: this.setGasMultiplier.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),
|
||||
// forward directly to keyringController
|
||||
placeSeedWords: keyringController.placeSeedWords.bind(keyringController),
|
||||
createNewVaultAndKeychain: keyringController.createNewVaultAndKeychain.bind(keyringController),
|
||||
createNewVaultAndRestore: keyringController.createNewVaultAndRestore.bind(keyringController),
|
||||
clearSeedWordCache: keyringController.clearSeedWordCache.bind(keyringController),
|
||||
addNewKeyring: keyringController.addNewKeyring.bind(keyringController),
|
||||
addNewAccount: keyringController.addNewAccount.bind(keyringController),
|
||||
submitPassword: keyringController.submitPassword.bind(keyringController),
|
||||
setSelectedAddress: keyringController.setSelectedAddress.bind(keyringController),
|
||||
approveTransaction: keyringController.approveTransaction.bind(keyringController),
|
||||
cancelTransaction: keyringController.cancelTransaction.bind(keyringController),
|
||||
signMessage: keyringController.signMessage.bind(keyringController),
|
||||
cancelMessage: keyringController.cancelMessage.bind(keyringController),
|
||||
setLocked: keyringController.setLocked.bind(keyringController),
|
||||
exportAccount: keyringController.exportAccount.bind(keyringController),
|
||||
saveAccountLabel: keyringController.saveAccountLabel.bind(keyringController),
|
||||
// coinbase
|
||||
buyEth: this.buyEth.bind(this),
|
||||
// shapeshift
|
||||
@ -85,23 +87,6 @@ module.exports = class MetamaskController {
|
||||
}
|
||||
|
||||
onRpcRequest (stream, originDomain, request) {
|
||||
|
||||
/* Commented out for Parity compliance
|
||||
* Parity does not permit additional keys, like `origin`,
|
||||
* and Infura is not currently filtering this key out.
|
||||
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 })
|
||||
}
|
||||
})
|
||||
*/
|
||||
|
||||
// handle rpc request
|
||||
this.provider.sendAsync(request, function onPayloadHandled (err, response) {
|
||||
logger(err, request, response)
|
||||
@ -134,38 +119,38 @@ module.exports = class MetamaskController {
|
||||
}
|
||||
|
||||
initializeProvider (opts) {
|
||||
const idStore = this.idStore
|
||||
const keyringController = this.keyringController
|
||||
|
||||
var providerOpts = {
|
||||
rpcUrl: this.configManager.getCurrentRpcAddress(),
|
||||
// account mgmt
|
||||
getAccounts: (cb) => {
|
||||
var selectedAddress = idStore.getSelectedAddress()
|
||||
var selectedAddress = this.configManager.getSelectedAccount()
|
||||
var result = selectedAddress ? [selectedAddress] : []
|
||||
cb(null, result)
|
||||
},
|
||||
// tx signing
|
||||
approveTransaction: this.newUnsignedTransaction.bind(this),
|
||||
signTransaction: (...args) => {
|
||||
idStore.signTransaction(...args)
|
||||
keyringController.signTransaction(...args)
|
||||
this.sendUpdate()
|
||||
},
|
||||
|
||||
// msg signing
|
||||
approveMessage: this.newUnsignedMessage.bind(this),
|
||||
signMessage: (...args) => {
|
||||
idStore.signMessage(...args)
|
||||
keyringController.signMessage(...args)
|
||||
this.sendUpdate()
|
||||
},
|
||||
}
|
||||
|
||||
var provider = MetaMaskProvider(providerOpts)
|
||||
var web3 = new Web3(provider)
|
||||
idStore.web3 = web3
|
||||
idStore.getNetwork()
|
||||
this.web3 = web3
|
||||
keyringController.web3 = web3
|
||||
|
||||
provider.on('block', this.processBlock.bind(this))
|
||||
provider.on('error', idStore.getNetwork.bind(idStore))
|
||||
provider.on('error', this.getNetwork.bind(this))
|
||||
|
||||
return provider
|
||||
}
|
||||
@ -173,7 +158,7 @@ module.exports = class MetamaskController {
|
||||
initPublicConfigStore () {
|
||||
// get init state
|
||||
var initPublicState = extend(
|
||||
idStoreToPublic(this.idStore.getState()),
|
||||
keyringControllerToPublic(this.keyringController.getState()),
|
||||
configToPublic(this.configManager.getConfig())
|
||||
)
|
||||
|
||||
@ -183,12 +168,14 @@ module.exports = class MetamaskController {
|
||||
this.configManager.subscribe(function (state) {
|
||||
storeSetFromObj(publicConfigStore, configToPublic(state))
|
||||
})
|
||||
this.idStore.on('update', function (state) {
|
||||
storeSetFromObj(publicConfigStore, idStoreToPublic(state))
|
||||
this.keyringController.on('update', () => {
|
||||
const state = this.keyringController.getState()
|
||||
storeSetFromObj(publicConfigStore, keyringControllerToPublic(state))
|
||||
this.sendUpdate()
|
||||
})
|
||||
|
||||
// idStore substate
|
||||
function idStoreToPublic (state) {
|
||||
// keyringController substate
|
||||
function keyringControllerToPublic (state) {
|
||||
return {
|
||||
selectedAddress: state.selectedAddress,
|
||||
}
|
||||
@ -211,10 +198,10 @@ module.exports = class MetamaskController {
|
||||
}
|
||||
|
||||
newUnsignedTransaction (txParams, onTxDoneCb) {
|
||||
const idStore = this.idStore
|
||||
let err = this.enforceTxValidations(txParams)
|
||||
const keyringController = this.keyringController
|
||||
const err = this.enforceTxValidations(txParams)
|
||||
if (err) return onTxDoneCb(err)
|
||||
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
|
||||
keyringController.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
|
||||
if (err) return onTxDoneCb(err)
|
||||
this.sendUpdate()
|
||||
this.opts.showUnconfirmedTx(txParams, txData, onTxDoneCb)
|
||||
@ -229,9 +216,9 @@ module.exports = class MetamaskController {
|
||||
}
|
||||
|
||||
newUnsignedMessage (msgParams, cb) {
|
||||
var state = this.idStore.getState()
|
||||
var state = this.keyringController.getState()
|
||||
if (!state.isUnlocked) {
|
||||
this.idStore.addUnconfirmedMessage(msgParams, cb)
|
||||
this.keyringController.addUnconfirmedMessage(msgParams, cb)
|
||||
this.opts.unlockAccountMessage()
|
||||
} else {
|
||||
this.addUnconfirmedMessage(msgParams, cb)
|
||||
@ -240,8 +227,8 @@ module.exports = class MetamaskController {
|
||||
}
|
||||
|
||||
addUnconfirmedMessage (msgParams, cb) {
|
||||
const idStore = this.idStore
|
||||
const msgId = idStore.addUnconfirmedMessage(msgParams, cb)
|
||||
const keyringController = this.keyringController
|
||||
const msgId = keyringController.addUnconfirmedMessage(msgParams, cb)
|
||||
this.opts.showUnconfirmedMessage(msgParams, msgId)
|
||||
}
|
||||
|
||||
@ -260,8 +247,8 @@ module.exports = class MetamaskController {
|
||||
|
||||
verifyNetwork () {
|
||||
// Check network when restoring connectivity:
|
||||
if (this.idStore._currentState.network === 'loading') {
|
||||
this.idStore.getNetwork()
|
||||
if (this.state.network === 'loading') {
|
||||
this.getNetwork()
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,12 +273,11 @@ module.exports = class MetamaskController {
|
||||
} catch (e) {
|
||||
console.error('Error in checking TOS change.')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
agreeToDisclaimer (cb) {
|
||||
try {
|
||||
this.configManager.setConfirmed(true)
|
||||
this.configManager.setConfirmedDisclaimer(true)
|
||||
cb()
|
||||
} catch (e) {
|
||||
cb(e)
|
||||
@ -300,7 +286,7 @@ module.exports = class MetamaskController {
|
||||
|
||||
resetDisclaimer () {
|
||||
try {
|
||||
this.configManager.setConfirmed(false)
|
||||
this.configManager.setConfirmedDisclaimer(false)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
@ -331,26 +317,17 @@ module.exports = class MetamaskController {
|
||||
}, 300000)
|
||||
}
|
||||
|
||||
agreeToEthWarning (cb) {
|
||||
try {
|
||||
this.configManager.setShouldntShowWarning()
|
||||
cb()
|
||||
} catch (e) {
|
||||
cb(e)
|
||||
}
|
||||
}
|
||||
|
||||
// called from popup
|
||||
setRpcTarget (rpcTarget) {
|
||||
this.configManager.setRpcTarget(rpcTarget)
|
||||
extension.runtime.reload()
|
||||
this.idStore.getNetwork()
|
||||
this.getNetwork()
|
||||
}
|
||||
|
||||
setProviderType (type) {
|
||||
this.configManager.setProviderType(type)
|
||||
extension.runtime.reload()
|
||||
this.idStore.getNetwork()
|
||||
this.getNetwork()
|
||||
}
|
||||
|
||||
useEtherscanProvider () {
|
||||
@ -361,7 +338,7 @@ module.exports = class MetamaskController {
|
||||
buyEth (address, amount) {
|
||||
if (!amount) amount = '5'
|
||||
|
||||
var network = this.idStore._currentState.network
|
||||
var network = this.state.network
|
||||
var url = `https://buy.coinbase.com/?code=9ec56d01-7e81-5017-930c-513daa27bb6a&amount=${amount}&address=${address}&crypto_currency=ETH`
|
||||
|
||||
if (network === '2') {
|
||||
@ -377,6 +354,25 @@ module.exports = class MetamaskController {
|
||||
this.configManager.createShapeShiftTx(depositAddress, depositType)
|
||||
}
|
||||
|
||||
getNetwork (err) {
|
||||
if (err) {
|
||||
this.state.network = 'loading'
|
||||
this.sendUpdate()
|
||||
}
|
||||
|
||||
this.web3.version.getNetwork((err, network) => {
|
||||
if (err) {
|
||||
this.state.network = 'loading'
|
||||
return this.sendUpdate()
|
||||
}
|
||||
if (global.METAMASK_DEBUG) {
|
||||
console.log('web3.getNetwork returned ' + network)
|
||||
}
|
||||
this.state.network = network
|
||||
this.sendUpdate()
|
||||
})
|
||||
}
|
||||
|
||||
setGasMultiplier (gasMultiplier, cb) {
|
||||
try {
|
||||
this.configManager.setGasMultiplier(gasMultiplier)
|
||||
@ -385,4 +381,8 @@ module.exports = class MetamaskController {
|
||||
cb(e)
|
||||
}
|
||||
}
|
||||
|
||||
getStateNetwork () {
|
||||
return this.state.network
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||
module.exports = initializePopup
|
||||
|
||||
|
||||
function initializePopup(connectionStream){
|
||||
function initializePopup (connectionStream) {
|
||||
// setup app
|
||||
connectToAccountManager(connectionStream, setupApp)
|
||||
}
|
||||
@ -32,7 +32,7 @@ function setupWeb3Connection (connectionStream) {
|
||||
}
|
||||
|
||||
function setupControllerConnection (connectionStream, cb) {
|
||||
// this is a really sneaky way of adding EventEmitter api
|
||||
// this is a really sneaky way of adding EventEmitter api
|
||||
// to a bi-directional dnode instance
|
||||
var eventEmitter = new EventEmitter()
|
||||
var accountManagerDnode = Dnode({
|
||||
|
@ -18,7 +18,7 @@ var portStream = new PortStream(pluginPort)
|
||||
|
||||
startPopup(portStream)
|
||||
|
||||
function closePopupIfOpen(name) {
|
||||
function closePopupIfOpen (name) {
|
||||
if (name !== 'notification') {
|
||||
notification.closePopup()
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -136,7 +136,7 @@
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "1",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -102,7 +102,7 @@
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -60,7 +60,7 @@
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -2,7 +2,6 @@
|
||||
"metamask": {
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"isEthConfirmed": true,
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {
|
||||
"0x0abdd95cafcabec9b3e99dcd09fc4b441037cb80": {
|
||||
@ -92,7 +91,7 @@
|
||||
"transactions": [],
|
||||
"selectedAddress": "0x0abdd95cafcabec9b3e99dcd09fc4b441037cb80",
|
||||
"network": "2",
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"shapeShiftTxList": [],
|
||||
@ -116,4 +115,4 @@
|
||||
"scrollToBottom": true
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@
|
||||
"transactions": [],
|
||||
"selectedAddress": "0x843963b837841dad3b0f5969ff271108776616df",
|
||||
"network": "2",
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -10,7 +10,7 @@
|
||||
"transactions": [],
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -160,8 +160,7 @@
|
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"network": "166",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isEthConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -57,7 +57,7 @@
|
||||
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -2,18 +2,22 @@
|
||||
"metamask": {
|
||||
"isInitialized": false,
|
||||
"isUnlocked": false,
|
||||
"currentDomain": "example.com",
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {},
|
||||
"unconfTxs": {},
|
||||
"currentFiat": "USD",
|
||||
"conversionRate": 11.47635827,
|
||||
"conversionDate": 1477606503,
|
||||
"network": null,
|
||||
"accounts": {},
|
||||
"transactions": [],
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"isConfirmed": false,
|
||||
"isEthConfirmed": false,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"shapeShiftTxList": [],
|
||||
"keyringTypes": [
|
||||
"Simple Key Pair"
|
||||
],
|
||||
"provider": {
|
||||
"type": "testnet"
|
||||
}
|
||||
@ -21,15 +25,15 @@
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
"currentView": {
|
||||
"name": "EthStoreWarning"
|
||||
"name": "accounts",
|
||||
"detailView": null
|
||||
},
|
||||
"accountDetail": {
|
||||
"subview": "transactions"
|
||||
},
|
||||
"currentDomain": "127.0.0.1:9966",
|
||||
"transForward": true,
|
||||
"isLoading": false,
|
||||
"warning": null
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@
|
||||
},
|
||||
"transactions": [],
|
||||
"network": "2",
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
@ -2,7 +2,6 @@
|
||||
"metamask": {
|
||||
"isInitialized": true,
|
||||
"isUnlocked": false,
|
||||
"isEthConfirmed": true,
|
||||
"currentDomain": "example.com",
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {},
|
||||
@ -15,7 +14,7 @@
|
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"network": "1473186153102",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"shapeShiftTxList": [],
|
||||
|
@ -10,7 +10,7 @@
|
||||
"transactions": [],
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"isConfirmed": false,
|
||||
"isDisclaimerConfirmed": false,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
File diff suppressed because one or more lines are too long
@ -2,7 +2,6 @@
|
||||
"metamask": {
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"isEthConfirmed": true,
|
||||
"currentDomain": "example.com",
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {
|
||||
@ -355,7 +354,7 @@
|
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"network": "1471904489432",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {
|
||||
"1472076978535283": {
|
||||
"id": 1472076978535283,
|
||||
@ -403,4 +402,4 @@
|
||||
"warning": null
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -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,"isDisclaimerConfirmed":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":{}}
|
||||
|
File diff suppressed because one or more lines are too long
@ -2,7 +2,6 @@
|
||||
"metamask": {
|
||||
"isInitialized": false,
|
||||
"isUnlocked": false,
|
||||
"isEthConfirmed": false,
|
||||
"currentDomain": "example.com",
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {},
|
||||
@ -13,7 +12,7 @@
|
||||
"accounts": {},
|
||||
"transactions": [],
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"shapeShiftTxList": [],
|
||||
@ -36,4 +35,4 @@
|
||||
"warning": null
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
"metamask": {
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"isEthConfirmed": false,
|
||||
"currentDomain": "example.com",
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {
|
||||
@ -49,7 +48,7 @@
|
||||
"transactions": [],
|
||||
"network": "2",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"shapeShiftTxList": [],
|
||||
@ -73,4 +72,4 @@
|
||||
"detailView": {}
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
"metamask": {
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"isEthConfirmed": true,
|
||||
"currentDomain": "example.com",
|
||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||
"identities": {
|
||||
@ -50,7 +49,7 @@
|
||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||
"network": "1",
|
||||
"seedWords": null,
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"shapeShiftTxList": [],
|
||||
@ -345,4 +344,4 @@
|
||||
"isSubLoading": false
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
"transactions": [],
|
||||
"network": "2",
|
||||
"seedWords": "debris dizzy just program just float decrease vacant alarm reduce speak stadium",
|
||||
"isConfirmed": true,
|
||||
"isDisclaimerConfirmed": true,
|
||||
"unconfMsgs": {},
|
||||
"messages": [],
|
||||
"provider": {
|
||||
|
188
docs/multi_vault_planning.md
Normal file
188
docs/multi_vault_planning.md
Normal file
@ -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.
|
||||
|
@ -26,7 +26,7 @@ const extension = require('./development/mockExtension')
|
||||
// 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)
|
||||
|
||||
@ -107,7 +107,7 @@ function getOldStyleData () {
|
||||
return result
|
||||
}
|
||||
|
||||
actions._setAccountManager(controller.getApi())
|
||||
actions._setBackgroundConnection(controller.getApi())
|
||||
actions.update = function(stateName) {
|
||||
selectedView = stateName
|
||||
updateQueryParams(stateName)
|
||||
|
13
package.json
13
package.json
@ -4,18 +4,19 @@
|
||||
"public": false,
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "gulp dev",
|
||||
"start": "npm run dev",
|
||||
"lint": "gulp lint",
|
||||
"dev": "gulp dev",
|
||||
"buildCiUnits": "node test/integration/index.js",
|
||||
"dev": "gulp dev --debug",
|
||||
"dist": "gulp dist --disableLiveReload",
|
||||
"test": "npm run fastTest && npm run ci && npm run lint",
|
||||
"fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
||||
"fastTest": "METAMASK_ENV=test 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 ./",
|
||||
"mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||
"buildMock": "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"
|
||||
},
|
||||
"browserify": {
|
||||
@ -33,6 +34,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "^1.5.2",
|
||||
"bip39": "^2.2.0",
|
||||
"browserify-derequire": "^0.9.4",
|
||||
"clone": "^1.0.2",
|
||||
"copy-to-clipboard": "^2.0.0",
|
||||
@ -46,6 +48,7 @@
|
||||
"eth-store": "^1.1.0",
|
||||
"ethereumjs-tx": "^1.0.0",
|
||||
"ethereumjs-util": "^4.4.0",
|
||||
"ethereumjs-wallet": "^0.6.0",
|
||||
"express": "^4.14.0",
|
||||
"gulp-eslint": "^2.0.0",
|
||||
"hat": "0.0.3",
|
||||
@ -53,7 +56,7 @@
|
||||
"iframe": "^1.0.0",
|
||||
"iframe-stream": "^1.0.2",
|
||||
"inject-css": "^0.1.1",
|
||||
"jazzicon": "1.1.5",
|
||||
"jazzicon": "^1.2.0",
|
||||
"menu-droppo": "^1.1.0",
|
||||
"metamask-logo": "^2.1.2",
|
||||
"mississippi": "^1.2.0",
|
||||
|
@ -12,7 +12,7 @@
|
||||
<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">
|
||||
|
21
test/integration/index.js
Normal file
21
test/integration/index.js
Normal file
@ -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);
|
||||
|
67
test/integration/lib/encryptor-test.js
Normal file
67
test/integration/lib/encryptor-test.js
Normal file
@ -0,0 +1,67 @@
|
||||
var encryptor = require('../../../app/scripts/lib/encryptor')
|
||||
|
||||
QUnit.test('encryptor:serializeBufferForStorage', function (assert) {
|
||||
assert.expect(1)
|
||||
var buf = new Buffer(2)
|
||||
buf[0] = 16
|
||||
buf[1] = 1
|
||||
|
||||
var output = encryptor.serializeBufferForStorage(buf)
|
||||
|
||||
var expect = '0x1001'
|
||||
assert.equal(expect, output)
|
||||
})
|
||||
|
||||
QUnit.test('encryptor:serializeBufferFromStorage', function (assert) {
|
||||
assert.expect(2)
|
||||
var input = '0x1001'
|
||||
var output = encryptor.serializeBufferFromStorage(input)
|
||||
|
||||
assert.equal(output[0], 16)
|
||||
assert.equal(output[1], 1)
|
||||
})
|
||||
|
||||
QUnit.test('encryptor:encrypt & decrypt', function(assert) {
|
||||
var done = assert.async();
|
||||
var password, data, encrypted
|
||||
|
||||
password = 'a sample passw0rd'
|
||||
data = { foo: 'data to encrypt' }
|
||||
|
||||
encryptor.encrypt(password, data)
|
||||
.then(function(encryptedStr) {
|
||||
assert.equal(typeof encryptedStr, 'string', 'returns a string')
|
||||
return encryptor.decrypt(password, encryptedStr)
|
||||
})
|
||||
.then(function (decryptedObj) {
|
||||
assert.deepEqual(decryptedObj, data, 'decrypted what was encrypted')
|
||||
done()
|
||||
})
|
||||
.catch(function(reason) {
|
||||
assert.ifError(reason, 'threw an error')
|
||||
done(reason)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
QUnit.test('encryptor:encrypt & decrypt with wrong password', function(assert) {
|
||||
var done = assert.async();
|
||||
var password, data, encrypted, wrongPassword
|
||||
|
||||
password = 'a sample passw0rd'
|
||||
wrongPassword = 'a wrong password'
|
||||
data = { foo: 'data to encrypt' }
|
||||
|
||||
encryptor.encrypt(password, data)
|
||||
.then(function(encryptedStr) {
|
||||
assert.equal(typeof encryptedStr, 'string', 'returns a string')
|
||||
return encryptor.decrypt(wrongPassword, encryptedStr)
|
||||
})
|
||||
.then(function (decryptedObj) {
|
||||
assert.equal(!decryptedObj, true, 'Wrong password should not decrypt')
|
||||
done()
|
||||
})
|
||||
.catch(function(reason) {
|
||||
done()
|
||||
})
|
||||
})
|
15
test/integration/lib/first-time.js
Normal file
15
test/integration/lib/first-time.js
Normal file
@ -0,0 +1,15 @@
|
||||
QUnit.test('agree to terms', function (assert) {
|
||||
var done = assert.async()
|
||||
let app
|
||||
|
||||
wait().then(function() {
|
||||
app = $('iframe').contents().find('#app-content .mock-app-root')
|
||||
app.find('.markdown').prop('scrollTop', 100000000)
|
||||
return wait()
|
||||
}).then(function() {
|
||||
var title = app.find('h1').text()
|
||||
assert.equal(title, 'MetaMask', 'title screen')
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
46
test/integration/lib/keyring-controller-test.js
Normal file
46
test/integration/lib/keyring-controller-test.js
Normal file
@ -0,0 +1,46 @@
|
||||
var KeyringController = require('../../../app/scripts/keyring-controller')
|
||||
var ConfigManager = require('../../../app/scripts/lib/config-manager')
|
||||
|
||||
var oldStyleVault = require('../mocks/oldVault.json')
|
||||
|
||||
var STORAGE_KEY = 'metamask-config'
|
||||
var PASSWORD = '12345678'
|
||||
var FIRST_ADDRESS = '0x4dd5d356c5A016A220bCD69e82e5AF680a430d00'.toLowerCase()
|
||||
|
||||
|
||||
QUnit.module('Old Style Vaults', {
|
||||
beforeEach: function () {
|
||||
window.localStorage[STORAGE_KEY] = JSON.stringify(oldStyleVault)
|
||||
|
||||
this.configManager = new ConfigManager({
|
||||
loadData: () => { return JSON.parse(window.localStorage[STORAGE_KEY]) },
|
||||
setData: (data) => { window.localStorage[STORAGE_KEY] = JSON.stringify(data) },
|
||||
})
|
||||
|
||||
this.keyringController = new KeyringController({
|
||||
configManager: this.configManager,
|
||||
getNetwork: () => { return '2' },
|
||||
})
|
||||
|
||||
this.ethStore = {
|
||||
addAccount: () => {},
|
||||
removeAccount: () => {},
|
||||
}
|
||||
|
||||
this.keyringController.setStore(this.ethStore)
|
||||
}
|
||||
})
|
||||
|
||||
QUnit.test('keyringController:isInitialized', function (assert) {
|
||||
assert.ok(this.keyringController.getState().isInitialized)
|
||||
})
|
||||
|
||||
QUnit.test('keyringController:submitPassword', function (assert) {
|
||||
var done = assert.async()
|
||||
|
||||
this.keyringController.submitPassword(PASSWORD, (err, state) => {
|
||||
assert.notOk(err)
|
||||
assert.ok(state.identities[FIRST_ADDRESS])
|
||||
done()
|
||||
})
|
||||
})
|
21
test/integration/mocks/oldVault.json
Normal file
21
test/integration/mocks/oldVault.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"meta": {
|
||||
"version": 4
|
||||
},
|
||||
"data": {
|
||||
"fiatCurrency": "USD",
|
||||
"isConfirmed": true,
|
||||
"TOSHash": "a4f4e23f823a7ac51783e7ffba7914a911b09acdb97263296b7e14b527f80c5b",
|
||||
"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"
|
||||
},
|
||||
"selectedAccount": "0x4dd5d356c5a016a220bcd69e82e5af680a430d00"
|
||||
},
|
||||
"showSeedWords": false,
|
||||
"isEthConfirmed": true
|
||||
}
|
||||
}
|
@ -1,24 +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')
|
||||
|
||||
app.find('.markdown').prop('scrollTop', 100000000)
|
||||
|
||||
wait().then(function() {
|
||||
app.find('button').click()
|
||||
}).then(function() {
|
||||
return 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()
|
||||
})
|
||||
|
||||
// Wait for view to transition:
|
||||
})
|
@ -1,5 +1,5 @@
|
||||
var ConfigManager = require('../../app/scripts/lib/config-manager')
|
||||
const STORAGE_KEY = 'metamask-persistance-key'
|
||||
const STORAGE_KEY = 'metamask-config'
|
||||
const extend = require('xtend')
|
||||
|
||||
module.exports = function() {
|
||||
|
32
test/lib/mock-encryptor.js
Normal file
32
test/lib/mock-encryptor.js
Normal file
@ -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!'
|
||||
},
|
||||
|
||||
}
|
38
test/lib/mock-simple-keychain.js
Normal file
38
test/lib/mock-simple-keychain.js
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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')
|
||||
});
|
||||
});
|
@ -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() },
|
||||
})
|
||||
|
||||
@ -135,7 +135,7 @@ describe('tx confirmation screen', function() {
|
||||
}
|
||||
freeze(initialState)
|
||||
|
||||
actions._setAccountManager({
|
||||
actions._setBackgroundConnection({
|
||||
approveTransaction(txId, cb) { cb() },
|
||||
})
|
||||
|
||||
|
@ -100,31 +100,31 @@ describe('config-manager', function() {
|
||||
|
||||
describe('confirmation', function() {
|
||||
|
||||
describe('#getConfirmed', function() {
|
||||
describe('#getConfirmedDisclaimer', function() {
|
||||
it('should return false if no previous key exists', function() {
|
||||
var result = configManager.getConfirmed()
|
||||
var result = configManager.getConfirmedDisclaimer()
|
||||
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()
|
||||
describe('#setConfirmedDisclaimer', function() {
|
||||
it('should make getConfirmedDisclaimer return true once set', function() {
|
||||
assert.equal(configManager.getConfirmedDisclaimer(), false)
|
||||
configManager.setConfirmedDisclaimer(true)
|
||||
var result = configManager.getConfirmedDisclaimer()
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should be able to set false', function() {
|
||||
configManager.setConfirmed(false)
|
||||
var result = configManager.getConfirmed()
|
||||
configManager.setConfirmedDisclaimer(false)
|
||||
var result = configManager.getConfirmedDisclaimer()
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should persist to local storage', function() {
|
||||
configManager.setConfirmed(true)
|
||||
configManager.setConfirmedDisclaimer(true)
|
||||
var data = configManager.getData()
|
||||
assert.equal(data.isConfirmed, true)
|
||||
assert.equal(data.isDisclaimerConfirmed, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -153,7 +153,7 @@ describe('config-manager', function() {
|
||||
rpcTarget: 'foobar'
|
||||
},
|
||||
}
|
||||
configManager.setConfirmed(true)
|
||||
configManager.setConfirmedDisclaimer(true)
|
||||
configManager.setConfig(testConfig)
|
||||
|
||||
var testWallet = {
|
||||
@ -164,7 +164,7 @@ 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)
|
||||
assert.equal(configManager.getConfirmedDisclaimer(), true)
|
||||
|
||||
testConfig.provider.type = 'something else!'
|
||||
configManager.setConfig(testConfig)
|
||||
@ -173,7 +173,7 @@ 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)
|
||||
assert.equal(configManager.getConfirmedDisclaimer(), true)
|
||||
})
|
||||
})
|
||||
|
||||
|
160
test/unit/idStore-migration-test.js
Normal file
160
test/unit/idStore-migration-test.js
Normal file
@ -0,0 +1,160 @@
|
||||
const async = require('async')
|
||||
const assert = require('assert')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const BN = ethUtil.BN
|
||||
const ConfigManager = require('../../app/scripts/lib/config-manager')
|
||||
const delegateCallCode = require('../lib/example-code.json').delegateCallCode
|
||||
|
||||
// The old way:
|
||||
const IdentityStore = require('../../app/scripts/lib/idStore')
|
||||
const STORAGE_KEY = 'metamask-config'
|
||||
const extend = require('xtend')
|
||||
|
||||
// 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',
|
||||
}
|
||||
|
||||
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()
|
||||
window.localStorage = {} // Hacking localStorage support into JSDom
|
||||
configManager = new ConfigManager({
|
||||
loadData,
|
||||
setData: (d) => { window.localStorage = d }
|
||||
})
|
||||
|
||||
|
||||
idStore = new IdentityStore({
|
||||
configManager: configManager,
|
||||
ethStore: {
|
||||
addAccount(acct) { accounts.push(ethUtil.addHexPrefix(acct)) },
|
||||
del(acct) { delete accounts[acct] },
|
||||
},
|
||||
})
|
||||
|
||||
idStore._createVault(password, mockVault.seed, null, (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] },
|
||||
},
|
||||
})
|
||||
|
||||
// Stub out the browser crypto for a mock encryptor.
|
||||
// Browser crypto is tested in the integration test suite.
|
||||
keyringController.encryptor = mockEncryptor
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('entering a password', function() {
|
||||
it('should identify an old wallet as an initialized keyring', function() {
|
||||
keyringController.configManager.setWallet('something')
|
||||
const state = keyringController.getState()
|
||||
assert(state.isInitialized, 'old vault counted as initialized.')
|
||||
})
|
||||
|
||||
/*
|
||||
it('should use the password to migrate the old vault', function(done) {
|
||||
this.timeout(5000)
|
||||
console.log('calling submitPassword')
|
||||
console.dir(keyringController)
|
||||
keyringController.submitPassword(password, function (err, state) {
|
||||
assert.ifError(err, 'submitPassword threw error')
|
||||
|
||||
function log(str, dat) { console.log(str + ': ' + JSON.stringify(dat)) }
|
||||
|
||||
let newAccounts = keyringController.getAccounts()
|
||||
log('new accounts: ', newAccounts)
|
||||
|
||||
let newAccount = ethUtil.addHexPrefix(newAccounts[0])
|
||||
assert.equal(ethUtil.addHexPrefix(newAccount), mockVault.account, 'restored the correct account')
|
||||
const newSeed = keyringController.keyrings[0].mnemonic
|
||||
log('keyringController keyrings', keyringController.keyrings)
|
||||
assert.equal(newSeed, mockVault.seed, 'seed phrase transferred.')
|
||||
|
||||
assert(configManager.getVault(), 'new type of vault is persisted')
|
||||
done()
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
}
|
172
test/unit/keyring-controller-test.js
Normal file
172
test/unit/keyring-controller-test.js
Normal file
@ -0,0 +1,172 @@
|
||||
var assert = require('assert')
|
||||
var KeyringController = require('../../app/scripts/keyring-controller')
|
||||
var 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 entropy = 'entripppppyy duuude'
|
||||
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(),
|
||||
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, null, function (err, newState) {
|
||||
assert.ifError(err)
|
||||
state = newState
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function() {
|
||||
// Cleanup mocks
|
||||
this.sinon.restore()
|
||||
})
|
||||
|
||||
describe('#createNewVaultAndKeychain', function () {
|
||||
this.timeout(10000)
|
||||
|
||||
it('should set a vault on the configManager', function(done) {
|
||||
keyringController.configManager.setVault(null)
|
||||
assert(!keyringController.configManager.getVault(), 'no previous vault')
|
||||
keyringController.createNewVaultAndKeychain(password, null, (err, state) => {
|
||||
assert.ifError(err)
|
||||
const vault = keyringController.configManager.getVault()
|
||||
assert(vault, 'vault created')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('#restoreKeyring', function() {
|
||||
|
||||
it(`should pass a keyring's serialized data back to the correct type.`, function() {
|
||||
const mockSerialized = {
|
||||
type: 'HD Key Tree',
|
||||
data: {
|
||||
mnemonic: seedWords,
|
||||
numberOfAccounts: 1,
|
||||
}
|
||||
}
|
||||
const mock = this.sinon.mock(keyringController)
|
||||
|
||||
mock.expects('getBalanceAndNickname')
|
||||
.exactly(1)
|
||||
|
||||
var keyring = keyringController.restoreKeyring(mockSerialized)
|
||||
assert.equal(keyring.wallets.length, 1, 'one wallet restored')
|
||||
assert.equal(keyring.getAccounts()[0], addresses[0])
|
||||
mock.verify()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('#migrateAndGetKey', function() {
|
||||
it('should return the key for that password', function(done) {
|
||||
keyringController.migrateAndGetKey(password)
|
||||
.then((key) => {
|
||||
assert(key, 'a key is returned')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createNickname', function() {
|
||||
it('should add the address to the identities hash', function() {
|
||||
const fakeAddress = '0x12345678'
|
||||
keyringController.createNickname(fakeAddress)
|
||||
const identities = keyringController.identities
|
||||
const identity = identities[fakeAddress]
|
||||
assert.equal(identity.address, fakeAddress)
|
||||
|
||||
const nick = keyringController.configManager.nicknameForWallet(fakeAddress)
|
||||
assert.equal(typeof nick, 'string')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#saveAccountLabel', function() {
|
||||
it ('sets the nickname', function() {
|
||||
const account = addresses[0]
|
||||
var nick = 'Test nickname'
|
||||
keyringController.identities[ethUtil.addHexPrefix(account)] = {}
|
||||
const label = keyringController.saveAccountLabel(account, nick)
|
||||
assert.equal(label, nick)
|
||||
|
||||
const persisted = keyringController.configManager.nicknameForWallet(account)
|
||||
assert.equal(persisted, nick)
|
||||
})
|
||||
|
||||
this.timeout(10000)
|
||||
it('retrieves the persisted nickname', function(done) {
|
||||
const account = addresses[0]
|
||||
var nick = 'Test nickname'
|
||||
keyringController.configManager.setNicknameForWallet(account, nick)
|
||||
console.log('calling to restore')
|
||||
keyringController.createNewVaultAndRestore(password, seedWords, (err, state) => {
|
||||
console.dir({err})
|
||||
assert.ifError(err)
|
||||
|
||||
const identity = keyringController.identities['0x' + account]
|
||||
assert.equal(identity.name, nick)
|
||||
|
||||
assert(accounts)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getAccounts', function() {
|
||||
it('returns the result of getAccounts for each keyring', function() {
|
||||
keyringController.keyrings = [
|
||||
{ getAccounts() { return [1,2,3] } },
|
||||
{ getAccounts() { return [4,5,6] } },
|
||||
]
|
||||
|
||||
const result = keyringController.getAccounts()
|
||||
assert.deepEqual(result, [1,2,3,4,5,6])
|
||||
})
|
||||
})
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
108
test/unit/keyrings/hd-test.js
Normal file
108
test/unit/keyrings/hd-test.js
Normal file
@ -0,0 +1,108 @@
|
||||
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() {
|
||||
keyring = new HdKeyring({
|
||||
mnemonic: sampleMnemonic,
|
||||
numberOfAccounts: 2,
|
||||
})
|
||||
|
||||
const accounts = keyring.getAccounts()
|
||||
assert.equal(accounts[0], firstAcct)
|
||||
assert.equal(accounts[1], secondAcct)
|
||||
})
|
||||
|
||||
describe('Keyring.type()', function() {
|
||||
it('is a class method 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() {
|
||||
const output = keyring.serialize()
|
||||
assert.equal(output.numberOfAccounts, 0)
|
||||
assert.equal(output.mnemonic, null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#deserialize a private key', function() {
|
||||
it('serializes what it deserializes', function() {
|
||||
keyring.deserialize({
|
||||
mnemonic: sampleMnemonic,
|
||||
numberOfAccounts: 1
|
||||
})
|
||||
assert.equal(keyring.wallets.length, 1, 'restores two accounts')
|
||||
keyring.addAccounts(1)
|
||||
|
||||
const accounts = keyring.getAccounts()
|
||||
assert.equal(accounts[0], firstAcct)
|
||||
assert.equal(accounts[1], secondAcct)
|
||||
assert.equal(accounts.length, 2)
|
||||
|
||||
const serialized = keyring.serialize()
|
||||
assert.equal(serialized.mnemonic, sampleMnemonic)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#addAccounts', function() {
|
||||
describe('with no arguments', function() {
|
||||
it('creates a single wallet', function() {
|
||||
keyring.addAccounts()
|
||||
assert.equal(keyring.wallets.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a numeric argument', function() {
|
||||
it('creates that number of wallets', function() {
|
||||
keyring.addAccounts(3)
|
||||
assert.equal(keyring.wallets.length, 3)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getAccounts', function() {
|
||||
it('calls getAddress on each wallet', function() {
|
||||
|
||||
// Push a mock wallet
|
||||
const desiredOutput = 'foo'
|
||||
keyring.wallets.push({
|
||||
getAddress() {
|
||||
return {
|
||||
toString() {
|
||||
return desiredOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const output = keyring.getAccounts()
|
||||
assert.equal(output[0], desiredOutput)
|
||||
assert.equal(output.length, 1)
|
||||
})
|
||||
})
|
||||
})
|
83
test/unit/keyrings/simple-test.js
Normal file
83
test/unit/keyrings/simple-test.js
Normal file
@ -0,0 +1,83 @@
|
||||
const assert = require('assert')
|
||||
const extend = require('xtend')
|
||||
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 method 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() {
|
||||
const output = keyring.serialize()
|
||||
assert.deepEqual(output, [])
|
||||
})
|
||||
})
|
||||
|
||||
describe('#deserialize a private key', function() {
|
||||
it('serializes what it deserializes', function() {
|
||||
keyring.deserialize([privKeyHex])
|
||||
assert.equal(keyring.wallets.length, 1, 'has one wallet')
|
||||
|
||||
const serialized = keyring.serialize()
|
||||
assert.equal(serialized[0], privKeyHex)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#addAccounts', function() {
|
||||
describe('with no arguments', function() {
|
||||
it('creates a single wallet', function() {
|
||||
keyring.addAccounts()
|
||||
assert.equal(keyring.wallets.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a numeric argument', function() {
|
||||
it('creates that number of wallets', function() {
|
||||
keyring.addAccounts(3)
|
||||
assert.equal(keyring.wallets.length, 3)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getAccounts', function() {
|
||||
it('calls getAddress on each wallet', function() {
|
||||
|
||||
// Push a mock wallet
|
||||
const desiredOutput = 'foo'
|
||||
keyring.wallets.push({
|
||||
getAddress() {
|
||||
return {
|
||||
toString() {
|
||||
return desiredOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const output = keyring.getAccounts()
|
||||
assert.equal(output[0], desiredOutput)
|
||||
assert.equal(output.length, 1)
|
||||
})
|
||||
})
|
||||
})
|
@ -6,4 +6,5 @@ launch_in_ci:
|
||||
- Firefox
|
||||
framework:
|
||||
- qunit
|
||||
before_tests: "npm run buildCiUnits"
|
||||
test_page: "test/integration/index.html"
|
||||
|
@ -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)
|
||||
|
@ -30,7 +30,6 @@ function mapStateToProps (state) {
|
||||
network: state.metamask.network,
|
||||
unconfTxs: valuesFor(state.metamask.unconfTxs),
|
||||
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
|
||||
isEthWarningConfirmed: state.metamask.isEthConfirmed,
|
||||
shapeShiftTxList: state.metamask.shapeShiftTxList,
|
||||
}
|
||||
}
|
||||
|
@ -34,11 +34,7 @@ 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),
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
h('.accounts-section.flex-grow', [
|
||||
@ -46,7 +42,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'),
|
||||
]),
|
||||
@ -87,7 +83,7 @@ AccountsScreen.prototype.render = function () {
|
||||
h('div.footer.hover-white.pointer', {
|
||||
key: 'reveal-account-bar',
|
||||
onClick: () => {
|
||||
this.onRevealAccount()
|
||||
this.addNewAccount()
|
||||
},
|
||||
style: {
|
||||
display: 'flex',
|
||||
@ -146,8 +142,8 @@ AccountsScreen.prototype.onShowDetail = function (address, event) {
|
||||
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))
|
||||
}
|
||||
|
||||
AccountsScreen.prototype.goHome = function () {
|
||||
|
@ -1,4 +1,6 @@
|
||||
var actions = {
|
||||
_setBackgroundConnection: _setBackgroundConnection,
|
||||
|
||||
GO_HOME: 'GO_HOME',
|
||||
goHome: goHome,
|
||||
// menu state
|
||||
@ -16,17 +18,16 @@ var actions = {
|
||||
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,
|
||||
unlockMetamask: unlockMetamask,
|
||||
unlockFailed: unlockFailed,
|
||||
showCreateVault: showCreateVault,
|
||||
showRestoreVault: showRestoreVault,
|
||||
showInitializeMenu: showInitializeMenu,
|
||||
createNewVault: createNewVault,
|
||||
createNewVaultAndKeychain: createNewVaultAndKeychain,
|
||||
createNewVaultAndRestore: createNewVaultAndRestore,
|
||||
createNewVaultInProgress: createNewVaultInProgress,
|
||||
addNewKeyring,
|
||||
addNewAccount,
|
||||
showNewVaultSeed: showNewVaultSeed,
|
||||
showInfoPage: showInfoPage,
|
||||
// seed recovery actions
|
||||
@ -52,8 +53,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
|
||||
@ -67,10 +66,6 @@ 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',
|
||||
@ -89,12 +84,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',
|
||||
@ -104,8 +99,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',
|
||||
@ -142,13 +135,18 @@ var actions = {
|
||||
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,
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = actions
|
||||
|
||||
var _accountManager = null
|
||||
function _setAccountManager (accountManager) {
|
||||
_accountManager = accountManager
|
||||
var background = null
|
||||
function _setBackgroundConnection (backgroundConnection) {
|
||||
background = backgroundConnection
|
||||
}
|
||||
|
||||
function goHome () {
|
||||
@ -163,25 +161,52 @@ function tryUnlockMetamask (password) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
dispatch(actions.unlockInProgress())
|
||||
_accountManager.submitPassword(password, (err, selectedAccount) => {
|
||||
background.submitPassword(password, (err, newState) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
dispatch(actions.unlockFailed())
|
||||
dispatch(actions.unlockFailed(err.message))
|
||||
} else {
|
||||
let selectedAccount
|
||||
try {
|
||||
selectedAccount = newState.metamask.selectedAccount
|
||||
} catch (e) {}
|
||||
dispatch(actions.unlockMetamask(selectedAccount))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function createNewVault (password, entropy) {
|
||||
function confirmSeedWords () {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.createNewVaultInProgress())
|
||||
_accountManager.createNewVault(password, entropy, (err, result) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
background.clearSeedWordCache((err, account) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(actions.displayWarning(err.message))
|
||||
}
|
||||
dispatch(actions.showNewVaultSeed(result))
|
||||
|
||||
console.log('Seed word cache cleared. ' + account)
|
||||
dispatch(actions.showAccountDetail(account))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function createNewVaultAndRestore (password, seed) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
background.createNewVaultAndRestore(password, seed, (err) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function createNewVaultAndKeychain (password, entropy) {
|
||||
return (dispatch) => {
|
||||
background.createNewVaultAndKeychain(password, entropy, (err) => {
|
||||
if (err) {
|
||||
return dispatch(actions.showWarning(err.message))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -195,27 +220,35 @@ function revealSeedConfirmation () {
|
||||
function requestRevealSeed (password) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
_accountManager.tryPassword(password, (err, seed) => {
|
||||
background.submitPassword(password, (err) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
_accountManager.recoverSeed((err, seed) => {
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
dispatch(actions.showNewVaultSeed(seed))
|
||||
})
|
||||
background.placeSeedWords()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function recoverFromSeed (password, seed) {
|
||||
return (dispatch) => {
|
||||
// dispatch(actions.createNewVaultInProgress())
|
||||
dispatch(actions.showLoadingIndication())
|
||||
_accountManager.recoverFromSeed(password, seed, (err, metamaskState) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
|
||||
var account = Object.keys(metamaskState.identities)[0]
|
||||
dispatch(actions.unlockMetamask(account))
|
||||
function addNewKeyring (type, opts) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
background.addNewKeyring(type, opts, (err) => {
|
||||
dispatch(this.hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(actions.showWarning(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function addNewAccount (ringNumber = 0) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
background.addNewAccount(ringNumber, (err) => {
|
||||
dispatch(this.hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(actions.showWarning(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -228,27 +261,14 @@ function showInfoPage () {
|
||||
|
||||
function setSelectedAddress (address) {
|
||||
return (dispatch) => {
|
||||
_accountManager.setSelectedAddress(address)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
})
|
||||
background.setSelectedAddress(address)
|
||||
}
|
||||
}
|
||||
|
||||
function setCurrentFiat (fiat) {
|
||||
return (dispatch) => {
|
||||
dispatch(this.showLoadingIndication())
|
||||
_accountManager.setCurrentFiat(fiat, (data, err) => {
|
||||
background.setCurrentFiat(fiat, (data, err) => {
|
||||
dispatch(this.hideLoadingIndication())
|
||||
dispatch({
|
||||
type: this.SET_CURRENT_FIAT,
|
||||
@ -266,7 +286,7 @@ function signMsg (msgData) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
|
||||
_accountManager.signMessage(msgData, (err) => {
|
||||
background.signMessage(msgData, (err) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
@ -277,7 +297,7 @@ function signMsg (msgData) {
|
||||
|
||||
function signTx (txData) {
|
||||
return (dispatch) => {
|
||||
_accountManager.setGasMultiplier(txData.gasMultiplier, (err) => {
|
||||
background.setGasMultiplier(txData.gasMultiplier, (err) => {
|
||||
if (err) return dispatch(actions.displayWarning(err.message))
|
||||
web3.eth.sendTransaction(txData, (err, data) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
@ -292,7 +312,7 @@ function signTx (txData) {
|
||||
|
||||
function sendTx (txData) {
|
||||
return (dispatch) => {
|
||||
_accountManager.approveTransaction(txData.id, (err) => {
|
||||
background.approveTransaction(txData.id, (err) => {
|
||||
if (err) {
|
||||
alert(err.message)
|
||||
dispatch(actions.txError(err))
|
||||
@ -318,12 +338,12 @@ function txError (err) {
|
||||
}
|
||||
|
||||
function cancelMsg (msgData) {
|
||||
_accountManager.cancelMessage(msgData.id)
|
||||
background.cancelMessage(msgData.id)
|
||||
return actions.completedTx(msgData.id)
|
||||
}
|
||||
|
||||
function cancelTx (txData) {
|
||||
_accountManager.cancelTransaction(txData.id)
|
||||
background.cancelTransaction(txData.id)
|
||||
return actions.completedTx(txData.id)
|
||||
}
|
||||
|
||||
@ -352,7 +372,7 @@ function showInitializeMenu () {
|
||||
function agreeToDisclaimer () {
|
||||
return (dispatch) => {
|
||||
dispatch(this.showLoadingIndication())
|
||||
_accountManager.agreeToDisclaimer((err) => {
|
||||
background.agreeToDisclaimer((err) => {
|
||||
if (err) {
|
||||
return dispatch(actions.displayWarning(err.message))
|
||||
}
|
||||
@ -384,6 +404,12 @@ function backToUnlockView () {
|
||||
}
|
||||
}
|
||||
|
||||
function showNewKeychain () {
|
||||
return {
|
||||
type: actions.SHOW_NEW_KEYCHAIN,
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// unlock screen
|
||||
//
|
||||
@ -394,9 +420,10 @@ function unlockInProgress () {
|
||||
}
|
||||
}
|
||||
|
||||
function unlockFailed () {
|
||||
function unlockFailed (message) {
|
||||
return {
|
||||
type: actions.UNLOCK_FAILED,
|
||||
value: message,
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,15 +443,11 @@ function updateMetamaskState (newState) {
|
||||
|
||||
function lockMetamask () {
|
||||
return (dispatch) => {
|
||||
_accountManager.setLocked((err) => {
|
||||
background.setLocked((err) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(actions.displayWarning(err.message))
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: actions.LOCK_METAMASK,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -432,7 +455,7 @@ function lockMetamask () {
|
||||
function showAccountDetail (address) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
_accountManager.setSelectedAddress(address, (err, address) => {
|
||||
background.setSelectedAddress(address, (err, address) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(actions.displayWarning(err.message))
|
||||
@ -452,27 +475,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.displayWarning(err.message))
|
||||
}
|
||||
|
||||
console.log('Seed word cache cleared. ' + account)
|
||||
dispatch(actions.showAccountDetail(account))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function showAccountsPage () {
|
||||
return {
|
||||
@ -524,7 +526,7 @@ function goBackToInitView () {
|
||||
//
|
||||
|
||||
function setRpcTarget (newRpc) {
|
||||
_accountManager.setRpcTarget(newRpc)
|
||||
background.setRpcTarget(newRpc)
|
||||
return {
|
||||
type: actions.SET_RPC_TARGET,
|
||||
value: newRpc,
|
||||
@ -532,7 +534,7 @@ function setRpcTarget (newRpc) {
|
||||
}
|
||||
|
||||
function setProviderType (type) {
|
||||
_accountManager.setProviderType(type)
|
||||
background.setProviderType(type)
|
||||
return {
|
||||
type: actions.SET_PROVIDER_TYPE,
|
||||
value: type,
|
||||
@ -540,7 +542,7 @@ function setProviderType (type) {
|
||||
}
|
||||
|
||||
function useEtherscanProvider () {
|
||||
_accountManager.useEtherscanProvider()
|
||||
background.useEtherscanProvider()
|
||||
return {
|
||||
type: actions.USE_ETHERSCAN_PROVIDER,
|
||||
}
|
||||
@ -595,7 +597,7 @@ function exportAccount (address) {
|
||||
return function (dispatch) {
|
||||
dispatch(self.showLoadingIndication())
|
||||
|
||||
_accountManager.exportAccount(address, function (err, result) {
|
||||
background.exportAccount(address, function (err, result) {
|
||||
dispatch(self.hideLoadingIndication())
|
||||
|
||||
if (err) {
|
||||
@ -618,7 +620,7 @@ function showPrivateKey (key) {
|
||||
function saveAccountLabel (account, label) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
_accountManager.saveAccountLabel(account, label, (err) => {
|
||||
background.saveAccountLabel(account, label, (err) => {
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
if (err) {
|
||||
return dispatch(actions.displayWarning(err.message))
|
||||
@ -637,28 +639,9 @@ 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)
|
||||
background.buyEth(address, amount)
|
||||
dispatch({
|
||||
type: actions.BUY_ETH,
|
||||
})
|
||||
@ -736,7 +719,7 @@ function coinShiftRquest (data, marketData) {
|
||||
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)
|
||||
background.createShapeShiftTx(response.deposit, response.depositType)
|
||||
dispatch(actions.showQrView(response.deposit, [message].concat(marketData)))
|
||||
})
|
||||
}
|
||||
|
@ -7,9 +7,7 @@ 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
|
||||
@ -19,7 +17,6 @@ const SendTransactionScreen = require('./send')
|
||||
const ConfirmTxScreen = require('./conf-tx')
|
||||
// other views
|
||||
const ConfigScreen = require('./config')
|
||||
const RevealSeedConfirmation = require('./recover-seed/confirmation')
|
||||
const InfoScreen = require('./info')
|
||||
const LoadingIndicator = require('./components/loading')
|
||||
const SandwichExpando = require('sandwich-expando')
|
||||
@ -27,9 +24,12 @@ 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)
|
||||
@ -39,8 +39,7 @@ function mapStateToProps (state) {
|
||||
return {
|
||||
// state from plugin
|
||||
isLoading: state.appState.isLoading,
|
||||
isConfirmed: state.metamask.isConfirmed,
|
||||
isEthConfirmed: state.metamask.isEthConfirmed,
|
||||
isDisclaimerConfirmed: state.metamask.isDisclaimerConfirmed,
|
||||
isInitialized: state.metamask.isInitialized,
|
||||
isUnlocked: state.metamask.isUnlocked,
|
||||
currentView: state.appState.currentView,
|
||||
@ -99,7 +98,6 @@ App.prototype.render = function () {
|
||||
}
|
||||
|
||||
App.prototype.renderAppBar = function () {
|
||||
|
||||
if (window.METAMASK_UI_TYPE === 'notification') {
|
||||
return null
|
||||
}
|
||||
@ -311,6 +309,7 @@ App.prototype.renderDropdown = function () {
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
App.prototype.renderBackButton = function (style, justArrow = false) {
|
||||
var props = this.props
|
||||
return (
|
||||
@ -328,12 +327,13 @@ App.prototype.renderBackButton = function (style, justArrow = false) {
|
||||
}, 'BACK'),
|
||||
])
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
App.prototype.renderBackToInitButton = function () {
|
||||
var props = this.props
|
||||
var button = null
|
||||
if (!props.isConfirmed) return button
|
||||
if (!props.isDisclaimerConfirmed) return button
|
||||
|
||||
if (!props.isUnlocked) {
|
||||
if (props.currentView.name === 'InitMenu') {
|
||||
button = props.forgottenPassword ? h('.flex-row', {
|
||||
@ -357,42 +357,6 @@ App.prototype.renderBackToInitButton = function () {
|
||||
}, 'LOGIN'),
|
||||
h('i.fa.fa-arrow-right.cursor-pointer'),
|
||||
]) : null
|
||||
} else if (props.isInitialized) {
|
||||
var style
|
||||
switch (props.currentView.name) {
|
||||
case 'createVault':
|
||||
style = {
|
||||
position: 'absolute',
|
||||
top: '41px',
|
||||
left: '80px',
|
||||
fontSize: '21px',
|
||||
fontFamily: 'Montserrat Bold',
|
||||
color: 'rgb(174, 174, 174)',
|
||||
}
|
||||
return this.renderBackButton(style, true)
|
||||
case 'restoreVault':
|
||||
style = {
|
||||
position: 'absolute',
|
||||
top: '41px',
|
||||
left: '70px',
|
||||
fontSize: '21px',
|
||||
fontFamily: 'Montserrat Bold',
|
||||
color: 'rgb(174, 174, 174)',
|
||||
}
|
||||
return this.renderBackButton(style, true)
|
||||
default:
|
||||
style = {
|
||||
position: 'absolute',
|
||||
bottom: '10px',
|
||||
left: '15px',
|
||||
fontSize: '21px',
|
||||
fontFamily: 'Montserrat Light',
|
||||
color: '#7F8082',
|
||||
width: '71.969px',
|
||||
alignItems: 'flex-end',
|
||||
}
|
||||
return this.renderBackButton(style)
|
||||
}
|
||||
}
|
||||
}
|
||||
return button
|
||||
@ -401,12 +365,12 @@ App.prototype.renderBackToInitButton = function () {
|
||||
App.prototype.renderPrimary = function () {
|
||||
var props = this.props
|
||||
|
||||
if (!props.isConfirmed) {
|
||||
if (!props.isDisclaimerConfirmed) {
|
||||
return h(DisclaimerScreen, {key: 'disclaimerScreen'})
|
||||
}
|
||||
|
||||
if (props.seedWords) {
|
||||
return h(CreateVaultCompleteScreen, {key: 'createVaultComplete'})
|
||||
return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
|
||||
}
|
||||
|
||||
// show initialize screen
|
||||
@ -414,30 +378,28 @@ App.prototype.renderPrimary = function () {
|
||||
// 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'})
|
||||
@ -448,6 +410,9 @@ 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'})
|
||||
|
||||
@ -460,10 +425,9 @@ App.prototype.renderPrimary = function () {
|
||||
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: {
|
||||
@ -519,12 +483,7 @@ App.prototype.renderCustomOption = function (rpcTarget) {
|
||||
})
|
||||
|
||||
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'),
|
||||
})
|
||||
return null
|
||||
|
||||
default:
|
||||
return h(DropMenuItem, {
|
||||
|
@ -7,7 +7,7 @@ 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,
|
||||
@ -16,7 +16,7 @@ function mapStateToProps(state) {
|
||||
|
||||
inherits(CoinbaseForm, Component)
|
||||
|
||||
function CoinbaseForm() {
|
||||
function CoinbaseForm () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
@ -124,7 +124,6 @@ CoinbaseForm.prototype.toCoinbase = function () {
|
||||
}
|
||||
|
||||
CoinbaseForm.prototype.renderLoading = function () {
|
||||
|
||||
return h('img', {
|
||||
style: {
|
||||
width: '27px',
|
||||
@ -134,9 +133,8 @@ CoinbaseForm.prototype.renderLoading = function () {
|
||||
})
|
||||
}
|
||||
|
||||
function isValidAmountforCoinBase(amount) {
|
||||
function isValidAmountforCoinBase (amount) {
|
||||
amount = parseFloat(amount)
|
||||
|
||||
if (amount) {
|
||||
if (amount <= 5 && amount > 0) {
|
||||
return {
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ 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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,7 +31,6 @@ Network.prototype.render = function () {
|
||||
},
|
||||
src: 'images/loading.svg',
|
||||
})
|
||||
|
||||
} else if (providerName === 'mainnet') {
|
||||
hoverText = 'Main Ethereum Network'
|
||||
iconName = 'ethereum-network'
|
||||
|
@ -8,7 +8,7 @@ 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,
|
||||
@ -25,7 +25,6 @@ function ShapeshiftForm () {
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.render = function () {
|
||||
|
||||
return h(ReactCSSTransitionGroup, {
|
||||
className: 'css-transition-group',
|
||||
transitionName: 'main',
|
||||
@ -34,7 +33,6 @@ ShapeshiftForm.prototype.render = function () {
|
||||
}, [
|
||||
this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain(),
|
||||
])
|
||||
|
||||
}
|
||||
|
||||
ShapeshiftForm.prototype.renderMain = function () {
|
||||
|
@ -26,7 +26,6 @@ function ShiftListItem () {
|
||||
}
|
||||
|
||||
ShiftListItem.prototype.render = function () {
|
||||
|
||||
return (
|
||||
h('.transaction-list-item.flex-row', {
|
||||
style: {
|
||||
|
@ -11,7 +11,6 @@ function Tooltip () {
|
||||
}
|
||||
|
||||
Tooltip.prototype.render = function () {
|
||||
|
||||
const props = this.props
|
||||
const { position, title, children } = props
|
||||
|
||||
@ -20,5 +19,4 @@ Tooltip.prototype.render = function () {
|
||||
title,
|
||||
fixed: false,
|
||||
}, children)
|
||||
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ TransactionListItem.prototype.render = function () {
|
||||
style: {
|
||||
fontSize: '27px',
|
||||
},
|
||||
}) : h( '.pop-hover', {
|
||||
}) : h('.pop-hover', {
|
||||
onClick: (event) => {
|
||||
event.stopPropagation()
|
||||
if (!isTx || isPending) return
|
||||
|
@ -3,7 +3,7 @@ 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
|
||||
module.exports = connect(mapStateToProps)(ConfigScreen)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
|
File diff suppressed because one or more lines are too long
5730
ui/app/conversion.json
Normal file
5730
ui/app/conversion.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,89 +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',
|
||||
},
|
||||
},
|
||||
`MetaMask is currently in beta; use
|
||||
caution in storing large
|
||||
amounts of ether.
|
||||
`),
|
||||
|
||||
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))
|
||||
}
|
@ -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*/))
|
||||
}
|
@ -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,49 +50,131 @@ 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),
|
||||
style: {
|
||||
margin: 12,
|
||||
},
|
||||
}, 'Create New Vault'),
|
||||
|
||||
h('.flex-row.flex-center.flex-grow', [
|
||||
h('hr'),
|
||||
h('div', 'OR'),
|
||||
h('hr'),
|
||||
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: {
|
||||
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,
|
||||
},
|
||||
}),
|
||||
|
||||
|
||||
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',
|
||||
},
|
||||
}, 'I already have a DEN that I would like to import'),
|
||||
]),
|
||||
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.createNewVaultAndKeychain(password, ''/* entropy*/))
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
@ -71,4 +71,3 @@ CreateVaultCompleteScreen.prototype.render = function () {
|
||||
CreateVaultCompleteScreen.prototype.confirmSeedWords = function () {
|
||||
this.props.dispatch(actions.confirmSeedWords())
|
||||
}
|
||||
|
@ -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')
|
@ -1,8 +1,8 @@
|
||||
const inherits = require('util').inherits
|
||||
const PersistentForm = require('../../lib/persistent-form')
|
||||
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)
|
||||
|
||||
@ -66,7 +66,7 @@ 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',
|
||||
},
|
||||
@ -96,7 +96,7 @@ RestoreVaultScreen.prototype.render = function () {
|
||||
|
||||
// submit
|
||||
h('button.primary', {
|
||||
onClick: this.restoreVault.bind(this),
|
||||
onClick: this.createNewVaultAndRestore.bind(this),
|
||||
}, 'OK'),
|
||||
|
||||
]),
|
||||
@ -110,13 +110,13 @@ RestoreVaultScreen.prototype.showInitializeMenu = function () {
|
||||
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
|
||||
@ -144,5 +144,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))
|
||||
}
|
29
ui/app/new-keychain.js
Normal file
29
ui/app/new-keychain.js
Normal file
@ -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!!!!`),
|
||||
])
|
||||
)
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -29,13 +29,10 @@ 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',
|
||||
},
|
||||
@ -119,6 +116,15 @@ 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:
|
||||
@ -272,7 +278,6 @@ function reduceApp (state, action) {
|
||||
warning: null,
|
||||
})
|
||||
} else {
|
||||
|
||||
notification.closePopup()
|
||||
|
||||
return extend(appState, {
|
||||
@ -329,7 +334,7 @@ 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:
|
||||
@ -540,4 +545,3 @@ function indexForPending (state, txId) {
|
||||
})
|
||||
return idx
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@ function reduceMetamask (state, action) {
|
||||
var metamaskState = extend({
|
||||
isInitialized: false,
|
||||
isUnlocked: false,
|
||||
isEthConfirmed: false,
|
||||
rpcTarget: 'https://rawtestrpc.metamask.io/',
|
||||
identities: {},
|
||||
unconfTxs: {},
|
||||
@ -31,12 +30,7 @@ function reduceMetamask (state, action) {
|
||||
|
||||
case actions.AGREE_TO_DISCLAIMER:
|
||||
return extend(metamaskState, {
|
||||
isConfirmed: true,
|
||||
})
|
||||
|
||||
case actions.AGREE_TO_ETH_WARNING:
|
||||
return extend(metamaskState, {
|
||||
isEthConfirmed: !metamaskState.isEthConfirmed,
|
||||
isDisclaimerConfirmed: true,
|
||||
})
|
||||
|
||||
case actions.UNLOCK_METAMASK:
|
||||
|
@ -55,6 +55,8 @@ UnlockScreen.prototype.render = function () {
|
||||
h('.error', {
|
||||
style: {
|
||||
display: warning ? 'block' : 'none',
|
||||
padding: '0 20px',
|
||||
textAlign: 'center',
|
||||
},
|
||||
}, warning),
|
||||
|
||||
@ -65,6 +67,17 @@ UnlockScreen.prototype.render = function () {
|
||||
},
|
||||
}, 'Unlock'),
|
||||
]),
|
||||
|
||||
h('.flex-row.flex-center.flex-grow', [
|
||||
h('p.pointer', {
|
||||
onClick: () => this.props.dispatch(actions.showRestoreVault()),
|
||||
style: {
|
||||
fontSize: '0.8em',
|
||||
color: 'rgb(247, 134, 28)',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}, 'I forgot my password.'),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
@ -104,6 +117,3 @@ UnlockScreen.prototype.inputChanged = function (event) {
|
||||
})
|
||||
}
|
||||
|
||||
UnlockScreen.prototype.emitAnim = function (name, a, b, c) {
|
||||
this.animationEventEmitter.emit(name, a, b, c)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ 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) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
module.exports = function(address, network) {
|
||||
module.exports = function (address, network) {
|
||||
const net = parseInt(network)
|
||||
let link
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user