Merge pull request #89 from poanetwork/token-per-net

(Feature) Support of token per network basis
This commit is contained in:
Victor Baranov 2018-08-23 10:19:29 +03:00 committed by GitHub
commit 6f8a0ab938
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 91 additions and 75 deletions

View File

@ -2,6 +2,7 @@
## Current Master ## Current Master
- [#89](https://github.com/poanetwork/metamask-extension/pull/89): (Feature) Support of token per network basis
- [#85](https://github.com/poanetwork/metamask-extension/pull/85): (Upgrade) node, npm packages versions - [#85](https://github.com/poanetwork/metamask-extension/pull/85): (Upgrade) node, npm packages versions
- [#84](https://github.com/poanetwork/metamask-extension/pull/84): (Fix) Change green color - [#84](https://github.com/poanetwork/metamask-extension/pull/84): (Fix) Change green color
- [#83](https://github.com/poanetwork/metamask-extension/pull/83): (Feature) Changing of password - [#83](https://github.com/poanetwork/metamask-extension/pull/83): (Feature) Changing of password

View File

@ -50,7 +50,7 @@ class DetectTokensController {
ethContract.balanceOf(this.selectedAddress, (error, result) => { ethContract.balanceOf(this.selectedAddress, (error, result) => {
if (!error) { if (!error) {
if (!result.isZero()) { if (!result.isZero()) {
this._preferences.addToken(contractAddress, contracts[contractAddress].symbol, contracts[contractAddress].decimals) this._preferences.addToken(contractAddress, contracts[contractAddress].symbol, contracts[contractAddress].decimals, this.network)
} }
} else { } else {
warn(`MetaMask - DetectTokensController balance fetch failed for ${contractAddress}.`, error) warn(`MetaMask - DetectTokensController balance fetch failed for ${contractAddress}.`, error)

View File

@ -217,13 +217,13 @@ class PreferencesController {
* @returns {Promise<array>} Promises the new array of AddedToken objects. * @returns {Promise<array>} Promises the new array of AddedToken objects.
* *
*/ */
async addToken (rawAddress, symbol, decimals) { async addToken (rawAddress, symbol, decimals, network) {
const address = normalizeAddress(rawAddress) const address = normalizeAddress(rawAddress)
const newEntry = { address, symbol, decimals } const newEntry = { address, symbol, decimals, network }
const tokens = this.store.getState().tokens const tokens = this.store.getState().tokens
const previousEntry = tokens.find((token, index) => { const previousEntry = tokens.find((token, index) => {
return token.address === address return (token.address === address && parseInt(token.network) === parseInt(network))
}) })
const previousIndex = tokens.indexOf(previousEntry) const previousIndex = tokens.indexOf(previousEntry)

View File

@ -18,6 +18,7 @@ module.exports = connect(mapStateToProps)(AddTokenScreen)
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
identities: state.metamask.identities, identities: state.metamask.identities,
network: state.metamask.network,
} }
} }
@ -36,6 +37,7 @@ AddTokenScreen.prototype.render = function () {
const state = this.state const state = this.state
const props = this.props const props = this.props
const { warning, symbol, decimals } = state const { warning, symbol, decimals } = state
const { network } = props
return ( return (
h('.flex-column.flex-grow', [ h('.flex-column.flex-grow', [
@ -167,7 +169,7 @@ AddTokenScreen.prototype.render = function () {
if (!valid) return if (!valid) return
const { address, symbol, decimals } = this.state const { address, symbol, decimals } = this.state
this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals)) this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals, network))
.then(() => { .then(() => {
this.props.dispatch(actions.goHome()) this.props.dispatch(actions.goHome())
}) })

View File

@ -49,8 +49,6 @@ TokenCell.prototype.render = function () {
}, },
}, ''), }, ''),
h('hr'),
/* /*
h('button', { h('button', {
onClick: this.send.bind(this, address), onClick: this.send.bind(this, address),

View File

@ -1,7 +1,7 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker') const TokenTracker = require('eth-token-watcher')
const TokenCell = require('./token-cell.js') const TokenCell = require('./token-cell.js')
const log = require('loglevel') const log = require('loglevel')
@ -48,8 +48,9 @@ TokenList.prototype.render = function () {
]) ])
} }
const tokenViews = tokens.map((tokenData, ind) => { const tokensFromCurrentNetwork = tokens.filter(token => (parseInt(token.network) === parseInt(network) || !token.network))
tokenData.network = network
const tokenViews = tokensFromCurrentNetwork.map((tokenData, ind) => {
tokenData.userAddress = userAddress tokenData.userAddress = userAddress
const isLastTokenCell = ind === (tokens.length - 1) const isLastTokenCell = ind === (tokens.length - 1)
return h(TokenCell, { return h(TokenCell, {
@ -97,12 +98,14 @@ TokenList.prototype.render = function () {
TokenList.prototype.renderTokenStatusBar = function () { TokenList.prototype.renderTokenStatusBar = function () {
const { tokens } = this.state const { tokens } = this.state
const { network } = this.props
const tokensFromCurrentNetwork = tokens.filter(token => (parseInt(token.network) === parseInt(network) || !token.network))
let msg let msg
if (tokens.length === 1) { if (tokensFromCurrentNetwork.length === 1) {
msg = `You own 1 token` msg = `You own 1 token`
} else if (tokens.length > 1) { } else if (tokensFromCurrentNetwork.length > 1) {
msg = `You own ${tokens.length} tokens` msg = `You own ${tokensFromCurrentNetwork.length} tokens`
} else { } else {
msg = `No tokens found` msg = `No tokens found`
} }

75
package-lock.json generated
View File

@ -8863,10 +8863,10 @@
} }
} }
}, },
"eth-token-tracker": { "eth-token-watcher": {
"version": "1.1.4", "version": "1.1.5",
"resolved": "https://registry.npmjs.org/eth-token-tracker/-/eth-token-tracker-1.1.4.tgz", "resolved": "https://registry.npmjs.org/eth-token-watcher/-/eth-token-watcher-1.1.5.tgz",
"integrity": "sha1-Kf8kV9Zr+juO5JDoP/QP0M8s7EE=", "integrity": "sha512-7cHDwFtSXpHonTRNDmY8a7yzLnoRQG9VJIllczE+L/NzBW6qc0m+cJcQjgFYiXvwtyFwyf8eaYseawTSOe7Efg==",
"requires": { "requires": {
"deep-equal": "^1.0.1", "deep-equal": "^1.0.1",
"eth-block-tracker": "^1.0.7", "eth-block-tracker": "^1.0.7",
@ -8901,13 +8901,6 @@
"ethjs-util": "^0.1.3", "ethjs-util": "^0.1.3",
"pify": "^2.3.0", "pify": "^2.3.0",
"tape": "^4.6.3" "tape": "^4.6.3"
},
"dependencies": {
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
}
} }
}, },
"ethjs": { "ethjs": {
@ -8927,19 +8920,6 @@
"number-to-bn": "1.7.0" "number-to-bn": "1.7.0"
}, },
"dependencies": { "dependencies": {
"ethjs-format": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.3.tgz",
"integrity": "sha1-m9hnyu6CstvtmEYAuzAiDPPLWDA=",
"requires": {
"bn.js": "4.11.6",
"ethjs-schema": "^0.1.6",
"ethjs-util": "0.1.3",
"is-hex-prefixed": "1.0.0",
"number-to-bn": "1.7.0",
"strip-hex-prefix": "1.0.0"
}
},
"ethjs-query": { "ethjs-query": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.0.tgz", "resolved": "https://registry.npmjs.org/ethjs-query/-/ethjs-query-0.3.0.tgz",
@ -8949,11 +8929,6 @@
"ethjs-rpc": "0.1.5" "ethjs-rpc": "0.1.5"
} }
}, },
"ethjs-schema": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/ethjs-schema/-/ethjs-schema-0.1.9.tgz",
"integrity": "sha1-hYwqXacGrgSBK0zosetLSSHjMJI="
},
"ethjs-util": { "ethjs-util": {
"version": "0.1.3", "version": "0.1.3",
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
@ -8988,12 +8963,12 @@
} }
}, },
"ethjs-format": { "ethjs-format": {
"version": "0.2.2", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.2.tgz", "resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.3.tgz",
"integrity": "sha1-1zs6YFwuElcHn3B3/VRI6ZjOD80=", "integrity": "sha1-m9hnyu6CstvtmEYAuzAiDPPLWDA=",
"requires": { "requires": {
"bn.js": "4.11.6", "bn.js": "4.11.6",
"ethjs-schema": "0.1.5", "ethjs-schema": "^0.1.6",
"ethjs-util": "0.1.3", "ethjs-util": "0.1.3",
"is-hex-prefixed": "1.0.0", "is-hex-prefixed": "1.0.0",
"number-to-bn": "1.7.0", "number-to-bn": "1.7.0",
@ -9018,6 +8993,19 @@
"requires": { "requires": {
"ethjs-format": "0.2.2", "ethjs-format": "0.2.2",
"ethjs-rpc": "0.1.5" "ethjs-rpc": "0.1.5"
},
"dependencies": {
"ethjs-format": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/ethjs-format/-/ethjs-format-0.2.2.tgz",
"integrity": "sha1-1zs6YFwuElcHn3B3/VRI6ZjOD80=",
"requires": {
"bn.js": "4.11.6",
"ethjs-schema": "0.1.5",
"ethjs-util": "0.1.3",
"is-hex-prefixed": "1.0.0",
"number-to-bn": "1.7.0",
"strip-hex-prefix": "1.0.0"
} }
}, },
"ethjs-schema": { "ethjs-schema": {
@ -9025,10 +9013,31 @@
"resolved": "https://registry.npmjs.org/ethjs-schema/-/ethjs-schema-0.1.5.tgz", "resolved": "https://registry.npmjs.org/ethjs-schema/-/ethjs-schema-0.1.5.tgz",
"integrity": "sha1-WXQOOzl3vNu5sRvDBoIB6Kzquw0=" "integrity": "sha1-WXQOOzl3vNu5sRvDBoIB6Kzquw0="
}, },
"ethjs-util": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.3.tgz",
"integrity": "sha1-39XqSkANxeQhqInK9H4IGtp4u1U=",
"requires": {
"is-hex-prefixed": "1.0.0",
"strip-hex-prefix": "1.0.0"
}
}
}
},
"ethjs-schema": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/ethjs-schema/-/ethjs-schema-0.1.9.tgz",
"integrity": "sha1-hYwqXacGrgSBK0zosetLSSHjMJI="
},
"js-sha3": { "js-sha3": {
"version": "0.5.5", "version": "0.5.5",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.5.tgz",
"integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko=" "integrity": "sha1-uvDA6MVK1ZA0R9+Wreekobynmko="
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
} }
} }
}, },

View File

@ -113,7 +113,7 @@
"eth-phishing-detect": "^1.1.4", "eth-phishing-detect": "^1.1.4",
"eth-query": "^2.1.2", "eth-query": "^2.1.2",
"eth-sig-util": "^1.4.2", "eth-sig-util": "^1.4.2",
"eth-token-tracker": "^1.1.4", "eth-token-watcher": "^1.1.5",
"eth-trezor-keyring": "^0.1.0", "eth-trezor-keyring": "^0.1.0",
"ethereumjs-abi": "^0.6.4", "ethereumjs-abi": "^0.6.4",
"ethereumjs-tx": "^1.3.0", "ethereumjs-tx": "^1.3.0",

View File

@ -70,33 +70,33 @@ describe('DetectTokensController', () => {
sandbox.stub(controller, 'detectTokenBalance') sandbox.stub(controller, 'detectTokenBalance')
.withArgs('0x0D262e5dC4A06a0F1c90cE79C7a60C09DfC884E4') .withArgs('0x0D262e5dC4A06a0F1c90cE79C7a60C09DfC884E4')
.returns(preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8)) .returns(preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8, 1))
.withArgs('0xBC86727E770de68B1060C91f6BB6945c73e10388') .withArgs('0xBC86727E770de68B1060C91f6BB6945c73e10388')
.returns(preferences.addToken('0xbc86727e770de68b1060c91f6bb6945c73e10388', 'XNK', 18)) .returns(preferences.addToken('0xbc86727e770de68b1060c91f6bb6945c73e10388', 'XNK', 18, 1))
await controller.detectNewTokens() await controller.detectNewTokens()
assert.deepEqual(preferences.store.getState().tokens, [{address: '0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', decimals: 8, symbol: 'J8T'}, assert.deepEqual(preferences.store.getState().tokens, [{address: '0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', decimals: 8, symbol: 'J8T', network: 1},
{address: '0xbc86727e770de68b1060c91f6bb6945c73e10388', decimals: 18, symbol: 'XNK'}]) {address: '0xbc86727e770de68b1060c91f6bb6945c73e10388', decimals: 18, symbol: 'XNK', network: 1}])
}) })
it('should not detect same token while in main network', async () => { it('should not detect same token while in main network', async () => {
const network = new NetworkController() const network = new NetworkController()
network.setProviderType('mainnet') network.setProviderType('mainnet')
const preferences = new PreferencesController() const preferences = new PreferencesController()
preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8) preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8, 1)
const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore }) const controller = new DetectTokensController({ preferences: preferences, network: network, keyringMemStore: keyringMemStore })
controller.isOpen = true controller.isOpen = true
controller.isUnlocked = true controller.isUnlocked = true
sandbox.stub(controller, 'detectTokenBalance') sandbox.stub(controller, 'detectTokenBalance')
.withArgs('0x0D262e5dC4A06a0F1c90cE79C7a60C09DfC884E4') .withArgs('0x0D262e5dC4A06a0F1c90cE79C7a60C09DfC884E4')
.returns(preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8)) .returns(preferences.addToken('0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', 'J8T', 8, 1))
.withArgs('0xBC86727E770de68B1060C91f6BB6945c73e10388') .withArgs('0xBC86727E770de68B1060C91f6BB6945c73e10388')
.returns(preferences.addToken('0xbc86727e770de68b1060c91f6bb6945c73e10388', 'XNK', 18)) .returns(preferences.addToken('0xbc86727e770de68b1060c91f6bb6945c73e10388', 'XNK', 18, 1))
await controller.detectNewTokens() await controller.detectNewTokens()
assert.deepEqual(preferences.store.getState().tokens, [{address: '0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', decimals: 8, symbol: 'J8T'}, assert.deepEqual(preferences.store.getState().tokens, [{address: '0x0d262e5dc4a06a0f1c90ce79c7a60c09dfc884e4', decimals: 8, symbol: 'J8T', network: 1},
{address: '0xbc86727e770de68b1060c91f6bb6945c73e10388', decimals: 18, symbol: 'XNK'}]) {address: '0xbc86727e770de68b1060c91f6bb6945c73e10388', decimals: 18, symbol: 'XNK', network: 1}])
}) })
it('should trigger detect new tokens when change address', async () => { it('should trigger detect new tokens when change address', async () => {

View File

@ -129,12 +129,13 @@ describe('preferences controller', function () {
const address = '0xabcdef1234567' const address = '0xabcdef1234567'
const symbol = 'ABBR' const symbol = 'ABBR'
const decimals = 5 const decimals = 5
const network = 1
await preferencesController.setSelectedAddress('0x7e57e2') await preferencesController.setSelectedAddress('0x7e57e2')
await preferencesController.addToken(address, symbol, decimals) await preferencesController.addToken(address, symbol, decimals, network)
const newDecimals = 6 const newDecimals = 6
await preferencesController.addToken(address, symbol, newDecimals) await preferencesController.addToken(address, symbol, newDecimals, network)
const tokens = preferencesController.getTokens() const tokens = preferencesController.getTokens()
assert.equal(tokens.length, 1, 'one token added') assert.equal(tokens.length, 1, 'one token added')
@ -143,19 +144,21 @@ describe('preferences controller', function () {
assert.equal(added.address, address, 'set address correctly') assert.equal(added.address, address, 'set address correctly')
assert.equal(added.symbol, symbol, 'set symbol correctly') assert.equal(added.symbol, symbol, 'set symbol correctly')
assert.equal(added.decimals, newDecimals, 'updated decimals correctly') assert.equal(added.decimals, newDecimals, 'updated decimals correctly')
assert.equal(added.network, network, 'set network correctly')
}) })
it('should allow adding tokens to two separate addresses', async function () { it('should allow adding tokens to two separate addresses', async function () {
const address = '0xabcdef1234567' const address = '0xabcdef1234567'
const symbol = 'ABBR' const symbol = 'ABBR'
const decimals = 5 const decimals = 5
const network = 1
await preferencesController.setSelectedAddress('0x7e57e2') await preferencesController.setSelectedAddress('0x7e57e2')
await preferencesController.addToken(address, symbol, decimals) await preferencesController.addToken(address, symbol, decimals, network)
assert.equal(preferencesController.getTokens().length, 1, 'one token added for 1st address') assert.equal(preferencesController.getTokens().length, 1, 'one token added for 1st address')
await preferencesController.setSelectedAddress('0xda22le') await preferencesController.setSelectedAddress('0xda22le')
await preferencesController.addToken(address, symbol, decimals) await preferencesController.addToken(address, symbol, decimals, network)
assert.equal(preferencesController.getTokens().length, 1, 'one token added for 2nd address') assert.equal(preferencesController.getTokens().length, 1, 'one token added for 2nd address')
}) })
}) })
@ -172,15 +175,15 @@ describe('preferences controller', function () {
it('should remove a token from its state', async function () { it('should remove a token from its state', async function () {
await preferencesController.setSelectedAddress('0x7e57e2') await preferencesController.setSelectedAddress('0x7e57e2')
await preferencesController.addToken('0xa', 'A', 4) await preferencesController.addToken('0xa', 'A', 4, 1)
await preferencesController.addToken('0xb', 'B', 5) await preferencesController.addToken('0xb', 'B', 5, 1)
await preferencesController.removeToken('0xa') await preferencesController.removeToken('0xa')
const tokens = preferencesController.getTokens() const tokens = preferencesController.getTokens()
assert.equal(tokens.length, 1, 'one token removed') assert.equal(tokens.length, 1, 'one token removed')
const [token1] = tokens const [token1] = tokens
assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5}) assert.deepEqual(token1, {address: '0xb', symbol: 'B', decimals: 5, network: 1})
}) })
}) })

View File

@ -1596,11 +1596,11 @@ function showRemoveTokenPage (token, transitionForward = true) {
} }
} }
function addToken (address, symbol, decimals) { function addToken (address, symbol, decimals, network) {
return (dispatch) => { return (dispatch) => {
dispatch(actions.showLoadingIndication()) dispatch(actions.showLoadingIndication())
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
background.addToken(address, symbol, decimals, (err, tokens) => { background.addToken(address, symbol, decimals, network, (err, tokens) => {
dispatch(actions.hideLoadingIndication()) dispatch(actions.hideLoadingIndication())
if (err) { if (err) {
dispatch(actions.displayWarning(err.message)) dispatch(actions.displayWarning(err.message))
@ -1634,16 +1634,16 @@ function addTokens (tokens) {
return dispatch => { return dispatch => {
if (Array.isArray(tokens)) { if (Array.isArray(tokens)) {
dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens[0]))) dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens[0])))
return Promise.all(tokens.map(({ address, symbol, decimals }) => ( return Promise.all(tokens.map(({ address, symbol, decimals, network }) => (
dispatch(addToken(address, symbol, decimals)) dispatch(addToken(address, symbol, decimals, network))
))) )))
} else { } else {
dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens))) dispatch(actions.setSelectedToken(getTokenAddressFromTokenObject(tokens)))
return Promise.all( return Promise.all(
Object Object
.entries(tokens) .entries(tokens)
.map(([_, { address, symbol, decimals }]) => ( .map(([_, { address, symbol, decimals, network }]) => (
dispatch(addToken(address, symbol, decimals)) dispatch(addToken(address, symbol, decimals, network))
)) ))
) )
} }

View File

@ -1,7 +1,7 @@
const Component = require('react').Component const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker') const TokenTracker = require('eth-token-watcher')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const selectors = require('../selectors') const selectors = require('../selectors')
const log = require('loglevel') const log = require('loglevel')

View File

@ -2,7 +2,7 @@ const Component = require('react').Component
const PropTypes = require('prop-types') const PropTypes = require('prop-types')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker') const TokenTracker = require('eth-token-watcher')
const TokenCell = require('./token-cell.js') const TokenCell = require('./token-cell.js')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const selectors = require('../selectors') const selectors = require('../selectors')

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import TokenTracker from 'eth-token-tracker' import TokenTracker from 'eth-token-watcher'
const withTokenTracker = WrappedComponent => { const withTokenTracker = WrappedComponent => {
return class TokenTrackerWrappedComponent extends Component { return class TokenTrackerWrappedComponent extends Component {