Merge pull request #356 from poanetwork/vb-rsk-dpath-update
Custom derivation paths and access to funds in accounts derived from ETH dPath
This commit is contained in:
commit
7662c11542
|
@ -2,9 +2,11 @@
|
|||
|
||||
## Current Master
|
||||
|
||||
- [#356](https://github.com/poanetwork/nifty-wallet/pull/356) - (Backwards-compatibility feature) Custom derivation paths and access to funds in accounts derived from ETH dPath
|
||||
- [#379](https://github.com/poanetwork/nifty-wallet/pull/379) - (Feature) Ability to set custom nonce of tx
|
||||
- [#377](https://github.com/poanetwork/nifty-wallet/pull/377) - (Fix) Sign message screen: do not decode message if it is not hex encoded
|
||||
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - (Fix) notifications order in batch requests
|
||||
- [#364](https://github.com/poanetwork/nifty-wallet/pull/364) - Fix notifications order in batch requests
|
||||
|
||||
## 5.0.3 Fri May 01 2020
|
||||
|
||||
|
@ -14,6 +16,7 @@
|
|||
- [#368](https://github.com/poanetwork/nifty-wallet/pull/368) - (Fix) Ability to import Keystore file if it is not secured by password
|
||||
- [#366](https://github.com/poanetwork/nifty-wallet/pull/366) - (Fix) Increase max token symbol length up to 12
|
||||
- [#363](https://github.com/poanetwork/nifty-wallet/pull/363) - (Fix) token decimals display in pending tx screen
|
||||
- [#356](https://github.com/poanetwork/nifty-wallet/pull/356) - (Backwards-compatibility feature) Custom derivation paths and access to funds in accounts derived from ETH dPath
|
||||
|
||||
## 5.0.2 Thu Apr 16 2020
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@ import createJsonRpcClient from './createJsonRpcClient'
|
|||
import createLocalhostClient from './createLocalhostClient'
|
||||
const createPocketClient = require('./createPocketClient')
|
||||
const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy')
|
||||
const ethNetProps = require('eth-net-props')
|
||||
import ethNetProps from 'eth-net-props'
|
||||
import parse from 'url-parse'
|
||||
const networks = { networkList: {} }
|
||||
const { isKnownProvider, getDPath } = require('../../../../old-ui/app/util')
|
||||
const { isKnownProvider } = require('../../../../old-ui/app/util')
|
||||
|
||||
const {
|
||||
ROPSTEN,
|
||||
|
@ -205,8 +205,6 @@ module.exports = class NetworkController extends EventEmitter {
|
|||
const previousNetworkID = this.getNetworkState()
|
||||
this.setNetworkState('loading')
|
||||
this._configureProvider(opts)
|
||||
const dPath = getDPath(opts.type)
|
||||
this.store.updateState({ dPath })
|
||||
this.emit('networkDidChange', opts.type, previousNetworkID)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const KeyringController = require('eth-keychain-controller')
|
||||
const log = require('loglevel')
|
||||
import KeyringController from 'eth-keychain-controller'
|
||||
import log from 'loglevel'
|
||||
const { getDPath } = require('../../../old-ui/app/util')
|
||||
|
||||
const seedPhraseVerifier = {
|
||||
|
@ -17,14 +17,14 @@ const seedPhraseVerifier = {
|
|||
* @returns {Promise<void>} Promises undefined
|
||||
*
|
||||
*/
|
||||
verifyAccounts (createdAccounts, seedWords, network) {
|
||||
verifyAccounts (createdAccounts, seedWords, network, isCreatedWithCorrectDPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (!createdAccounts || createdAccounts.length < 1) {
|
||||
return reject(new Error('No created accounts defined.'))
|
||||
}
|
||||
|
||||
const dPath = getDPath(network)
|
||||
const dPath = getDPath(network, isCreatedWithCorrectDPath)
|
||||
const keyringController = new KeyringController({})
|
||||
const Keyring = keyringController.getKeyringClassForType('HD Key Tree')
|
||||
const opts = {
|
||||
|
@ -56,4 +56,4 @@ const seedPhraseVerifier = {
|
|||
},
|
||||
}
|
||||
|
||||
module.exports = seedPhraseVerifier
|
||||
export default seedPhraseVerifier
|
||||
|
|
|
@ -52,7 +52,7 @@ const version = require('../manifest.json').version
|
|||
import ethUtil, { BN } from 'ethereumjs-util'
|
||||
const GWEI_BN = new BN('1000000000')
|
||||
import percentile from 'percentile'
|
||||
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
|
||||
import seedPhraseVerifier from './lib/seed-phrase-verifier'
|
||||
import log from 'loglevel'
|
||||
const TrezorKeyring = require('eth-trezor-keyring')
|
||||
const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
|
||||
|
@ -169,26 +169,32 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
})
|
||||
|
||||
// ensure accountTracker updates balances after network change
|
||||
this.networkController.on('networkDidChange', (newType, previousNetworkIDStr) => {
|
||||
const dPath = getDPath(newType)
|
||||
this.deriveKeyringFromNewDPath(dPath)
|
||||
.then(accounts => {
|
||||
this.accountTracker._updateAccounts()
|
||||
this.detectTokensController.restartTokenDetection()
|
||||
this.networkController.on('networkDidChange', (newNetworkType, previousNetworkIDStr) => {
|
||||
this.keyringController.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
const dPath = getDPath(newNetworkType, isCreatedWithCorrectDPath)
|
||||
this.deriveKeyringFromNewDPath(dPath)
|
||||
.then(_accounts => {
|
||||
this.accountTracker._updateAccounts()
|
||||
this.detectTokensController.restartTokenDetection()
|
||||
|
||||
const previousNetworkID = parseInt(previousNetworkIDStr, 10)
|
||||
const nextNetwork = getNetworkID({network: newType})
|
||||
const nextNetworkID = parseInt(nextNetwork && nextNetwork.netId, 10)
|
||||
if (nextNetworkID !== previousNetworkID) {
|
||||
const isPreviousETC = previousNetworkID === CLASSIC_CODE
|
||||
const isPreviousRSK = ifRSK(previousNetworkID)
|
||||
const isNextETC = nextNetworkID === CLASSIC_CODE
|
||||
const isNextRSK = ifRSK(nextNetworkID)
|
||||
if (isPreviousETC || isPreviousRSK || isNextETC || isNextRSK) {
|
||||
this.forgetDevice(LEDGER, false)
|
||||
this.forgetDevice(TREZOR, false)
|
||||
const previousNetworkID = parseInt(previousNetworkIDStr, 10)
|
||||
const nextNetwork = getNetworkID({network: newNetworkType})
|
||||
const nextNetworkID = parseInt(nextNetwork && nextNetwork.netId, 10)
|
||||
if (nextNetworkID !== previousNetworkID) {
|
||||
const isPreviousETC = previousNetworkID === CLASSIC_CODE
|
||||
const isPreviousRSK = ifRSK(previousNetworkID)
|
||||
const isNextETC = nextNetworkID === CLASSIC_CODE
|
||||
const isNextRSK = ifRSK(nextNetworkID)
|
||||
if (isPreviousETC || isPreviousRSK || isNextETC || isNextRSK) {
|
||||
this.forgetDevice(LEDGER, false)
|
||||
this.forgetDevice(TREZOR, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e)
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e)
|
||||
|
@ -493,6 +499,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController),
|
||||
addNewMultisig: nodeify(keyringController.addNewMultisig, keyringController),
|
||||
exportAccount: nodeify(keyringController.exportAccount, keyringController),
|
||||
isCreatedWithCorrectDPath: nodeify(keyringController.isCreatedWithCorrectDPath, keyringController),
|
||||
|
||||
// txController
|
||||
cancelTransaction: nodeify(txController.cancelTransaction, txController),
|
||||
|
@ -582,7 +589,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @param {} password
|
||||
* @param {} seed
|
||||
*/
|
||||
async createNewVaultAndRestore (password, seed) {
|
||||
async createNewVaultAndRestore (password, seed, dPath) {
|
||||
const releaseLock = await this.createVaultMutex.acquire()
|
||||
try {
|
||||
let accounts, lastBalance
|
||||
|
@ -592,9 +599,8 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
// clear known identities
|
||||
this.preferencesController.setAddresses([])
|
||||
// create new vault
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
const dPath = getDPath(network)
|
||||
this.store.updateState({dPath})
|
||||
const networkType = this.networkController.getProviderConfig().type
|
||||
const isCreatedWithCorrectDPath = true
|
||||
const vault = await keyringController.createNewVaultAndRestore(password, seed, dPath)
|
||||
|
||||
const ethQuery = new EthQuery(this.provider)
|
||||
|
@ -606,7 +612,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
throw new Error('MetamaskController - No HD Key Tree found')
|
||||
}
|
||||
|
||||
setDPath(primaryKeyring, network)
|
||||
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
|
||||
|
||||
// seek out the first zero balance
|
||||
while (lastBalance !== '0x0') {
|
||||
|
@ -911,13 +917,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @returns {} keyState
|
||||
*/
|
||||
async addNewAccount () {
|
||||
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
const keyringController = this.keyringController
|
||||
const primaryKeyring = keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
if (!primaryKeyring) {
|
||||
throw new Error('MetamaskController - No HD Key Tree found')
|
||||
}
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
setDPath(primaryKeyring, network)
|
||||
const keyringController = this.keyringController
|
||||
const networkType = this.networkController.getProviderConfig().type
|
||||
const isCreatedWithCorrectDPath = await keyringController.isCreatedWithCorrectDPath()
|
||||
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
|
||||
const oldAccounts = await keyringController.getAccounts()
|
||||
const keyState = await keyringController.addNewAccount(primaryKeyring)
|
||||
const newAccounts = await keyringController.getAccounts()
|
||||
|
@ -965,12 +972,14 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
* @returns {Promise<string>} Seed phrase to be confirmed by the user.
|
||||
*/
|
||||
async verifySeedPhrase () {
|
||||
const primaryKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
const keyringController = this.keyringController
|
||||
const isCreatedWithCorrectDPath = await keyringController.isCreatedWithCorrectDPath()
|
||||
const primaryKeyring = keyringController.getKeyringsByType('HD Key Tree')[0]
|
||||
if (!primaryKeyring) {
|
||||
throw new Error('MetamaskController - No HD Key Tree found')
|
||||
}
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
setDPath(primaryKeyring, network)
|
||||
const networkType = this.networkController.getProviderConfig().type
|
||||
setDPath(primaryKeyring, networkType, isCreatedWithCorrectDPath)
|
||||
|
||||
const serialized = await primaryKeyring.serialize()
|
||||
const seedWords = serialized.mnemonic
|
||||
|
@ -981,7 +990,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
}
|
||||
|
||||
try {
|
||||
await seedPhraseVerifier.verifyAccounts(accounts, seedWords, network)
|
||||
await seedPhraseVerifier.verifyAccounts(accounts, seedWords, networkType, isCreatedWithCorrectDPath)
|
||||
return seedWords
|
||||
} catch (err) {
|
||||
log.error(err.message)
|
||||
|
@ -1086,8 +1095,6 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||
const privateKey = await accountImporter.importAccount(strategy, args)
|
||||
keyring = await this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
|
||||
}
|
||||
const network = this.networkController.getProviderConfig().type
|
||||
setDPath(keyring, network)
|
||||
const accounts = await keyring.getAccounts()
|
||||
// update accounts in preferences controller
|
||||
const allAccounts = await this.keyringController.getAccounts()
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -4,7 +4,7 @@ const Component = require('react').Component
|
|||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../ui/app/actions')
|
||||
const { getCurrentKeyring, ifContractAcc, valuesFor, toChecksumAddress } = require('./util')
|
||||
const { getCurrentKeyring, ifContractAcc, valuesFor, toChecksumAddress, ifLooseAcc, ifRSK, ifETC } = require('./util')
|
||||
const Identicon = require('./components/identicon')
|
||||
const EthBalance = require('./components/eth-balance')
|
||||
const TransactionList = require('./components/transaction-list')
|
||||
|
@ -14,10 +14,10 @@ const TabBar = require('./components/tab-bar')
|
|||
const TokenList = require('./components/token-list')
|
||||
const AccountDropdowns = require('./components/account-dropdowns/account-dropdowns.component').AccountDropdowns
|
||||
const CopyButton = require('./components/copy/copy-button')
|
||||
const ToastComponent = require('./components/toast')
|
||||
import * as Toast from './components/toast'
|
||||
import { getMetaMaskAccounts } from '../../ui/app/selectors'
|
||||
|
||||
module.exports = connect(mapStateToProps)(AccountDetailScreen)
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(AccountDetailScreen)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const accounts = getMetaMaskAccounts(state)
|
||||
|
@ -42,11 +42,66 @@ function mapStateToProps (state) {
|
|||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
actions: {
|
||||
showSendPage: () => dispatch(actions.showSendPage()),
|
||||
showSendContractPage: ({ methodSelected, methodABI, inputValues }) => dispatch(actions.showSendContractPage({methodSelected, methodABI, inputValues})),
|
||||
buyEthView: (selected) => dispatch(actions.buyEthView(selected)),
|
||||
viewPendingTx: (txId) => dispatch(actions.viewPendingTx(txId)),
|
||||
setAccountLabel: (account, label) => dispatch(actions.setAccountLabel(account, label)),
|
||||
showRemoveTokenPage: (token) => dispatch(actions.showRemoveTokenPage(token)),
|
||||
showAddSuggestedTokenPage: () => dispatch(actions.showAddSuggestedTokenPage()),
|
||||
showAddTokenPage: () => dispatch(actions.showAddTokenPage()),
|
||||
setCurrentAccountTab: (key) => dispatch(actions.setCurrentAccountTab(key)),
|
||||
displayToast: (msg) => dispatch(actions.displayToast(msg)),
|
||||
isCreatedWithCorrectDPath: () => dispatch(actions.isCreatedWithCorrectDPath()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
inherits(AccountDetailScreen, Component)
|
||||
function AccountDetailScreen () {
|
||||
Component.call(this)
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.componentDidMount = function () {
|
||||
const props = this.props
|
||||
const { address, network, keyrings, identities } = props
|
||||
props.actions.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
if (!isCreatedWithCorrectDPath) {
|
||||
const currentKeyring = getCurrentKeyring(address, network, keyrings, identities)
|
||||
if (!ifLooseAcc(currentKeyring) && !ifContractAcc(currentKeyring) && (ifRSK(network) || ifETC(network))) {
|
||||
props.actions.displayToast(Toast.ERROR_ON_INCORRECT_DPATH)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.componentWillUpdate = function (nextProps) {
|
||||
const {
|
||||
network: oldNet,
|
||||
} = this.props
|
||||
const {
|
||||
network: newNet,
|
||||
} = nextProps
|
||||
|
||||
if (oldNet !== newNet) {
|
||||
const props = this.props
|
||||
const { address, keyrings, identities } = props
|
||||
props.actions.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
if (!isCreatedWithCorrectDPath) {
|
||||
const currentKeyring = getCurrentKeyring(address, newNet, keyrings, identities)
|
||||
if (!ifLooseAcc(currentKeyring) && !ifContractAcc(currentKeyring) && (ifRSK(newNet) || ifETC(newNet))) {
|
||||
props.actions.displayToast(Toast.ERROR_ON_INCORRECT_DPATH)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.render = function () {
|
||||
const props = this.props
|
||||
const { network, conversionRate, currentCurrency } = props
|
||||
|
@ -56,7 +111,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||
const account = props.accounts[selected]
|
||||
|
||||
if (Object.keys(props.suggestedTokens).length > 0) {
|
||||
this.props.dispatch(actions.showAddSuggestedTokenPage())
|
||||
this.props.actions.showAddSuggestedTokenPage()
|
||||
}
|
||||
|
||||
const currentKeyring = getCurrentKeyring(props.address, network, props.keyrings, props.identities)
|
||||
|
@ -65,8 +120,9 @@ AccountDetailScreen.prototype.render = function () {
|
|||
|
||||
h('.account-detail-section.full-flex-height', [
|
||||
|
||||
h(ToastComponent, {
|
||||
isSuccess: false,
|
||||
h(Toast.ToastComponent, {
|
||||
type: Toast.TOAST_TYPE_ERROR,
|
||||
hideManually: true,
|
||||
}),
|
||||
|
||||
// identicon, label, balance, etc
|
||||
|
@ -108,7 +164,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||
isEditingLabel: false,
|
||||
},
|
||||
saveText: (text) => {
|
||||
props.dispatch(actions.setAccountLabel(selected, text))
|
||||
props.actions.setAccountLabel(selected, text)
|
||||
},
|
||||
}, [
|
||||
|
||||
|
@ -223,16 +279,16 @@ AccountDetailScreen.prototype.render = function () {
|
|||
h('.flex-grow'),
|
||||
|
||||
!ifContractAcc(currentKeyring) ? h('button', {
|
||||
onClick: () => props.dispatch(actions.buyEthView(selected)),
|
||||
onClick: () => props.actions.buyEthView(selected),
|
||||
style: { marginRight: '10px' },
|
||||
}, 'Buy') : null,
|
||||
|
||||
h('button', {
|
||||
onClick: () => {
|
||||
if (ifContractAcc(currentKeyring)) {
|
||||
return props.dispatch(actions.showSendContractPage({}))
|
||||
return props.actions.showSendContractPage({})
|
||||
} else {
|
||||
return props.dispatch(actions.showSendPage())
|
||||
return props.actions.showSendPage()
|
||||
}
|
||||
},
|
||||
}, ifContractAcc(currentKeyring) ? 'Execute methods' : 'Send'),
|
||||
|
@ -278,7 +334,7 @@ AccountDetailScreen.prototype.tabSections = function () {
|
|||
],
|
||||
defaultTab: currentAccountTab || 'history',
|
||||
tabSelected: (key) => {
|
||||
this.props.dispatch(actions.setCurrentAccountTab(key))
|
||||
this.props.actions.setCurrentAccountTab(key)
|
||||
},
|
||||
}),
|
||||
|
||||
|
@ -297,8 +353,8 @@ AccountDetailScreen.prototype.tabSwitchView = function () {
|
|||
userAddress: address,
|
||||
network,
|
||||
tokens,
|
||||
addToken: () => this.props.dispatch(actions.showAddTokenPage()),
|
||||
removeToken: (token) => this.props.dispatch(actions.showRemoveTokenPage(token)),
|
||||
addToken: () => this.props.actions.showAddTokenPage(),
|
||||
removeToken: (token) => this.props.actions.showRemoveTokenPage(token),
|
||||
})
|
||||
default:
|
||||
return this.transactionList()
|
||||
|
@ -317,7 +373,7 @@ AccountDetailScreen.prototype.transactionList = function () {
|
|||
address,
|
||||
shapeShiftTxList,
|
||||
viewPendingTx: (txId) => {
|
||||
this.props.dispatch(actions.viewPendingTx(txId))
|
||||
this.props.actions.viewPendingTx(txId)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@ import actions from '../../../../ui/app/actions'
|
|||
import { connect } from 'react-redux'
|
||||
import { DropdownMenuItem } from '../dropdown'
|
||||
import Identicon from '../identicon'
|
||||
import { ifLooseAcc, ifContractAcc, ifHardwareAcc } from '../../util'
|
||||
import { ifLooseAcc, ifContractAcc, ifHardwareAcc, ifRSK, ifETC } from '../../util'
|
||||
import { getHdPaths, isLedger } from '../connect-hardware/util'
|
||||
import { LEDGER } from '../connect-hardware/enum'
|
||||
import { importTypes, labels } from '../../accounts/import/enums'
|
||||
import { ERROR_ON_INCORRECT_DPATH } from '../toast'
|
||||
|
||||
class AccountsDropdownItemView extends Component {
|
||||
static propTypes = {
|
||||
|
@ -100,7 +101,7 @@ class AccountsDropdownItemView extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
ifProxyAcc (address, setProxy) {
|
||||
ifProxyAcc (address, _setProxy) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.props.actions.getContract(address)
|
||||
.then(contractProps => {
|
||||
|
@ -138,6 +139,15 @@ class AccountsDropdownItemView extends Component {
|
|||
} else {
|
||||
this.preventToast()
|
||||
}
|
||||
} else if (!ifLooseAcc(keyring) && !ifContractAcc(keyring) && (ifRSK(this.props.network) || ifETC(this.props.network))) {
|
||||
this.props.actions.isCreatedWithCorrectDPath()
|
||||
.then(isCreatedWithCorrectDPath => {
|
||||
if (isCreatedWithCorrectDPath) {
|
||||
this.preventToast()
|
||||
} else {
|
||||
this.props.actions.displayToast(ERROR_ON_INCORRECT_DPATH)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.preventToast()
|
||||
}
|
||||
|
@ -152,6 +162,13 @@ class AccountsDropdownItemView extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const result = {
|
||||
network: state.metamask.network,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
|
@ -163,10 +180,11 @@ const mapDispatchToProps = (dispatch) => {
|
|||
return dispatch(actions.connectHardwareAndUnlockAddress(deviceName, hdPath, address))
|
||||
},
|
||||
displayToast: (msg) => dispatch(actions.displayToast(msg)),
|
||||
isCreatedWithCorrectDPath: () => dispatch(actions.isCreatedWithCorrectDPath()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AccountsDropdownItemView: connect(null, mapDispatchToProps)(AccountsDropdownItemView),
|
||||
AccountsDropdownItemView: connect(mapStateToProps, mapDispatchToProps)(AccountsDropdownItemView),
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ const { tokenInfoGetter, calcTokenAmount } = require('../../../ui/app/token-util
|
|||
import BigNumber from 'bignumber.js'
|
||||
import ethNetProps from 'eth-net-props'
|
||||
import { getMetaMaskAccounts } from '../../../ui/app/selectors'
|
||||
import ToastComponent from './toast'
|
||||
import * as Toast from './toast'
|
||||
|
||||
const MIN_GAS_PRICE_BN = new BN('0')
|
||||
const MIN_GAS_LIMIT_BN = new BN('21000')
|
||||
|
@ -158,14 +158,15 @@ class PendingTx extends Component {
|
|||
fontSize: '14px',
|
||||
}
|
||||
|
||||
|
||||
const isError = txMeta.simulationFails || !isValidAddress || insufficientBalance || (dangerousGasLimit && !gasLimitSpecified)
|
||||
return (
|
||||
|
||||
h('div', {
|
||||
key: txMeta.id,
|
||||
}, [
|
||||
h(ToastComponent, {
|
||||
isSuccess: false,
|
||||
h(Toast.ToastComponent, {
|
||||
type: Toast.TOAST_TYPE_ERROR,
|
||||
}),
|
||||
|
||||
h('form#pending-tx-form', {
|
||||
|
|
|
@ -5,7 +5,7 @@ import PersistentForm from '../../../lib/persistent-form'
|
|||
import SendProfile from './send-profile'
|
||||
import SendHeader from './send-header'
|
||||
import ErrorComponent from '../error'
|
||||
import ToastComponent from '../toast'
|
||||
import * as Toast from '../toast'
|
||||
import Select from 'react-select'
|
||||
import actions from '../../../../ui/app/actions'
|
||||
import abi from 'web3-eth-abi'
|
||||
|
@ -154,7 +154,7 @@ class SendTransactionScreen extends PersistentForm {
|
|||
<SendProfile />
|
||||
<SendHeader title="Execute Method" />
|
||||
<ErrorComponent error={error} />
|
||||
<ToastComponent isSuccess={true} />
|
||||
<Toast.ToastComponent type={Toast.TOAST_TYPE_SUCCESS} />
|
||||
<div style={{ padding: '0 30px' }}>
|
||||
<Select
|
||||
clearable={false}
|
||||
|
|
|
@ -15,7 +15,7 @@ import SendProfile from './send-profile'
|
|||
import SendHeader from './send-header'
|
||||
import ErrorComponent from '../error'
|
||||
import { getMetaMaskAccounts } from '../../../../ui/app/selectors'
|
||||
import ToastComponent from '../toast'
|
||||
import * as Toast from '../toast'
|
||||
|
||||
const optionalDataLabelStyle = {
|
||||
background: '#ffffff',
|
||||
|
@ -42,7 +42,7 @@ class SendTransactionScreen extends PersistentForm {
|
|||
|
||||
return (
|
||||
<div className="send-screen flex-column flex-grow">
|
||||
<ToastComponent isSuccess={false} />
|
||||
<Toast.ToastComponent type={Toast.TOAST_TYPE_ERROR} />
|
||||
<SendProfile/>
|
||||
|
||||
<SendHeader
|
||||
|
|
|
@ -4,12 +4,18 @@ import { connect } from 'react-redux'
|
|||
import classnames from 'classnames'
|
||||
import actions from '../../../ui/app/actions'
|
||||
|
||||
const TOAST_TYPE_SUCCESS = 'success'
|
||||
const TOAST_TYPE_ERROR = 'error'
|
||||
|
||||
const ERROR_ON_INCORRECT_DPATH = 'The account is derived from ETH derivation path despite you connected to another chain. If you are ready to switch to correct derivation path, just restore from the same seed phrase.'
|
||||
|
||||
class ToastComponent extends Component {
|
||||
static propTypes = {
|
||||
msg: PropTypes.string,
|
||||
toastMsg: PropTypes.string,
|
||||
isSuccess: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
hideToast: PropTypes.func,
|
||||
hideManually: PropTypes.bool,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
|
@ -19,10 +25,12 @@ class ToastComponent extends Component {
|
|||
|
||||
componentDidUpdate (prevProps) {
|
||||
if ((!prevProps.msg && this.props.msg) || (!prevProps.toastMsg && this.props.toastMsg)) {
|
||||
this.timerID = setTimeout(() => {
|
||||
this.props.hideToast()
|
||||
clearTimeout(this.timerID)
|
||||
}, 4000)
|
||||
if (!this.props.hideManually) {
|
||||
this.timerID = setTimeout(() => {
|
||||
this.props.hideToast()
|
||||
clearTimeout(this.timerID)
|
||||
}, 4000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,15 +39,23 @@ class ToastComponent extends Component {
|
|||
clearTimeout(this.timerID)
|
||||
}
|
||||
|
||||
_getClass (type) {
|
||||
switch (type) {
|
||||
case TOAST_TYPE_SUCCESS:
|
||||
return 'green'
|
||||
case TOAST_TYPE_ERROR:
|
||||
return 'red'
|
||||
default:
|
||||
return 'green'
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let toastMsg = this.props.msg || this.props.toastMsg
|
||||
toastMsg = (toastMsg && toastMsg.message) || toastMsg
|
||||
return toastMsg ? (
|
||||
<div
|
||||
className={classnames('toast', {
|
||||
'green': this.props.isSuccess,
|
||||
'red': !this.props.isSuccess,
|
||||
})}
|
||||
className={classnames('toast', this._getClass(this.props.type))}
|
||||
onClick={(e) => this.props.hideToast()}
|
||||
>{toastMsg}</div>
|
||||
) : null
|
||||
|
@ -58,4 +74,9 @@ function mapDispatchToProps (dispatch) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(ToastComponent)
|
||||
module.exports = {
|
||||
ToastComponent: connect(mapStateToProps, mapDispatchToProps)(ToastComponent),
|
||||
TOAST_TYPE_SUCCESS,
|
||||
TOAST_TYPE_ERROR,
|
||||
ERROR_ON_INCORRECT_DPATH,
|
||||
}
|
||||
|
|
|
@ -293,7 +293,7 @@ app sections
|
|||
color: #ffffff !important;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
padding: 20px;
|
||||
width: 357px;
|
||||
line-height: 14px;
|
||||
position: fixed;
|
||||
|
|
|
@ -4,6 +4,7 @@ const Component = require('react').Component
|
|||
const connect = require('react-redux').connect
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('../../../../../ui/app/actions')
|
||||
const { getDPath } = require('../../../util')
|
||||
|
||||
module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
|
||||
|
||||
|
@ -16,6 +17,7 @@ function mapStateToProps (state) {
|
|||
return {
|
||||
warning: state.appState.warning,
|
||||
dPath: state.metamask.dPath,
|
||||
provider: state.metamask.provider,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +117,9 @@ RevealSeedConfirmation.prototype.checkConfirmation = function (event) {
|
|||
}
|
||||
}
|
||||
|
||||
RevealSeedConfirmation.prototype.revealSeedWords = function () {
|
||||
RevealSeedConfirmation.prototype.revealSeedWords = async function () {
|
||||
const password = document.getElementById('password-box').value
|
||||
this.props.dispatch(actions.requestRevealSeed(password, this.props.dPath))
|
||||
const isCreatedWithCorrectDPath = this.props.dispatch(actions.isCreatedWithCorrectDPath())
|
||||
const dPath = getDPath(this.props.provider.type, isCreatedWithCorrectDPath)
|
||||
this.props.dispatch(actions.requestRevealSeed(password, dPath))
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const PersistentForm = require('../../../lib/persistent-form')
|
|||
const connect = require('react-redux').connect
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('../../../../ui/app/actions')
|
||||
const { getDPath } = require('../../util')
|
||||
|
||||
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
|
||||
|
||||
|
@ -15,6 +16,7 @@ function mapStateToProps (state) {
|
|||
return {
|
||||
warning: state.appState.warning,
|
||||
forgottenPassword: state.appState.forgottenPassword,
|
||||
provider: state.metamask.provider,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,5 +187,7 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
|
|||
// submit
|
||||
this.warning = null
|
||||
this.props.dispatch(actions.displayWarning(this.warning))
|
||||
this.props.dispatch(actions.createNewVaultAndRestore(password, seed))
|
||||
const isCreatedWithCorrectDPath = true
|
||||
const dPath = getDPath(this.props.provider.type, isCreatedWithCorrectDPath)
|
||||
this.props.dispatch(actions.createNewVaultAndRestore(password, seed, dPath))
|
||||
}
|
||||
|
|
|
@ -111,11 +111,13 @@ UnlockScreen.prototype.onKeyPress = function (event) {
|
|||
UnlockScreen.prototype.submitPassword = async function (event) {
|
||||
const element = event.target
|
||||
const password = element.value
|
||||
const props = this.props
|
||||
// reset input
|
||||
element.value = ''
|
||||
try {
|
||||
const dPath = getDPath(this.props.provider.type) || this.props.dPath
|
||||
await this.props.dispatch(actions.tryUnlockMetamask(password, dPath))
|
||||
const isCreatedWithCorrectDPath = await props.dispatch(actions.isCreatedWithCorrectDPath())
|
||||
const dPath = getDPath(props.provider.type, isCreatedWithCorrectDPath) || props.dPath
|
||||
await props.dispatch(actions.tryUnlockMetamask(password, dPath))
|
||||
} catch (e) {
|
||||
log.error(e)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ const {
|
|||
RSK,
|
||||
RSK_TESTNET,
|
||||
RSK_TICK,
|
||||
// customDPaths,
|
||||
customDPaths,
|
||||
} = require('../../app/scripts/controllers/network/enums')
|
||||
|
||||
const valueTable = {
|
||||
|
@ -88,6 +88,7 @@ module.exports = {
|
|||
ifHardwareAcc,
|
||||
getAllKeyRingsAccounts,
|
||||
ifRSK,
|
||||
ifETC,
|
||||
ifRSKByProviderType,
|
||||
ifPOA,
|
||||
toChecksumAddress,
|
||||
|
@ -432,6 +433,12 @@ function ifRSK (network) {
|
|||
return numericNet === RSK_CODE || numericNet === RSK_TESTNET_CODE
|
||||
}
|
||||
|
||||
function ifETC (network) {
|
||||
if (!network) return false
|
||||
const numericNet = isNaN(network) ? network : parseInt(network)
|
||||
return numericNet === CLASSIC_CODE
|
||||
}
|
||||
|
||||
function ifRSKByProviderType (type) {
|
||||
if (!type) return false
|
||||
return type === RSK || type === RSK_TESTNET
|
||||
|
@ -557,14 +564,16 @@ function getNetworkID ({ network }) {
|
|||
}
|
||||
}
|
||||
|
||||
function getDPath (network) {
|
||||
// todo: return when the robust solution will be ready
|
||||
return `m/44'/60'/0'/0`
|
||||
// return customDPaths[network] || `m/44'/60'/0'/0`
|
||||
function getDPath (networkType, isCreatedWithCorrectDPath) {
|
||||
if (isCreatedWithCorrectDPath) {
|
||||
return customDPaths[networkType] || `m/44'/60'/0'/0`
|
||||
} else {
|
||||
return `m/44'/60'/0'/0`
|
||||
}
|
||||
}
|
||||
|
||||
function setDPath (keyring, network) {
|
||||
const dPath = getDPath(network)
|
||||
function setDPath (keyring, networkType, isCreatedWithCorrectDPath) {
|
||||
const dPath = getDPath(networkType, isCreatedWithCorrectDPath)
|
||||
if (dPath && keyring.setHdPath) {
|
||||
keyring.setHdPath(dPath)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
const path = require('path')
|
||||
const Func = require('./func').Functions
|
||||
const account1 = '0x2E428ABd9313D256d64D1f69fe3929C3BE18fD1f'
|
||||
// todo:
|
||||
// const account1RSK = '0x7a9bc05F7441d862d1B83CB724861a9872FF43fe'
|
||||
const account1RSK = '0x2E428aBd9313D256d64D1f69fe3929c3Be18Fd1F'
|
||||
const account1RSK = '0x7a9bc05F7441d862d1B83CB724861a9872FF43fe'
|
||||
const account2 = '0xd7b7AFeCa35e32594e29504771aC847E2a803742'
|
||||
const testsFolder = './test-cases'
|
||||
const setup = require(`${testsFolder}/setup.spec`)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const assert = require('assert')
|
||||
const clone = require('clone')
|
||||
const KeyringController = require('eth-keychain-controller')
|
||||
import assert from 'assert'
|
||||
import clone from 'clone'
|
||||
import KeyringController from 'eth-keychain-controller'
|
||||
import seedPhraseVerifier from '../../../app/scripts/lib/seed-phrase-verifier'
|
||||
const firstTimeState = require('../../../app/scripts/first-time-state')
|
||||
const seedPhraseVerifier = require('../../../app/scripts/lib/seed-phrase-verifier')
|
||||
const mockEncryptor = require('../../lib/mock-encryptor')
|
||||
|
||||
describe('SeedPhraseVerifier', function () {
|
||||
|
|
|
@ -235,7 +235,7 @@ describe('Actions', () => {
|
|||
|
||||
createNewVaultAndRestoreSpy = sinon.stub(background, 'createNewVaultAndRestore')
|
||||
|
||||
createNewVaultAndRestoreSpy.callsFake((password, seed, callback) => {
|
||||
createNewVaultAndRestoreSpy.callsFake((password, seed, dPath, callback) => {
|
||||
callback(new Error('error'))
|
||||
})
|
||||
|
||||
|
|
|
@ -370,6 +370,7 @@ const actions = {
|
|||
getRequestAccountTabIds,
|
||||
setOpenMetamaskTabsIDs,
|
||||
getOpenMetamaskTabsIds,
|
||||
isCreatedWithCorrectDPath,
|
||||
closeCurrentNotificationWindow,
|
||||
closeNotificationWindow,
|
||||
}
|
||||
|
@ -406,7 +407,12 @@ function tryUnlockMetamask (password, dPath) {
|
|||
})
|
||||
.then(() => {
|
||||
dispatch(actions.unlockSucceeded())
|
||||
return forceUpdateMetamaskState(dispatch)
|
||||
return updateMetamaskStateFromBackground()
|
||||
.then(newState => {
|
||||
newState = Object.assign(newState, {dPath: dPath})
|
||||
dispatch(actions.updateMetamaskState(newState))
|
||||
forceUpdateMetamaskState(dispatch)
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
|
@ -436,6 +442,21 @@ function tryUnlockMetamask (password, dPath) {
|
|||
}
|
||||
}
|
||||
|
||||
function isCreatedWithCorrectDPath () {
|
||||
return dispatch => {
|
||||
return new Promise((resolve, reject) => {
|
||||
background.isCreatedWithCorrectDPath((err, isCreatedWithCorrectDPath) => {
|
||||
if (err) {
|
||||
dispatch(actions.displayWarning(err.message))
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
resolve(isCreatedWithCorrectDPath)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function transitionForward () {
|
||||
return {
|
||||
type: this.TRANSITION_FORWARD,
|
||||
|
@ -468,7 +489,7 @@ function confirmSeedWords () {
|
|||
}
|
||||
}
|
||||
|
||||
function createNewVaultAndRestore (password, seed) {
|
||||
function createNewVaultAndRestore (password, seed, dPath) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
log.debug(`background.createNewVaultAndRestore`)
|
||||
|
@ -479,7 +500,7 @@ function createNewVaultAndRestore (password, seed) {
|
|||
return reject(err)
|
||||
}
|
||||
|
||||
background.createNewVaultAndRestore(password, seed, (err) => {
|
||||
background.createNewVaultAndRestore(password, seed, dPath, (err) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
|
@ -491,6 +512,11 @@ function createNewVaultAndRestore (password, seed) {
|
|||
.then(() => dispatch(actions.unMarkPasswordForgotten()))
|
||||
.then(() => {
|
||||
dispatch(actions.showAccountsPage())
|
||||
updateMetamaskStateFromBackground()
|
||||
.then(newState => {
|
||||
newState = Object.assign(newState, {dPath: dPath})
|
||||
dispatch(actions.updateMetamaskState(newState))
|
||||
})
|
||||
dispatch(actions.hideLoadingIndication())
|
||||
})
|
||||
.catch(err => {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const extend = require('xtend')
|
||||
import extend from 'xtend'
|
||||
import log from 'loglevel'
|
||||
const actions = require('../actions')
|
||||
const txHelper = require('../../lib/tx-helper')
|
||||
const { customHdPaths } = require('../../../old-ui/app/components/connect-hardware/util.js')
|
||||
const log = require('loglevel')
|
||||
|
||||
module.exports = reduceApp
|
||||
|
||||
|
|
Loading…
Reference in New Issue