commit
e3f6c010ab
|
@ -2,8 +2,13 @@
|
||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
## 3.4.0 2017-3-8
|
||||||
|
|
||||||
|
- Add two most recently used custom RPCs to network dropdown menu.
|
||||||
- Add personal_sign method support.
|
- Add personal_sign method support.
|
||||||
|
- Add personal_ecRecover method support.
|
||||||
- Add ability to customize gas and gasPrice on the transaction approval screen.
|
- Add ability to customize gas and gasPrice on the transaction approval screen.
|
||||||
|
- Increase default gas buffer to 1.5x estimated gas value.
|
||||||
|
|
||||||
## 3.3.0 2017-2-20
|
## 3.3.0 2017-2-20
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "MetaMask",
|
"name": "MetaMask",
|
||||||
"short_name": "Metamask",
|
"short_name": "Metamask",
|
||||||
"version": "3.3.0",
|
"version": "3.4.0",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"author": "https://metamask.io",
|
"author": "https://metamask.io",
|
||||||
"description": "Ethereum Browser Extension",
|
"description": "Ethereum Browser Extension",
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
const ObservableStore = require('obs-store')
|
||||||
|
const normalizeAddress = require('eth-sig-util').normalize
|
||||||
|
const extend = require('xtend')
|
||||||
|
|
||||||
|
class PreferencesController {
|
||||||
|
|
||||||
|
constructor (opts = {}) {
|
||||||
|
const initState = extend({ frequentRpcList: [] }, opts.initState)
|
||||||
|
this.store = new ObservableStore(initState)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PUBLIC METHODS
|
||||||
|
//
|
||||||
|
|
||||||
|
setSelectedAddress (_address) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const address = normalizeAddress(_address)
|
||||||
|
this.store.updateState({ selectedAddress: address })
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedAddress (_address) {
|
||||||
|
return this.store.getState().selectedAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFrequentRpcList (_url) {
|
||||||
|
return this.addToFrequentRpcList(_url)
|
||||||
|
.then((rpcList) => {
|
||||||
|
this.store.updateState({ frequentRpcList: rpcList })
|
||||||
|
return Promise.resolve()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
addToFrequentRpcList (_url) {
|
||||||
|
let rpcList = this.getFrequentRpcList()
|
||||||
|
let index = rpcList.findIndex((element) => { return element === _url })
|
||||||
|
if (index !== -1) {
|
||||||
|
rpcList.splice(index, 1)
|
||||||
|
}
|
||||||
|
if (_url !== 'http://localhost:8545') {
|
||||||
|
rpcList.push(_url)
|
||||||
|
}
|
||||||
|
if (rpcList.length > 2) {
|
||||||
|
rpcList.shift()
|
||||||
|
}
|
||||||
|
return Promise.resolve(rpcList)
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrequentRpcList () {
|
||||||
|
return this.store.getState().frequentRpcList
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PRIVATE METHODS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PreferencesController
|
|
@ -164,8 +164,11 @@ class KeyringController extends EventEmitter {
|
||||||
return keyring.getAccounts()
|
return keyring.getAccounts()
|
||||||
})
|
})
|
||||||
.then((accounts) => {
|
.then((accounts) => {
|
||||||
|
return this.checkForDuplicate(type, accounts)
|
||||||
|
})
|
||||||
|
.then((checkedAccounts) => {
|
||||||
this.keyrings.push(keyring)
|
this.keyrings.push(keyring)
|
||||||
return this.setupAccounts(accounts)
|
return this.setupAccounts(checkedAccounts)
|
||||||
})
|
})
|
||||||
.then(() => this.persistAllKeyrings())
|
.then(() => this.persistAllKeyrings())
|
||||||
.then(() => this.fullUpdate())
|
.then(() => this.fullUpdate())
|
||||||
|
@ -175,6 +178,24 @@ class KeyringController extends EventEmitter {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For now just checks for simple key pairs
|
||||||
|
// but in the future
|
||||||
|
// should possibly add HD and other types
|
||||||
|
//
|
||||||
|
checkForDuplicate (type, newAccount) {
|
||||||
|
return this.getAccounts()
|
||||||
|
.then((accounts) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'Simple Key Pair':
|
||||||
|
let isNotIncluded = !accounts.find((key) => key === newAccount[0] || key === ethUtil.stripHexPrefix(newAccount[0]))
|
||||||
|
return (isNotIncluded) ? Promise.resolve(newAccount) : Promise.reject(new Error('The account you\'re are trying to import is a duplicate'))
|
||||||
|
default:
|
||||||
|
return Promise.resolve(newAccount)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add New Account
|
// Add New Account
|
||||||
// @number keyRingNum
|
// @number keyRingNum
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
const ObservableStore = require('obs-store')
|
|
||||||
const normalizeAddress = require('eth-sig-util').normalize
|
|
||||||
|
|
||||||
class PreferencesController {
|
|
||||||
|
|
||||||
constructor (opts = {}) {
|
|
||||||
const initState = opts.initState || {}
|
|
||||||
this.store = new ObservableStore(initState)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// PUBLIC METHODS
|
|
||||||
//
|
|
||||||
|
|
||||||
setSelectedAddress(_address) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const address = normalizeAddress(_address)
|
|
||||||
this.store.updateState({ selectedAddress: address })
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getSelectedAddress(_address) {
|
|
||||||
return this.store.getState().selectedAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// PRIVATE METHODS
|
|
||||||
//
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = PreferencesController
|
|
|
@ -2,6 +2,7 @@ const EventEmitter = require('events')
|
||||||
const ObservableStore = require('obs-store')
|
const ObservableStore = require('obs-store')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const createId = require('./random-id')
|
const createId = require('./random-id')
|
||||||
|
const hexRe = /^[0-9A-Fa-f]+$/g
|
||||||
|
|
||||||
|
|
||||||
module.exports = class PersonalMessageManager extends EventEmitter{
|
module.exports = class PersonalMessageManager extends EventEmitter{
|
||||||
|
@ -24,7 +25,8 @@ module.exports = class PersonalMessageManager extends EventEmitter{
|
||||||
}
|
}
|
||||||
|
|
||||||
addUnapprovedMessage (msgParams) {
|
addUnapprovedMessage (msgParams) {
|
||||||
msgParams.data = normalizeMsgData(msgParams.data)
|
log.debug(`PersonalMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
|
||||||
|
msgParams.data = this.normalizeMsgData(msgParams.data)
|
||||||
// create txData obj with parameters and meta data
|
// create txData obj with parameters and meta data
|
||||||
var time = (new Date()).getTime()
|
var time = (new Date()).getTime()
|
||||||
var msgId = createId()
|
var msgId = createId()
|
||||||
|
@ -106,14 +108,18 @@ module.exports = class PersonalMessageManager extends EventEmitter{
|
||||||
this.emit('updateBadge')
|
this.emit('updateBadge')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
normalizeMsgData(data) {
|
||||||
|
try {
|
||||||
|
const stripped = ethUtil.stripHexPrefix(data)
|
||||||
|
if (stripped.match(hexRe)) {
|
||||||
|
return ethUtil.addHexPrefix(stripped)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.debug(`Message was not hex encoded, interpreting as utf8.`)
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeMsgData(data) {
|
|
||||||
if (data.slice(0, 2) === '0x') {
|
|
||||||
// data is already hex
|
|
||||||
return data
|
|
||||||
} else {
|
|
||||||
// data is unicode, convert to hex
|
|
||||||
return ethUtil.bufferToHex(new Buffer(data, 'utf8'))
|
return ethUtil.bufferToHex(new Buffer(data, 'utf8'))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,26 +53,23 @@ module.exports = class txProviderUtils {
|
||||||
}
|
}
|
||||||
// if gasLimit not originally specified,
|
// if gasLimit not originally specified,
|
||||||
// try adding an additional gas buffer to our estimation for safety
|
// try adding an additional gas buffer to our estimation for safety
|
||||||
const estimatedGasBn = new BN(ethUtil.stripHexPrefix(txData.estimatedGas), 16)
|
const recommendedGasHex = this.addGasBuffer(txData.estimatedGas, blockGasLimitHex)
|
||||||
const blockGasLimitBn = new BN(ethUtil.stripHexPrefix(blockGasLimitHex), 16)
|
txParams.gas = recommendedGasHex
|
||||||
const estimationWithBuffer = new BN(this.addGasBuffer(estimatedGasBn), 16)
|
|
||||||
// added gas buffer is too high
|
|
||||||
if (estimationWithBuffer.gt(blockGasLimitBn)) {
|
|
||||||
txParams.gas = txData.estimatedGas
|
|
||||||
// added gas buffer is safe
|
|
||||||
} else {
|
|
||||||
const gasWithBufferHex = ethUtil.intToHex(estimationWithBuffer)
|
|
||||||
txParams.gas = gasWithBufferHex
|
|
||||||
}
|
|
||||||
cb()
|
cb()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
addGasBuffer (gas) {
|
addGasBuffer (initialGasLimitHex, blockGasLimitHex) {
|
||||||
const gasBuffer = new BN('100000', 10)
|
const initialGasLimitBn = hexToBn(initialGasLimitHex)
|
||||||
const bnGas = new BN(ethUtil.stripHexPrefix(gas), 16)
|
const blockGasLimitBn = hexToBn(blockGasLimitHex)
|
||||||
const correct = bnGas.add(gasBuffer)
|
const bufferedGasLimitBn = initialGasLimitBn.muln(1.5)
|
||||||
return ethUtil.addHexPrefix(correct.toString(16))
|
|
||||||
|
// if initialGasLimit is above blockGasLimit, dont modify it
|
||||||
|
if (initialGasLimitBn.gt(blockGasLimitBn)) return bnToHex(initialGasLimitBn)
|
||||||
|
// if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
|
||||||
|
if (bufferedGasLimitBn.lt(blockGasLimitBn)) return bnToHex(bufferedGasLimitBn)
|
||||||
|
// otherwise use blockGasLimit
|
||||||
|
return bnToHex(blockGasLimitBn)
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInTxParams (txParams, cb) {
|
fillInTxParams (txParams, cb) {
|
||||||
|
@ -94,7 +91,7 @@ module.exports = class txProviderUtils {
|
||||||
// builds ethTx from txParams object
|
// builds ethTx from txParams object
|
||||||
buildEthTxFromParams (txParams) {
|
buildEthTxFromParams (txParams) {
|
||||||
// apply gas multiplyer
|
// apply gas multiplyer
|
||||||
let gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice), 16)
|
let gasPrice = hexToBn(txParams.gasPrice)
|
||||||
// multiply and divide by 100 so as to add percision to integer mul
|
// multiply and divide by 100 so as to add percision to integer mul
|
||||||
txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
|
txParams.gasPrice = ethUtil.intToHex(gasPrice.toNumber())
|
||||||
// normalize values
|
// normalize values
|
||||||
|
@ -130,3 +127,11 @@ module.exports = class txProviderUtils {
|
||||||
function isUndef(value) {
|
function isUndef(value) {
|
||||||
return value === undefined
|
return value === undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bnToHex(inputBn) {
|
||||||
|
return ethUtil.addHexPrefix(inputBn.toString(16))
|
||||||
|
}
|
||||||
|
|
||||||
|
function hexToBn(inputHex) {
|
||||||
|
return new BN(ethUtil.stripHexPrefix(inputHex), 16)
|
||||||
|
}
|
|
@ -11,10 +11,10 @@ const streamIntoProvider = require('web3-stream-provider/handler')
|
||||||
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
||||||
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||||
const KeyringController = require('./keyring-controller')
|
const KeyringController = require('./keyring-controller')
|
||||||
const PreferencesController = require('./lib/controllers/preferences')
|
const PreferencesController = require('./controllers/preferences')
|
||||||
const CurrencyController = require('./lib/controllers/currency')
|
const CurrencyController = require('./controllers/currency')
|
||||||
const NoticeController = require('./notice-controller')
|
const NoticeController = require('./notice-controller')
|
||||||
const ShapeShiftController = require('./lib/controllers/shapeshift')
|
const ShapeShiftController = require('./controllers/shapeshift')
|
||||||
const MessageManager = require('./lib/message-manager')
|
const MessageManager = require('./lib/message-manager')
|
||||||
const PersonalMessageManager = require('./lib/personal-message-manager')
|
const PersonalMessageManager = require('./lib/personal-message-manager')
|
||||||
const TxManager = require('./transaction-manager')
|
const TxManager = require('./transaction-manager')
|
||||||
|
@ -244,7 +244,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
return {
|
return {
|
||||||
// etc
|
// etc
|
||||||
getState: (cb) => cb(null, this.getState()),
|
getState: (cb) => cb(null, this.getState()),
|
||||||
setRpcTarget: this.setRpcTarget.bind(this),
|
|
||||||
setProviderType: this.setProviderType.bind(this),
|
setProviderType: this.setProviderType.bind(this),
|
||||||
useEtherscanProvider: this.useEtherscanProvider.bind(this),
|
useEtherscanProvider: this.useEtherscanProvider.bind(this),
|
||||||
setCurrentCurrency: this.setCurrentCurrency.bind(this),
|
setCurrentCurrency: this.setCurrentCurrency.bind(this),
|
||||||
|
@ -265,6 +264,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
|
|
||||||
// PreferencesController
|
// PreferencesController
|
||||||
setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController),
|
setSelectedAddress: nodeify(preferencesController.setSelectedAddress).bind(preferencesController),
|
||||||
|
setDefaultRpc: nodeify(this.setDefaultRpc).bind(this),
|
||||||
|
setCustomRpc: nodeify(this.setCustomRpc).bind(this),
|
||||||
|
|
||||||
// KeyringController
|
// KeyringController
|
||||||
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
|
setLocked: nodeify(keyringController.setLocked).bind(keyringController),
|
||||||
|
@ -414,14 +415,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
self.sendUpdate()
|
self.sendUpdate()
|
||||||
self.opts.showUnapprovedTx(txMeta)
|
self.opts.showUnapprovedTx(txMeta)
|
||||||
// listen for tx completion (success, fail)
|
// listen for tx completion (success, fail)
|
||||||
self.txManager.once(`${txMeta.id}:finished`, (status) => {
|
self.txManager.once(`${txMeta.id}:finished`, (completedTx) => {
|
||||||
switch (status) {
|
switch (completedTx.status) {
|
||||||
case 'submitted':
|
case 'submitted':
|
||||||
return cb(null, txMeta.hash)
|
return cb(null, completedTx.hash)
|
||||||
case 'rejected':
|
case 'rejected':
|
||||||
return cb(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
return cb(new Error('MetaMask Tx Signature: User denied transaction signature.'))
|
||||||
default:
|
default:
|
||||||
return cb(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(txMeta.txParams)}`))
|
return cb(new Error(`MetaMask Tx Signature: Unknown problem: ${JSON.stringify(completedTx.txParams)}`))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -661,10 +662,21 @@ module.exports = class MetamaskController extends EventEmitter {
|
||||||
if (this.isNetworkLoading()) this.lookupNetwork()
|
if (this.isNetworkLoading()) this.lookupNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
setRpcTarget (rpcTarget) {
|
setDefaultRpc () {
|
||||||
this.configManager.setRpcTarget(rpcTarget)
|
this.configManager.setRpcTarget('http://localhost:8545')
|
||||||
extension.runtime.reload()
|
extension.runtime.reload()
|
||||||
this.lookupNetwork()
|
this.lookupNetwork()
|
||||||
|
return Promise.resolve('http://localhost:8545')
|
||||||
|
}
|
||||||
|
|
||||||
|
setCustomRpc (rpcTarget, rpcList) {
|
||||||
|
this.configManager.setRpcTarget(rpcTarget)
|
||||||
|
return this.preferencesController.updateFrequentRpcList(rpcTarget)
|
||||||
|
.then(() => {
|
||||||
|
extension.runtime.reload()
|
||||||
|
this.lookupNetwork()
|
||||||
|
return Promise.resolve(rpcTarget)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setProviderType (type) {
|
setProviderType (type) {
|
||||||
|
|
|
@ -49,12 +49,14 @@ function setupControllerConnection (connectionStream, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupApp (err, accountManager) {
|
function setupApp (err, accountManager) {
|
||||||
|
var container = document.getElementById('app-content')
|
||||||
if (err) {
|
if (err) {
|
||||||
alert(err.stack)
|
container.innerHTML = '<div class="critical-error">The MetaMask app failed to load: please open and close MetaMask again to restart.</div>'
|
||||||
|
container.style.height = '80px'
|
||||||
|
log.error(err.stack)
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
var container = document.getElementById('app-content')
|
|
||||||
|
|
||||||
MetaMaskUi({
|
MetaMaskUi({
|
||||||
container: container,
|
container: container,
|
||||||
|
|
|
@ -353,7 +353,7 @@ module.exports = class TransactionManager extends EventEmitter {
|
||||||
txMeta.status = status
|
txMeta.status = status
|
||||||
this.emit(`${txMeta.id}:${status}`, txId)
|
this.emit(`${txMeta.id}:${status}`, txId)
|
||||||
if (status === 'submitted' || status === 'rejected') {
|
if (status === 'submitted' || status === 'rejected') {
|
||||||
this.emit(`${txMeta.id}:finished`, status)
|
this.emit(`${txMeta.id}:finished`, txMeta)
|
||||||
}
|
}
|
||||||
this.updateTx(txMeta)
|
this.updateTx(txMeta)
|
||||||
this.emit('updateBadge')
|
this.emit('updateBadge')
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
"isUnlocked": false,
|
"isUnlocked": false,
|
||||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
"identities": {},
|
"identities": {},
|
||||||
|
"frequentRpcList": [],
|
||||||
"unapprovedTxs": {},
|
"unapprovedTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentFiat": "USD",
|
||||||
"conversionRate": 12.7527416,
|
"conversionRate": 12.7527416,
|
||||||
|
|
|
@ -2,73 +2,108 @@
|
||||||
"metamask": {
|
"metamask": {
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
"isUnlocked": true,
|
"isUnlocked": true,
|
||||||
"currentDomain": "example.com",
|
|
||||||
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
"identities": {
|
"identities": {
|
||||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
"name": "Wallet 1",
|
|
||||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
"mayBeFauceting": false
|
"name": "Account 1"
|
||||||
},
|
},
|
||||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
"name": "Wallet 2",
|
|
||||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
"mayBeFauceting": false
|
"name": "Account 2"
|
||||||
},
|
},
|
||||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
"name": "Wallet 3",
|
|
||||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
|
||||||
"mayBeFauceting": false
|
"name": "Account 3"
|
||||||
|
},
|
||||||
|
"0xd85a4b6a394794842887b8284293d69163007bbb": {
|
||||||
|
"address": "0xd85a4b6a394794842887b8284293d69163007bbb",
|
||||||
|
"name": "Account 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"unconfTxs": {},
|
"unapprovedTxs": {},
|
||||||
"currentFiat": "USD",
|
"currentFiat": "USD",
|
||||||
"conversionRate": 11.21283484,
|
"conversionRate": 16.88200327,
|
||||||
"conversionDate": 1472158984,
|
"conversionDate": 1489013762,
|
||||||
|
"noActiveNotices": true,
|
||||||
|
"frequentRpcList": [],
|
||||||
|
"network": "3",
|
||||||
"accounts": {
|
"accounts": {
|
||||||
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
"code": "0x",
|
"code": "0x",
|
||||||
"balance": "0x34693f54a1e25900",
|
"balance": "0x47c9d71831c76efe",
|
||||||
"nonce": "0x100013",
|
"nonce": "0x1b",
|
||||||
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
},
|
},
|
||||||
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
"code": "0x",
|
"code": "0x",
|
||||||
"nonce": "0x100000",
|
"balance": "0x37452b1315889f80",
|
||||||
"balance": "0x18af912cee770000",
|
"nonce": "0xa",
|
||||||
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
|
||||||
},
|
},
|
||||||
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
"code": "0x",
|
"code": "0x",
|
||||||
"nonce": "0x100000",
|
"balance": "0x0",
|
||||||
"balance": "0x2386f26fc10000",
|
"nonce": "0x0",
|
||||||
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
|
},
|
||||||
|
"0xd85a4b6a394794842887b8284293d69163007bbb": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0xd85a4b6a394794842887b8284293d69163007bbb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transactions": [],
|
"transactions": {},
|
||||||
"network": "2",
|
"selectedAddressTxList": [],
|
||||||
"seedWords": null,
|
"unapprovedMsgs": {},
|
||||||
"unconfMsgs": {},
|
"unapprovedMsgCount": 0,
|
||||||
"messages": [],
|
"unapprovedPersonalMsgs": {},
|
||||||
"shapeShiftTxList": [],
|
"unapprovedPersonalMsgCount": 0,
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
],
|
||||||
|
"keyrings": [
|
||||||
|
{
|
||||||
|
"type": "HD Key Tree",
|
||||||
|
"accounts": [
|
||||||
|
"fdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"c5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
|
"2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Simple Key Pair",
|
||||||
|
"accounts": [
|
||||||
|
"0xd85a4b6a394794842887b8284293d69163007bbb"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectedAddress": "0xd85a4b6a394794842887b8284293d69163007bbb",
|
||||||
|
"currentCurrency": "USD",
|
||||||
"provider": {
|
"provider": {
|
||||||
"type": "testnet"
|
"type": "testnet"
|
||||||
},
|
},
|
||||||
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
"shapeShiftTxList": [],
|
||||||
|
"lostAccounts": []
|
||||||
},
|
},
|
||||||
"appState": {
|
"appState": {
|
||||||
"menuOpen": false,
|
"menuOpen": false,
|
||||||
"currentView": {
|
"currentView": {
|
||||||
"name": "sendTransaction"
|
"name": "sendTransaction",
|
||||||
|
"context": "0xd85a4b6a394794842887b8284293d69163007bbb"
|
||||||
},
|
},
|
||||||
"accountDetail": {
|
"accountDetail": {
|
||||||
"subview": "transactions"
|
"subview": "transactions",
|
||||||
|
"accountExport": "none",
|
||||||
|
"privateKey": ""
|
||||||
},
|
},
|
||||||
"currentDomain": "127.0.0.1:9966",
|
|
||||||
"transForward": true,
|
"transForward": true,
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"warning": null,
|
"warning": null,
|
||||||
"detailView": {}
|
"scrollToBottom": false,
|
||||||
|
"forgottenPassword": null
|
||||||
},
|
},
|
||||||
"identities": {}
|
"identities": {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,11 +56,11 @@
|
||||||
"eth-lightwallet": "^2.3.3",
|
"eth-lightwallet": "^2.3.3",
|
||||||
"eth-query": "^1.0.3",
|
"eth-query": "^1.0.3",
|
||||||
"eth-sig-util": "^1.1.1",
|
"eth-sig-util": "^1.1.1",
|
||||||
"eth-simple-keyring": "^1.1.0",
|
"eth-simple-keyring": "^1.1.1",
|
||||||
"ethereum-ens": "^0.5.0",
|
|
||||||
"ethereumjs-tx": "^1.0.0",
|
"ethereumjs-tx": "^1.0.0",
|
||||||
"ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
|
"ethereumjs-util": "ethereumjs/ethereumjs-util#ac5d0908536b447083ea422b435da27f26615de9",
|
||||||
"ethereumjs-wallet": "^0.6.0",
|
"ethereumjs-wallet": "^0.6.0",
|
||||||
|
"ethjs-ens": "^1.0.1",
|
||||||
"express": "^4.14.0",
|
"express": "^4.14.0",
|
||||||
"extension-link-enabler": "^1.0.0",
|
"extension-link-enabler": "^1.0.0",
|
||||||
"extensionizer": "^1.0.0",
|
"extensionizer": "^1.0.0",
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
"valid-url": "^1.0.9",
|
"valid-url": "^1.0.9",
|
||||||
"vreme": "^3.0.2",
|
"vreme": "^3.0.2",
|
||||||
"web3": "0.18.2",
|
"web3": "0.18.2",
|
||||||
"web3-provider-engine": "^9.1.0",
|
"web3-provider-engine": "^10.0.1",
|
||||||
"web3-stream-provider": "^2.0.6",
|
"web3-stream-provider": "^2.0.6",
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ describe ('config view actions', function() {
|
||||||
var initialState = {
|
var initialState = {
|
||||||
metamask: {
|
metamask: {
|
||||||
rpcTarget: 'foo',
|
rpcTarget: 'foo',
|
||||||
|
frequentRpcList: []
|
||||||
},
|
},
|
||||||
appState: {
|
appState: {
|
||||||
currentView: {
|
currentView: {
|
||||||
|
@ -32,13 +33,13 @@ describe ('config view actions', function() {
|
||||||
it('sets the state.metamask.rpcTarget property of the state to the action.value', function() {
|
it('sets the state.metamask.rpcTarget property of the state to the action.value', function() {
|
||||||
const action = {
|
const action = {
|
||||||
type: actions.SET_RPC_TARGET,
|
type: actions.SET_RPC_TARGET,
|
||||||
value: 'bar',
|
value: 'foo',
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = reducers(initialState, action)
|
var result = reducers(initialState, action)
|
||||||
assert.equal(result.metamask.provider.type, 'rpc')
|
assert.equal(result.metamask.provider.type, 'rpc')
|
||||||
assert.equal(result.metamask.provider.rpcTarget, action.value)
|
assert.equal(result.metamask.provider.rpcTarget, 'foo')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
var assert = require('assert')
|
||||||
|
var BinaryRenderer = require('../../../ui/app/components/binary-renderer')
|
||||||
|
|
||||||
|
describe('BinaryRenderer', function() {
|
||||||
|
|
||||||
|
let binaryRenderer
|
||||||
|
const message = 'Hello, world!'
|
||||||
|
const buffer = new Buffer(message, 'utf8')
|
||||||
|
const hex = buffer.toString('hex')
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
binaryRenderer = new BinaryRenderer()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('recovers message', function() {
|
||||||
|
const result = binaryRenderer.hexToText(hex)
|
||||||
|
assert.equal(result, message)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
it('recovers message with hex prefix', function() {
|
||||||
|
const result = binaryRenderer.hexToText('0x' + hex)
|
||||||
|
assert.equal(result, message)
|
||||||
|
})
|
||||||
|
})
|
|
@ -5,7 +5,7 @@ const assert = require('assert')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const rp = require('request-promise')
|
const rp = require('request-promise')
|
||||||
const nock = require('nock')
|
const nock = require('nock')
|
||||||
const CurrencyController = require('../../app/scripts/lib/controllers/currency')
|
const CurrencyController = require('../../app/scripts/controllers/currency')
|
||||||
|
|
||||||
describe('config-manager', function() {
|
describe('config-manager', function() {
|
||||||
var currencyController
|
var currencyController
|
||||||
|
|
|
@ -4,7 +4,7 @@ const rp = require('request-promise')
|
||||||
const nock = require('nock')
|
const nock = require('nock')
|
||||||
const configManagerGen = require('../lib/mock-config-manager')
|
const configManagerGen = require('../lib/mock-config-manager')
|
||||||
const NoticeController = require('../../app/scripts/notice-controller')
|
const NoticeController = require('../../app/scripts/notice-controller')
|
||||||
const STORAGE_KEY = 'metamask-persistance-key'
|
const STORAGE_KEY = 'metamask-persistence-key'
|
||||||
|
|
||||||
describe('notice-controller', function() {
|
describe('notice-controller', function() {
|
||||||
var noticeController
|
var noticeController
|
||||||
|
|
|
@ -4,7 +4,7 @@ const EventEmitter = require('events')
|
||||||
|
|
||||||
const PersonalMessageManager = require('../../app/scripts/lib/personal-message-manager')
|
const PersonalMessageManager = require('../../app/scripts/lib/personal-message-manager')
|
||||||
|
|
||||||
describe('Transaction Manager', function() {
|
describe('Personal Message Manager', function() {
|
||||||
let messageManager
|
let messageManager
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
@ -86,4 +86,25 @@ describe('Transaction Manager', function() {
|
||||||
assert.equal(messageManager.getMsg('2').status, 'approved')
|
assert.equal(messageManager.getMsg('2').status, 'approved')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('#normalizeMsgData', function() {
|
||||||
|
it('converts text to a utf8 hex string', function() {
|
||||||
|
var input = 'hello'
|
||||||
|
var output = messageManager.normalizeMsgData(input)
|
||||||
|
assert.equal(output, '0x68656c6c6f', 'predictably hex encoded')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('tolerates a hex prefix', function() {
|
||||||
|
var input = '0x12'
|
||||||
|
var output = messageManager.normalizeMsgData(input)
|
||||||
|
assert.equal(output, '0x12', 'un modified')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('tolerates normal hex', function() {
|
||||||
|
var input = '12'
|
||||||
|
var output = messageManager.normalizeMsgData(input)
|
||||||
|
assert.equal(output, '0x12', 'adds prefix')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
const assert = require('assert')
|
||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
const BN = ethUtil.BN
|
||||||
|
|
||||||
|
const TxUtils = require('../../app/scripts/lib/tx-utils')
|
||||||
|
|
||||||
|
|
||||||
|
describe('txUtils', function() {
|
||||||
|
let txUtils
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
txUtils = new TxUtils()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('addGasBuffer', function() {
|
||||||
|
it('multiplies by 1.5, when within block gas limit', function() {
|
||||||
|
// naive estimatedGas: 0x123fad (~1.2 mil)
|
||||||
|
const inputHex = '0x123fad'
|
||||||
|
// dummy gas limit: 0x3d4c52 (4 mil)
|
||||||
|
const blockGasLimitHex = '0x3d4c52'
|
||||||
|
const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex)
|
||||||
|
const inputBn = hexToBn(inputHex)
|
||||||
|
const outputBn = hexToBn(output)
|
||||||
|
const expectedBn = inputBn.muln(1.5)
|
||||||
|
assert(outputBn.eq(expectedBn), 'returns 1.5 the input value')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses original estimatedGas, when above block gas limit', function() {
|
||||||
|
// naive estimatedGas: 0x123fad (~1.2 mil)
|
||||||
|
const inputHex = '0x123fad'
|
||||||
|
// dummy gas limit: 0x0f4240 (1 mil)
|
||||||
|
const blockGasLimitHex = '0x0f4240'
|
||||||
|
const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex)
|
||||||
|
const inputBn = hexToBn(inputHex)
|
||||||
|
const outputBn = hexToBn(output)
|
||||||
|
const expectedBn = hexToBn(inputHex)
|
||||||
|
assert(outputBn.eq(expectedBn), 'returns the original estimatedGas value')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('buffers up to block gas limit', function() {
|
||||||
|
// naive estimatedGas: 0x123fad (~1.2 mil)
|
||||||
|
const inputHex = '0x1e8480'
|
||||||
|
// dummy gas limit: 0x1e8480 (2 mil)
|
||||||
|
const blockGasLimitHex = '0x1e8480'
|
||||||
|
const output = txUtils.addGasBuffer(inputHex, blockGasLimitHex)
|
||||||
|
const inputBn = hexToBn(inputHex)
|
||||||
|
const outputBn = hexToBn(output)
|
||||||
|
const expectedBn = hexToBn(blockGasLimitHex)
|
||||||
|
assert(outputBn.eq(expectedBn), 'returns the block gas limit value')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// util
|
||||||
|
|
||||||
|
function hexToBn(inputHex) {
|
||||||
|
return new BN(ethUtil.stripHexPrefix(inputHex), 16)
|
||||||
|
}
|
|
@ -112,11 +112,13 @@ var actions = {
|
||||||
// config screen
|
// config screen
|
||||||
SHOW_CONFIG_PAGE: 'SHOW_CONFIG_PAGE',
|
SHOW_CONFIG_PAGE: 'SHOW_CONFIG_PAGE',
|
||||||
SET_RPC_TARGET: 'SET_RPC_TARGET',
|
SET_RPC_TARGET: 'SET_RPC_TARGET',
|
||||||
|
SET_DEFAULT_RPC_TARGET: 'SET_DEFAULT_RPC_TARGET',
|
||||||
SET_PROVIDER_TYPE: 'SET_PROVIDER_TYPE',
|
SET_PROVIDER_TYPE: 'SET_PROVIDER_TYPE',
|
||||||
USE_ETHERSCAN_PROVIDER: 'USE_ETHERSCAN_PROVIDER',
|
USE_ETHERSCAN_PROVIDER: 'USE_ETHERSCAN_PROVIDER',
|
||||||
useEtherscanProvider: useEtherscanProvider,
|
useEtherscanProvider: useEtherscanProvider,
|
||||||
showConfigPage: showConfigPage,
|
showConfigPage: showConfigPage,
|
||||||
setRpcTarget: setRpcTarget,
|
setRpcTarget: setRpcTarget,
|
||||||
|
setDefaultRpcTarget: setDefaultRpcTarget,
|
||||||
setProviderType: setProviderType,
|
setProviderType: setProviderType,
|
||||||
// loading overlay
|
// loading overlay
|
||||||
SHOW_LOADING: 'SHOW_LOADING_INDICATION',
|
SHOW_LOADING: 'SHOW_LOADING_INDICATION',
|
||||||
|
@ -669,12 +671,28 @@ function markAccountsFound() {
|
||||||
// config
|
// config
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// default rpc target refers to localhost:8545 in this instance.
|
||||||
|
function setDefaultRpcTarget (rpcList) {
|
||||||
|
log.debug(`background.setDefaultRpcTarget`)
|
||||||
|
return (dispatch) => {
|
||||||
|
background.setDefaultRpc((err, result) => {
|
||||||
|
if (err) {
|
||||||
|
log.error(err)
|
||||||
|
return dispatch(self.displayWarning('Had a problem changing networks.'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setRpcTarget (newRpc) {
|
function setRpcTarget (newRpc) {
|
||||||
log.debug(`background.setRpcTarget`)
|
log.debug(`background.setRpcTarget`)
|
||||||
background.setRpcTarget(newRpc)
|
return (dispatch) => {
|
||||||
return {
|
background.setCustomRpc(newRpc, (err, result) => {
|
||||||
type: actions.SET_RPC_TARGET,
|
if (err) {
|
||||||
value: newRpc,
|
log.error(err)
|
||||||
|
return dispatch(self.displayWarning('Had a problem changing networks!'))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +768,7 @@ function exportAccount (address) {
|
||||||
dispatch(self.hideLoadingIndication())
|
dispatch(self.hideLoadingIndication())
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err)
|
log.error(err)
|
||||||
return dispatch(self.displayWarning('Had a problem exporting the account.'))
|
return dispatch(self.displayWarning('Had a problem exporting the account.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ function mapStateToProps (state) {
|
||||||
forgottenPassword: state.appState.forgottenPassword,
|
forgottenPassword: state.appState.forgottenPassword,
|
||||||
lastUnreadNotice: state.metamask.lastUnreadNotice,
|
lastUnreadNotice: state.metamask.lastUnreadNotice,
|
||||||
lostAccounts: state.metamask.lostAccounts,
|
lostAccounts: state.metamask.lostAccounts,
|
||||||
|
frequentRpcList: state.metamask.frequentRpcList || [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +212,7 @@ App.prototype.renderAppBar = function () {
|
||||||
|
|
||||||
App.prototype.renderNetworkDropdown = function () {
|
App.prototype.renderNetworkDropdown = function () {
|
||||||
const props = this.props
|
const props = this.props
|
||||||
|
const rpcList = props.frequentRpcList
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
const isOpen = state.isNetworkMenuOpen
|
const isOpen = state.isNetworkMenuOpen
|
||||||
|
|
||||||
|
@ -256,12 +258,13 @@ App.prototype.renderNetworkDropdown = function () {
|
||||||
h(DropMenuItem, {
|
h(DropMenuItem, {
|
||||||
label: 'Localhost 8545',
|
label: 'Localhost 8545',
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
action: () => props.dispatch(actions.setRpcTarget('http://localhost:8545')),
|
action: () => props.dispatch(actions.setDefaultRpcTarget(rpcList)),
|
||||||
icon: h('i.fa.fa-question-circle.fa-lg'),
|
icon: h('i.fa.fa-question-circle.fa-lg'),
|
||||||
activeNetworkRender: props.provider.rpcTarget,
|
activeNetworkRender: props.provider.rpcTarget,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
this.renderCustomOption(props.provider),
|
this.renderCustomOption(props.provider),
|
||||||
|
this.renderCommonRpc(rpcList, props.provider),
|
||||||
|
|
||||||
props.isUnlocked && h(DropMenuItem, {
|
props.isUnlocked && h(DropMenuItem, {
|
||||||
label: 'Custom RPC',
|
label: 'Custom RPC',
|
||||||
|
@ -496,6 +499,12 @@ App.prototype.renderCustomOption = function (provider) {
|
||||||
const { rpcTarget, type } = provider
|
const { rpcTarget, type } = provider
|
||||||
if (type !== 'rpc') return null
|
if (type !== 'rpc') return null
|
||||||
|
|
||||||
|
// Concatenate long URLs
|
||||||
|
let label = rpcTarget
|
||||||
|
if (rpcTarget.length > 31) {
|
||||||
|
label = label.substr(0, 34) + '...'
|
||||||
|
}
|
||||||
|
|
||||||
switch (rpcTarget) {
|
switch (rpcTarget) {
|
||||||
|
|
||||||
case 'http://localhost:8545':
|
case 'http://localhost:8545':
|
||||||
|
@ -503,10 +512,32 @@ App.prototype.renderCustomOption = function (provider) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return h(DropMenuItem, {
|
return h(DropMenuItem, {
|
||||||
label: `${rpcTarget}`,
|
label,
|
||||||
|
key: rpcTarget,
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
icon: h('i.fa.fa-question-circle.fa-lg'),
|
icon: h('i.fa.fa-question-circle.fa-lg'),
|
||||||
activeNetworkRender: 'custom',
|
activeNetworkRender: 'custom',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
App.prototype.renderCommonRpc = function (rpcList, provider) {
|
||||||
|
const { rpcTarget } = provider
|
||||||
|
const props = this.props
|
||||||
|
|
||||||
|
return rpcList.map((rpc) => {
|
||||||
|
if ((rpc === 'http://localhost:8545') || (rpc === rpcTarget)) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
return h(DropMenuItem, {
|
||||||
|
label: rpc,
|
||||||
|
key: rpc,
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
|
action: () => props.dispatch(actions.setRpcTarget(rpc)),
|
||||||
|
icon: h('i.fa.fa-question-circle.fa-lg'),
|
||||||
|
activeNetworkRender: rpc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
|
||||||
|
module.exports = BinaryRenderer
|
||||||
|
|
||||||
|
inherits(BinaryRenderer, Component)
|
||||||
|
function BinaryRenderer () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryRenderer.prototype.render = function () {
|
||||||
|
const props = this.props
|
||||||
|
const { value } = props
|
||||||
|
const text = this.hexToText(value)
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('textarea.font-small', {
|
||||||
|
readOnly: true,
|
||||||
|
style: {
|
||||||
|
width: '315px',
|
||||||
|
maxHeight: '210px',
|
||||||
|
resize: 'none',
|
||||||
|
border: 'none',
|
||||||
|
background: 'white',
|
||||||
|
padding: '3px',
|
||||||
|
},
|
||||||
|
defaultValue: text,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryRenderer.prototype.hexToText = function (hex) {
|
||||||
|
try {
|
||||||
|
const stripped = ethUtil.stripHexPrefix(hex)
|
||||||
|
const buff = Buffer.from(stripped, 'hex')
|
||||||
|
return buff.toString('utf8')
|
||||||
|
} catch (e) {
|
||||||
|
return hex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const debounce = require('debounce')
|
const debounce = require('debounce')
|
||||||
const ENS = require('ethereum-ens')
|
const ENS = require('ethjs-ens')
|
||||||
const ensRE = /.+\.eth$/
|
const ensRE = /.+\.eth$/
|
||||||
|
|
||||||
const networkResolvers = {
|
const networkResolvers = {
|
||||||
|
@ -30,6 +30,8 @@ EnsInput.prototype.render = function () {
|
||||||
console.dir(recipient)
|
console.dir(recipient)
|
||||||
return this.setState({
|
return this.setState({
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
|
ensResolution: null,
|
||||||
|
ensFailure: null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +55,16 @@ EnsInput.prototype.componentDidMount = function () {
|
||||||
let resolverAddress = networkResolvers[network]
|
let resolverAddress = networkResolvers[network]
|
||||||
|
|
||||||
if (resolverAddress) {
|
if (resolverAddress) {
|
||||||
this.ens = new ENS(web3, resolverAddress)
|
const provider = web3.currentProvider
|
||||||
|
this.ens = new ENS({ provider, network })
|
||||||
this.checkName = debounce(this.lookupEnsName.bind(this), 200)
|
this.checkName = debounce(this.lookupEnsName.bind(this), 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsInput.prototype.lookupEnsName = function () {
|
EnsInput.prototype.lookupEnsName = function () {
|
||||||
|
const recipient = document.querySelector('input[name="address"]').value
|
||||||
|
const { ensResolution } = this.state
|
||||||
|
|
||||||
if (!this.ens) {
|
if (!this.ens) {
|
||||||
return this.setState({
|
return this.setState({
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
|
@ -67,17 +73,23 @@ EnsInput.prototype.lookupEnsName = function () {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipient = document.querySelector('input[name="address"]').value
|
|
||||||
log.info(`ENS attempting to resolve name: ${recipient}`)
|
log.info(`ENS attempting to resolve name: ${recipient}`)
|
||||||
this.ens.resolver(recipient).addr()
|
this.ens.lookup(recipient.trim())
|
||||||
.then((address) => {
|
.then((address) => {
|
||||||
this.setState({
|
console.log('ens called back with ' + address)
|
||||||
loadingEns: false,
|
|
||||||
ensResolution: address,
|
if (address !== ensResolution) {
|
||||||
hoverText: address,
|
this.setState({
|
||||||
})
|
loadingEns: false,
|
||||||
|
ensResolution: address,
|
||||||
|
hoverText: address,
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
|
console.log('ens threw error: ' + reason.message)
|
||||||
|
console.trace(reason)
|
||||||
|
debugger
|
||||||
return this.setState({
|
return this.setState({
|
||||||
loadingEns: false,
|
loadingEns: false,
|
||||||
ensFailure: true,
|
ensFailure: true,
|
||||||
|
@ -86,10 +98,12 @@ EnsInput.prototype.lookupEnsName = function () {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsInput.prototype.componentDidUpdate = function () {
|
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
const { ensResolution } = state
|
const { ensResolution } = state
|
||||||
if (ensResolution && this.props.onChange) {
|
if (ensResolution && this.props.onChange &&
|
||||||
|
ensResolution !== prevState.ensResolution) {
|
||||||
|
console.log('Firing on change to parent')
|
||||||
this.props.onChange(ensResolution)
|
this.props.onChange(ensResolution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,6 +129,7 @@ EnsInput.prototype.ensIconContents = function (recipient) {
|
||||||
style: {
|
style: {
|
||||||
width: '30px',
|
width: '30px',
|
||||||
height: '30px',
|
height: '30px',
|
||||||
|
transform: 'translateY(-6px)',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,17 @@ HexAsDecimalInput.prototype.render = function () {
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
h('input.ether-balance.ether-balance-amount', {
|
h('input.ether-balance.ether-balance-amount', {
|
||||||
|
type: 'number',
|
||||||
style: extend({
|
style: extend({
|
||||||
display: 'block',
|
display: 'block',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
border: '1px solid #bdbdbd',
|
border: '1px solid #bdbdbd',
|
||||||
|
|
||||||
}, style),
|
}, style),
|
||||||
value: decimalValue,
|
value: decimalValue,
|
||||||
onChange: (event) => {
|
onChange: (event) => {
|
||||||
const hexString = hexify(event.target.value)
|
const hexString = (event.target.value === '') ? '' : hexify(event.target.value)
|
||||||
onChange(hexString)
|
onChange(hexString)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -70,7 +72,11 @@ function hexify (decimalString) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function decimalize (input, toEth) {
|
function decimalize (input, toEth) {
|
||||||
const strippedInput = ethUtil.stripHexPrefix(input)
|
if (input === '') {
|
||||||
const inputBN = new BN(strippedInput, 'hex')
|
return ''
|
||||||
return inputBN.toString(10)
|
} else {
|
||||||
|
const strippedInput = ethUtil.stripHexPrefix(input)
|
||||||
|
const inputBN = new BN(strippedInput, 'hex')
|
||||||
|
return inputBN.toString(10)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
|
||||||
const AccountPanel = require('./account-panel')
|
const AccountPanel = require('./account-panel')
|
||||||
|
const BinaryRenderer = require('./binary-renderer')
|
||||||
|
|
||||||
module.exports = PendingMsgDetails
|
module.exports = PendingMsgDetails
|
||||||
|
|
||||||
|
@ -41,18 +42,7 @@ PendingMsgDetails.prototype.render = function () {
|
||||||
// message data
|
// message data
|
||||||
h('div', [
|
h('div', [
|
||||||
h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
|
h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
|
||||||
h('textarea.font-small', {
|
h(BinaryRenderer, { value: data }),
|
||||||
readOnly: true,
|
|
||||||
style: {
|
|
||||||
width: '315px',
|
|
||||||
maxHeight: '210px',
|
|
||||||
resize: 'none',
|
|
||||||
border: 'none',
|
|
||||||
background: 'white',
|
|
||||||
padding: '3px',
|
|
||||||
},
|
|
||||||
defaultValue: data,
|
|
||||||
}),
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
|
@ -32,10 +32,8 @@ PTXP.render = function () {
|
||||||
var account = props.accounts[address]
|
var account = props.accounts[address]
|
||||||
var balance = account ? account.balance : '0x0'
|
var balance = account ? account.balance : '0x0'
|
||||||
|
|
||||||
const gas = state.gas || txParams.gas
|
const gas = (state.gas === undefined) ? txParams.gas : state.gas
|
||||||
const gasPrice = state.gasPrice || txData.gasPrice
|
const gasPrice = (state.gasPrice === undefined) ? txData.gasPrice : state.gasPrice
|
||||||
const gasDefault = txParams.gas
|
|
||||||
const gasPriceDefault = txData.gasPrice
|
|
||||||
|
|
||||||
var txFee = state.txFee || txData.txFee || ''
|
var txFee = state.txFee || txData.txFee || ''
|
||||||
var maxCost = state.maxCost || txData.maxCost || ''
|
var maxCost = state.maxCost || txData.maxCost || ''
|
||||||
|
@ -131,11 +129,7 @@ PTXP.render = function () {
|
||||||
},
|
},
|
||||||
onChange: (newHex) => {
|
onChange: (newHex) => {
|
||||||
log.info(`Gas limit changed to ${newHex}`)
|
log.info(`Gas limit changed to ${newHex}`)
|
||||||
if (newHex === '0x0') {
|
this.setState({ gas: newHex })
|
||||||
this.setState({gas: gasDefault})
|
|
||||||
} else {
|
|
||||||
this.setState({ gas: newHex })
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
@ -155,11 +149,7 @@ PTXP.render = function () {
|
||||||
},
|
},
|
||||||
onChange: (newHex) => {
|
onChange: (newHex) => {
|
||||||
log.info(`Gas price changed to: ${newHex}`)
|
log.info(`Gas price changed to: ${newHex}`)
|
||||||
if (newHex === '0x0') {
|
this.setState({ gasPrice: newHex })
|
||||||
this.setState({gasPrice: gasPriceDefault})
|
|
||||||
} else {
|
|
||||||
this.setState({ gasPrice: newHex })
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
@ -316,7 +306,6 @@ PTXP.gatherParams = function () {
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
const txData = state.txData || props.txData
|
const txData = state.txData || props.txData
|
||||||
const txParams = txData.txParams
|
const txParams = txData.txParams
|
||||||
|
|
||||||
const gas = state.gas || txParams.gas
|
const gas = state.gas || txParams.gas
|
||||||
const gasPrice = state.gasPrice || txParams.gasPrice
|
const gasPrice = state.gasPrice || txParams.gasPrice
|
||||||
const resultTx = extend(txParams, {
|
const resultTx = extend(txParams, {
|
||||||
|
@ -330,6 +319,16 @@ PTXP.gatherParams = function () {
|
||||||
return resultTxMeta
|
return resultTxMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PTXP.verifyGasParams = function () {
|
||||||
|
// We call this in case the gas has not been modified at all
|
||||||
|
if (!this.state) { return true }
|
||||||
|
return this._notZeroOrEmptyString(this.state.gas) && this._notZeroOrEmptyString(this.state.gasPrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
PTXP._notZeroOrEmptyString = function (obj) {
|
||||||
|
return obj !== '' && obj !== '0x0'
|
||||||
|
}
|
||||||
|
|
||||||
function forwardCarrat () {
|
function forwardCarrat () {
|
||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
const Component = require('react').Component
|
const Component = require('react').Component
|
||||||
|
const connect = require('react-redux').connect
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const PendingTxDetails = require('./pending-tx-details')
|
const PendingTxDetails = require('./pending-tx-details')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
|
const actions = require('../actions')
|
||||||
|
|
||||||
module.exports = PendingTx
|
module.exports = connect(mapStateToProps)(PendingTx)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inherits(PendingTx, Component)
|
inherits(PendingTx, Component)
|
||||||
function PendingTx () {
|
function PendingTx () {
|
||||||
|
@ -60,25 +68,31 @@ PendingTx.prototype.render = function () {
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
props.insufficientBalance ?
|
props.insufficientBalance ?
|
||||||
h('button.btn-green', {
|
h('button', {
|
||||||
onClick: props.buyEth,
|
onClick: props.buyEth,
|
||||||
}, 'Buy Ether')
|
}, 'Buy Ether')
|
||||||
: null,
|
: null,
|
||||||
|
|
||||||
h('button.confirm', {
|
|
||||||
disabled: props.insufficientBalance,
|
|
||||||
onClick: props.sendTransaction,
|
|
||||||
}, 'Accept'),
|
|
||||||
|
|
||||||
h('button.cancel.btn-red', {
|
|
||||||
onClick: props.cancelTransaction,
|
|
||||||
}, 'Reject'),
|
|
||||||
|
|
||||||
h('button', {
|
h('button', {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.refs.details.resetGasFields()
|
this.refs.details.resetGasFields()
|
||||||
},
|
},
|
||||||
}, 'Reset'),
|
}, 'Reset'),
|
||||||
|
|
||||||
|
h('button.confirm.btn-green', {
|
||||||
|
disabled: props.insufficientBalance,
|
||||||
|
onClick: (txData, event) => {
|
||||||
|
if (this.refs.details.verifyGasParams()) {
|
||||||
|
props.sendTransaction(txData, event)
|
||||||
|
} else {
|
||||||
|
this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, 'Accept'),
|
||||||
|
|
||||||
|
h('button.cancel.btn-red', {
|
||||||
|
onClick: props.cancelTransaction,
|
||||||
|
}, 'Reject'),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,7 @@ const connect = require('react-redux').connect
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
const currencies = require('./conversion.json').rows
|
const currencies = require('./conversion.json').rows
|
||||||
const validUrl = require('valid-url')
|
const validUrl = require('valid-url')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(ConfigScreen)
|
module.exports = connect(mapStateToProps)(ConfigScreen)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
|
|
|
@ -256,3 +256,9 @@ hr.horizontal-line {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.critical-error {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ function reduceMetamask (state, action) {
|
||||||
conversionDate: 'N/A',
|
conversionDate: 'N/A',
|
||||||
noActiveNotices: true,
|
noActiveNotices: true,
|
||||||
lastUnreadNotice: undefined,
|
lastUnreadNotice: undefined,
|
||||||
|
frequentRpcList: [],
|
||||||
}, state.metamask)
|
}, state.metamask)
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
@ -53,6 +54,11 @@ function reduceMetamask (state, action) {
|
||||||
isUnlocked: false,
|
isUnlocked: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.SET_RPC_LIST:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
frequentRpcList: action.value,
|
||||||
|
})
|
||||||
|
|
||||||
case actions.SET_RPC_TARGET:
|
case actions.SET_RPC_TARGET:
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
provider: {
|
provider: {
|
||||||
|
|
Loading…
Reference in New Issue