Merge pull request #3998 from MetaMask/i3988-checksummed-address

Show checksum addresses on UI
feature/default_network_editable
Dan Finlay 7 years ago committed by GitHub
commit 40f0a40edf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 2
      test/integration/lib/tx-list-items.js
  3. 5
      ui/app/components/account-dropdowns.js
  4. 5
      ui/app/components/dropdowns/components/account-dropdowns.js
  5. 7
      ui/app/components/modals/export-private-key-modal.js
  6. 13
      ui/app/components/qr-code.js
  7. 3
      ui/app/components/send/account-list-item.js
  8. 5
      ui/app/components/tx-list-item.js
  9. 4
      ui/app/components/tx-view.js
  10. 9
      ui/app/components/wallet-view.js
  11. 15
      ui/app/util.js
  12. 4
      ui/lib/icon-factory.js

@ -6,6 +6,7 @@
- Improved performance of 3D fox logo. - Improved performance of 3D fox logo.
- Fetch token prices based on contract address, not symbol - Fetch token prices based on contract address, not symbol
- Fix bug that prevents setting language locale in settings. - Fix bug that prevents setting language locale in settings.
- Show checksum addresses throughout the UI
## 4.5.5 Fri Apr 06 2018 ## 4.5.5 Fri Apr 06 2018

@ -53,7 +53,7 @@ async function runTxListItemsTest(assert, done) {
const confirmedTokenTx = txListItems[6] const confirmedTokenTx = txListItems[6]
const confirmedTokenTxAddress = await findAsync($(confirmedTokenTx), '.tx-list-account') 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 rejectedTx = txListItems[7]
const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.tx-list-status') const rejectedTxRenderedStatus = await findAsync($(rejectedTx), '.tx-list-status')

@ -7,8 +7,8 @@ const connect = require('react-redux').connect
const Dropdown = require('./dropdown').Dropdown const Dropdown = require('./dropdown').Dropdown
const DropdownMenuItem = require('./dropdown').DropdownMenuItem const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('./identicon') const Identicon = require('./identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard') const copyToClipboard = require('copy-to-clipboard')
const { checksumAddress } = require('../util')
class AccountDropdowns extends Component { class AccountDropdowns extends Component {
constructor (props) { constructor (props) {
@ -212,8 +212,7 @@ class AccountDropdowns extends Component {
closeMenu: () => {}, closeMenu: () => {},
onClick: () => { onClick: () => {
const { selected } = this.props const { selected } = this.props
const checkSumAddress = selected && ethUtil.toChecksumAddress(selected) copyToClipboard(checksumAddress(selected))
copyToClipboard(checkSumAddress)
}, },
}, },
this.context.t('copyAddress'), this.context.t('copyAddress'),

@ -7,7 +7,7 @@ const connect = require('react-redux').connect
const Dropdown = require('./dropdown').Dropdown const Dropdown = require('./dropdown').Dropdown
const DropdownMenuItem = require('./dropdown').DropdownMenuItem const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('../../identicon') const Identicon = require('../../identicon')
const ethUtil = require('ethereumjs-util') const { checksumAddress } = require('../../../util')
const copyToClipboard = require('copy-to-clipboard') const copyToClipboard = require('copy-to-clipboard')
const { formatBalance } = require('../../../util') const { formatBalance } = require('../../../util')
@ -311,8 +311,7 @@ class AccountDropdowns extends Component {
closeMenu: () => {}, closeMenu: () => {},
onClick: () => { onClick: () => {
const { selected } = this.props const { selected } = this.props
const checkSumAddress = selected && ethUtil.toChecksumAddress(selected) copyToClipboard(checksumAddress(selected))
copyToClipboard(checkSumAddress)
}, },
style: Object.assign( style: Object.assign(
dropdownMenuItemStyle, dropdownMenuItemStyle,

@ -3,12 +3,13 @@ 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 connect = require('react-redux').connect const connect = require('react-redux').connect
const ethUtil = require('ethereumjs-util') const { stripHexPrefix } = require('ethereumjs-util')
const actions = require('../../actions') const actions = require('../../actions')
const AccountModalContainer = require('./account-modal-container') const AccountModalContainer = require('./account-modal-container')
const { getSelectedIdentity } = require('../../selectors') const { getSelectedIdentity } = require('../../selectors')
const ReadOnlyInput = require('../readonly-input') const ReadOnlyInput = require('../readonly-input')
const copyToClipboard = require('copy-to-clipboard') const copyToClipboard = require('copy-to-clipboard')
const { checksumAddress } = require('../../util')
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
@ -60,7 +61,7 @@ ExportPrivateKeyModal.prototype.renderPasswordLabel = function (privateKey) {
} }
ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) { ExportPrivateKeyModal.prototype.renderPasswordInput = function (privateKey) {
const plainKey = privateKey && ethUtil.stripHexPrefix(privateKey) const plainKey = privateKey && stripHexPrefix(privateKey)
return privateKey return privateKey
? h(ReadOnlyInput, { ? h(ReadOnlyInput, {
@ -121,7 +122,7 @@ ExportPrivateKeyModal.prototype.render = function () {
h(ReadOnlyInput, { h(ReadOnlyInput, {
wrapperClass: 'ellip-address-wrapper', wrapperClass: 'ellip-address-wrapper',
inputClass: 'qr-ellip-address ellip-address', inputClass: 'qr-ellip-address ellip-address',
value: address, value: checksumAddress(address),
}), }),
h('div.account-modal-divider'), h('div.account-modal-divider'),

@ -3,8 +3,9 @@ const h = require('react-hyperscript')
const qrCode = require('qrcode-npm').qrcode const qrCode = require('qrcode-npm').qrcode
const inherits = require('util').inherits const inherits = require('util').inherits
const connect = require('react-redux').connect const connect = require('react-redux').connect
const isHexPrefixed = require('ethereumjs-util').isHexPrefixed const { isHexPrefixed } = require('ethereumjs-util')
const ReadOnlyInput = require('./readonly-input') const ReadOnlyInput = require('./readonly-input')
const { checksumAddress } = require('../util')
module.exports = connect(mapStateToProps)(QrCodeView) module.exports = connect(mapStateToProps)(QrCodeView)
@ -24,16 +25,16 @@ function QrCodeView () {
QrCodeView.prototype.render = function () { QrCodeView.prototype.render = function () {
const props = this.props const props = this.props
const Qr = props.Qr const { message, data } = props.Qr
const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}` const address = `${isHexPrefixed(data) ? 'ethereum:' : ''}${data}`
const qrImage = qrCode(4, 'M') const qrImage = qrCode(4, 'M')
qrImage.addData(address) qrImage.addData(address)
qrImage.make() qrImage.make()
return h('.div.flex-column.flex-center', [ return h('.div.flex-column.flex-center', [
Array.isArray(Qr.message) Array.isArray(message)
? h('.message-container', this.renderMultiMessage()) ? 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', { this.props.warning ? this.props.warning && h('span.error.flex-center', {
style: { style: {
@ -50,7 +51,7 @@ QrCodeView.prototype.render = function () {
h(ReadOnlyInput, { h(ReadOnlyInput, {
wrapperClass: 'ellip-address-wrapper', wrapperClass: 'ellip-address-wrapper',
inputClass: 'qr-ellip-address', inputClass: 'qr-ellip-address',
value: Qr.data, value: checksumAddress(data),
}), }),
]) ])
} }

@ -2,6 +2,7 @@ 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 connect = require('react-redux').connect const connect = require('react-redux').connect
const { checksumAddress } = require('../../util')
const Identicon = require('../identicon') const Identicon = require('../identicon')
const CurrencyDisplay = require('./currency-display') const CurrencyDisplay = require('./currency-display')
const { conversionRateSelector, getCurrentCurrency } = require('../../selectors') 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, { displayBalance && h(CurrencyDisplay, {
primaryCurrency: 'ETH', primaryCurrency: 'ETH',

@ -9,6 +9,7 @@ const abiDecoder = require('abi-decoder')
abiDecoder.addABI(abi) abiDecoder.addABI(abi)
const Identicon = require('./identicon') const Identicon = require('./identicon')
const contractMap = require('eth-contract-metadata') const contractMap = require('eth-contract-metadata')
const { checksumAddress } = require('../util')
const actions = require('../actions') const actions = require('../actions')
const { conversionUtil, multiplyCurrencies } = require('../conversion-util') const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
@ -74,10 +75,12 @@ TxListItem.prototype.getAddressText = function () {
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { name: txDataName, params = [] } = decodedData || {} const { name: txDataName, params = [] } = decodedData || {}
const { value } = params[0] || {} const { value } = params[0] || {}
const checksummedAddress = checksumAddress(address)
const checksummedValue = checksumAddress(value)
let addressText let addressText
if (txDataName === 'transfer' || address) { if (txDataName === 'transfer' || address) {
const addressToRender = txDataName === 'transfer' ? value : address const addressToRender = txDataName === 'transfer' ? checksummedValue : checksummedAddress
addressText = `${addressToRender.slice(0, 10)}...${addressToRender.slice(-4)}` addressText = `${addressToRender.slice(0, 10)}...${addressToRender.slice(-4)}`
} else if (isMsg) { } else if (isMsg) {
addressText = this.context.t('sigRequest') addressText = this.context.t('sigRequest')

@ -2,13 +2,13 @@ const Component = require('react').Component
const PropTypes = require('prop-types') const PropTypes = require('prop-types')
const connect = require('react-redux').connect const connect = require('react-redux').connect
const h = require('react-hyperscript') const h = require('react-hyperscript')
const ethUtil = require('ethereumjs-util')
const inherits = require('util').inherits const inherits = require('util').inherits
const { withRouter } = require('react-router-dom') const { withRouter } = require('react-router-dom')
const { compose } = require('recompose') const { compose } = require('recompose')
const actions = require('../actions') const actions = require('../actions')
const selectors = require('../selectors') const selectors = require('../selectors')
const { SEND_ROUTE } = require('../routes') const { SEND_ROUTE } = require('../routes')
const { checksumAddress: toChecksumAddress } = require('../util')
const BalanceComponent = require('./balance-component') const BalanceComponent = require('./balance-component')
const TxList = require('./tx-list') const TxList = require('./tx-list')
@ -32,7 +32,7 @@ function mapStateToProps (state) {
const network = state.metamask.network const network = state.metamask.network
const selectedTokenAddress = state.metamask.selectedTokenAddress const selectedTokenAddress = state.metamask.selectedTokenAddress
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0] const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
const checksumAddress = selectedAddress && ethUtil.toChecksumAddress(selectedAddress) const checksumAddress = toChecksumAddress(selectedAddress)
const identity = identities[selectedAddress] const identity = identities[selectedAddress]
return { return {

@ -6,6 +6,7 @@ const { withRouter } = require('react-router-dom')
const { compose } = require('recompose') const { compose } = require('recompose')
const inherits = require('util').inherits const inherits = require('util').inherits
const classnames = require('classnames') const classnames = require('classnames')
const { checksumAddress } = require('../util')
const Identicon = require('./identicon') const Identicon = require('./identicon')
// const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns // const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns
const Tooltip = require('./tooltip-v2.js') const Tooltip = require('./tooltip-v2.js')
@ -107,6 +108,8 @@ WalletView.prototype.render = function () {
// temporary logs + fake extra wallets // temporary logs + fake extra wallets
// console.log('walletview, selectedAccount:', selectedAccount) // console.log('walletview, selectedAccount:', selectedAccount)
const checksummedAddress = checksumAddress(selectedAddress)
const keyring = keyrings.find((kr) => { const keyring = keyrings.find((kr) => {
return kr.accounts.includes(selectedAddress) || return kr.accounts.includes(selectedAddress) ||
kr.accounts.includes(selectedIdentity.address) kr.accounts.includes(selectedIdentity.address)
@ -135,7 +138,7 @@ WalletView.prototype.render = function () {
}, [ }, [
h(Identicon, { h(Identicon, {
diameter: 54, diameter: 54,
address: selectedAddress, address: checksummedAddress,
}), }),
h('span.account-name', { h('span.account-name', {
@ -158,7 +161,7 @@ WalletView.prototype.render = function () {
'wallet-view__address__pressed': this.state.copyToClipboardPressed, 'wallet-view__address__pressed': this.state.copyToClipboardPressed,
}), }),
onClick: () => { onClick: () => {
copyToClipboard(selectedAddress) copyToClipboard(checksummedAddress)
this.setState({ hasCopied: true }) this.setState({ hasCopied: true })
setTimeout(() => this.setState({ hasCopied: false }), 3000) setTimeout(() => this.setState({ hasCopied: false }), 3000)
}, },
@ -169,7 +172,7 @@ WalletView.prototype.render = function () {
this.setState({ copyToClipboardPressed: false }) 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' } }), h('i.fa.fa-clipboard', { style: { marginLeft: '8px' } }),
]), ]),
]), ]),

@ -57,6 +57,7 @@ module.exports = {
isInvalidChecksumAddress, isInvalidChecksumAddress,
allNull, allNull,
getTokenAddressFromTokenObject, getTokenAddressFromTokenObject,
checksumAddress,
} }
function valuesFor (obj) { function valuesFor (obj) {
@ -67,7 +68,7 @@ function valuesFor (obj) {
function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includeHex = true) { function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includeHex = true) {
if (!address) return '' if (!address) return ''
let checked = ethUtil.toChecksumAddress(address) let checked = checksumAddress(address)
if (!includeHex) { if (!includeHex) {
checked = ethUtil.stripHexPrefix(checked) checked = ethUtil.stripHexPrefix(checked)
} }
@ -76,7 +77,7 @@ function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includ
function miniAddressSummary (address) { function miniAddressSummary (address) {
if (!address) return '' if (!address) return ''
var checked = ethUtil.toChecksumAddress(address) var checked = checksumAddress(address)
return checked ? checked.slice(0, 4) + '...' + checked.slice(-4) : '...' return checked ? checked.slice(0, 4) + '...' + checked.slice(-4) : '...'
} }
@ -287,3 +288,13 @@ function allNull (obj) {
function getTokenAddressFromTokenObject (token) { function getTokenAddressFromTokenObject (token) {
return Object.values(token)[0].address.toLowerCase() 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) : ''
}

@ -1,6 +1,6 @@
var iconFactory var iconFactory
const isValidAddress = require('ethereumjs-util').isValidAddress const isValidAddress = require('ethereumjs-util').isValidAddress
const toChecksumAddress = require('ethereumjs-util').toChecksumAddress const { checksumAddress } = require('../app/util')
const contractMap = require('eth-contract-metadata') const contractMap = require('eth-contract-metadata')
module.exports = function (jazzicon) { module.exports = function (jazzicon) {
@ -16,7 +16,7 @@ function IconFactory (jazzicon) {
} }
IconFactory.prototype.iconForAddress = function (address, diameter) { IconFactory.prototype.iconForAddress = function (address, diameter) {
const addr = toChecksumAddress(address) const addr = checksumAddress(address)
if (iconExistsFor(addr)) { if (iconExistsFor(addr)) {
return imageElFor(addr) return imageElFor(addr)
} }

Loading…
Cancel
Save