diff --git a/CHANGELOG.md b/CHANGELOG.md index 9537657ec..3e2b481de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Improved performance of 3D fox logo. - Fetch token prices based on contract address, not symbol - Fix bug that prevents setting language locale in settings. +- Show checksum addresses throughout the UI ## 4.5.5 Fri Apr 06 2018 diff --git a/test/integration/lib/tx-list-items.js b/test/integration/lib/tx-list-items.js index d0056eb94..0c0c5a77f 100644 --- a/test/integration/lib/tx-list-items.js +++ b/test/integration/lib/tx-list-items.js @@ -53,7 +53,7 @@ async function runTxListItemsTest(assert, done) { const confirmedTokenTx = txListItems[6] const confirmedTokenTxAddress = await findAsync($(confirmedTokenTx), '.tx-list-account') - assert.equal(confirmedTokenTxAddress[0].textContent, '0xe7884118...81a9', 'confirmedTokenTx has correct address') + assert.equal(confirmedTokenTxAddress[0].textContent, '0xE7884118...81a9', 'confirmedTokenTx has correct address') const rejectedTx = txListItems[7] const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.tx-list-status') diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js index 03955e077..043008a36 100644 --- a/ui/app/components/account-dropdowns.js +++ b/ui/app/components/account-dropdowns.js @@ -7,8 +7,8 @@ const connect = require('react-redux').connect const Dropdown = require('./dropdown').Dropdown const DropdownMenuItem = require('./dropdown').DropdownMenuItem const Identicon = require('./identicon') -const ethUtil = require('ethereumjs-util') const copyToClipboard = require('copy-to-clipboard') +const { checksumAddress } = require('../util') class AccountDropdowns extends Component { constructor (props) { @@ -212,8 +212,7 @@ class AccountDropdowns extends Component { closeMenu: () => {}, onClick: () => { const { selected } = this.props - const checkSumAddress = selected && ethUtil.toChecksumAddress(selected) - copyToClipboard(checkSumAddress) + copyToClipboard(checksumAddress(selected)) }, }, this.context.t('copyAddress'), diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js index a133f0e29..179b6617f 100644 --- a/ui/app/components/dropdowns/components/account-dropdowns.js +++ b/ui/app/components/dropdowns/components/account-dropdowns.js @@ -7,7 +7,7 @@ const connect = require('react-redux').connect const Dropdown = require('./dropdown').Dropdown const DropdownMenuItem = require('./dropdown').DropdownMenuItem const Identicon = require('../../identicon') -const ethUtil = require('ethereumjs-util') +const { checksumAddress } = require('../../../util') const copyToClipboard = require('copy-to-clipboard') const { formatBalance } = require('../../../util') @@ -311,8 +311,7 @@ class AccountDropdowns extends Component { closeMenu: () => {}, onClick: () => { const { selected } = this.props - const checkSumAddress = selected && ethUtil.toChecksumAddress(selected) - copyToClipboard(checkSumAddress) + copyToClipboard(checksumAddress(selected)) }, style: Object.assign( dropdownMenuItemStyle, diff --git a/ui/app/components/modals/export-private-key-modal.js b/ui/app/components/modals/export-private-key-modal.js index 1f80aed39..447e43b7a 100644 --- a/ui/app/components/modals/export-private-key-modal.js +++ b/ui/app/components/modals/export-private-key-modal.js @@ -3,12 +3,13 @@ const PropTypes = require('prop-types') const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect -const ethUtil = require('ethereumjs-util') +const { stripHexPrefix } = require('ethereumjs-util') const actions = require('../../actions') const AccountModalContainer = require('./account-modal-container') const { getSelectedIdentity } = require('../../selectors') const ReadOnlyInput = require('../readonly-input') const copyToClipboard = require('copy-to-clipboard') +const { checksumAddress } = require('../../util') function mapStateToProps (state) { return { @@ -60,7 +61,7 @@ ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) { } ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) { - const plainKey = privateKey && ethUtil.stripHexPrefix(privateKey) + const plainKey = privateKey && stripHexPrefix(privateKey) return privateKey ? h(ReadOnlyInput, { @@ -121,7 +122,7 @@ ExportPrivateKeyModal.prototype.render = function () { h(ReadOnlyInput, { wrapperClass: 'ellip-address-wrapper', inputClass: 'qr-ellip-address ellip-address', - value: address, + value: checksumAddress(address), }), h('div.account-modal-divider'), diff --git a/ui/app/components/qr-code.js b/ui/app/components/qr-code.js index 83885539c..3b2c62f49 100644 --- a/ui/app/components/qr-code.js +++ b/ui/app/components/qr-code.js @@ -3,8 +3,9 @@ const h = require('react-hyperscript') const qrCode = require('qrcode-npm').qrcode const inherits = require('util').inherits const connect = require('react-redux').connect -const isHexPrefixed = require('ethereumjs-util').isHexPrefixed +const { isHexPrefixed } = require('ethereumjs-util') const ReadOnlyInput = require('./readonly-input') +const { checksumAddress } = require('../util') module.exports = connect(mapStateToProps)(QrCodeView) @@ -24,16 +25,16 @@ function QrCodeView () { QrCodeView.prototype.render = function () { const props = this.props - const Qr = props.Qr - const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}` + const { message, data } = props.Qr + const address = `${isHexPrefixed(data) ? 'ethereum:' : ''}${data}` const qrImage = qrCode(4, 'M') qrImage.addData(address) qrImage.make() return h('.div.flex-column.flex-center', [ - Array.isArray(Qr.message) + Array.isArray(message) ? h('.message-container', this.renderMultiMessage()) - : Qr.message && h('.qr-header', Qr.message), + : message && h('.qr-header', message), this.props.warning ? this.props.warning && h('span.error.flex-center', { style: { @@ -50,7 +51,7 @@ QrCodeView.prototype.render = function () { h(ReadOnlyInput, { wrapperClass: 'ellip-address-wrapper', inputClass: 'qr-ellip-address', - value: Qr.data, + value: checksumAddress(data), }), ]) } diff --git a/ui/app/components/send/account-list-item.js b/ui/app/components/send/account-list-item.js index 1ad3f69c1..b5e604a6e 100644 --- a/ui/app/components/send/account-list-item.js +++ b/ui/app/components/send/account-list-item.js @@ -2,6 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect +const { checksumAddress } = require('../../util') const Identicon = require('../identicon') const CurrencyDisplay = require('./currency-display') const { conversionRateSelector, getCurrentCurrency } = require('../../selectors') @@ -56,7 +57,7 @@ AccountListItem.prototype.render = function () { ]), - displayAddress && name && h('div.account-list-item__account-address', address), + displayAddress && name && h('div.account-list-item__account-address', checksumAddress(address)), displayBalance && h(CurrencyDisplay, { primaryCurrency: 'ETH', diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js index 403f83ab9..bd4ea80a6 100644 --- a/ui/app/components/tx-list-item.js +++ b/ui/app/components/tx-list-item.js @@ -9,6 +9,7 @@ const abiDecoder = require('abi-decoder') abiDecoder.addABI(abi) const Identicon = require('./identicon') const contractMap = require('eth-contract-metadata') +const { checksumAddress } = require('../util') const actions = require('../actions') const { conversionUtil, multiplyCurrencies } = require('../conversion-util') @@ -74,10 +75,12 @@ TxListItem.prototype.getAddressText = function () { const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) const { name: txDataName, params = [] } = decodedData || {} const { value } = params[0] || {} + const checksummedAddress = checksumAddress(address) + const checksummedValue = checksumAddress(value) let addressText if (txDataName === 'transfer' || address) { - const addressToRender = txDataName === 'transfer' ? value : address + const addressToRender = txDataName === 'transfer' ? checksummedValue : checksummedAddress addressText = `${addressToRender.slice(0, 10)}...${addressToRender.slice(-4)}` } else if (isMsg) { addressText = this.context.t('sigRequest') diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js index 80aac35c4..263f992c0 100644 --- a/ui/app/components/tx-view.js +++ b/ui/app/components/tx-view.js @@ -2,13 +2,13 @@ const Component = require('react').Component const PropTypes = require('prop-types') const connect = require('react-redux').connect const h = require('react-hyperscript') -const ethUtil = require('ethereumjs-util') const inherits = require('util').inherits const { withRouter } = require('react-router-dom') const { compose } = require('recompose') const actions = require('../actions') const selectors = require('../selectors') const { SEND_ROUTE } = require('../routes') +const { checksumAddress: toChecksumAddress } = require('../util') const BalanceComponent = require('./balance-component') const TxList = require('./tx-list') @@ -32,7 +32,7 @@ function mapStateToProps (state) { const network = state.metamask.network const selectedTokenAddress = state.metamask.selectedTokenAddress const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] - const checksumAddress = selectedAddress && ethUtil.toChecksumAddress(selectedAddress) + const checksumAddress = toChecksumAddress(selectedAddress) const identity = identities[selectedAddress] return { diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index e3e1b8903..9e430f87b 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -6,6 +6,7 @@ const { withRouter } = require('react-router-dom') const { compose } = require('recompose') const inherits = require('util').inherits const classnames = require('classnames') +const { checksumAddress } = require('../util') const Identicon = require('./identicon') // const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns const Tooltip = require('./tooltip-v2.js') @@ -107,6 +108,8 @@ WalletView.prototype.render = function () { // temporary logs + fake extra wallets // console.log('walletview, selectedAccount:', selectedAccount) + const checksummedAddress = checksumAddress(selectedAddress) + const keyring = keyrings.find((kr) => { return kr.accounts.includes(selectedAddress) || kr.accounts.includes(selectedIdentity.address) @@ -135,7 +138,7 @@ WalletView.prototype.render = function () { }, [ h(Identicon, { diameter: 54, - address: selectedAddress, + address: checksummedAddress, }), h('span.account-name', { @@ -158,7 +161,7 @@ WalletView.prototype.render = function () { 'wallet-view__address__pressed': this.state.copyToClipboardPressed, }), onClick: () => { - copyToClipboard(selectedAddress) + copyToClipboard(checksummedAddress) this.setState({ hasCopied: true }) setTimeout(() => this.setState({ hasCopied: false }), 3000) }, @@ -169,7 +172,7 @@ WalletView.prototype.render = function () { this.setState({ copyToClipboardPressed: false }) }, }, [ - `${selectedAddress.slice(0, 4)}...${selectedAddress.slice(-4)}`, + `${checksummedAddress.slice(0, 4)}...${checksummedAddress.slice(-4)}`, h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }), ]), ]), diff --git a/ui/app/util.js b/ui/app/util.js index bbe2bb09e..8e9390dfb 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -57,6 +57,7 @@ module.exports = { isInvalidChecksumAddress, allNull, getTokenAddressFromTokenObject, + checksumAddress, } function valuesFor (obj) { @@ -67,7 +68,7 @@ function valuesFor (obj) { function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includeHex = true) { if (!address) return '' - let checked = ethUtil.toChecksumAddress(address) + let checked = checksumAddress(address) if (!includeHex) { checked = ethUtil.stripHexPrefix(checked) } @@ -76,7 +77,7 @@ function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includ function miniAddressSummary (address) { if (!address) return '' - var checked = ethUtil.toChecksumAddress(address) + var checked = checksumAddress(address) return checked ? checked.slice(0, 4) + '...' + checked.slice(-4) : '...' } @@ -287,3 +288,13 @@ function allNull (obj) { function getTokenAddressFromTokenObject (token) { return Object.values(token)[0].address.toLowerCase() } + +/** + * Safely checksumms a potentially-null address + * + * @param {String} [address] - address to checksum + * @returns {String} - checksummed address + */ +function checksumAddress (address) { + return address ? ethUtil.toChecksumAddress(address) : '' +} diff --git a/ui/lib/icon-factory.js b/ui/lib/icon-factory.js index 31498a3a9..7fadbceff 100644 --- a/ui/lib/icon-factory.js +++ b/ui/lib/icon-factory.js @@ -1,6 +1,6 @@ var iconFactory const isValidAddress = require('ethereumjs-util').isValidAddress -const toChecksumAddress = require('ethereumjs-util').toChecksumAddress +const { checksumAddress } = require('../app/util') const contractMap = require('eth-contract-metadata') module.exports = function (jazzicon) { @@ -16,7 +16,7 @@ function IconFactory (jazzicon) { } IconFactory.prototype.iconForAddress = function (address, diameter) { - const addr = toChecksumAddress(address) + const addr = checksumAddress(address) if (iconExistsFor(addr)) { return imageElFor(addr) }