Skip to content

Commit

Permalink
Attempt ENS resolution on any valid domain name (#8059)
Browse files Browse the repository at this point in the history
ENS currently supports a variety of tlds in addition to `.eth`, and
more will be supported in the future. Rather than hard-code a list of
supported ENS tlds, all valid domain names will now be interpreted as
potential ENS addresses in our address input component.

Closes #7978
  • Loading branch information
Gudahtt committed Feb 18, 2020
1 parent 5156191 commit 5b030bf
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 13 deletions.
58 changes: 58 additions & 0 deletions test/unit/util_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,64 @@ describe('util', function () {
})
})

describe('isValidDomainName', function () {
it('should return true when given a valid domain name', function () {
assert.strictEqual(util.isValidDomainName('foo.bar'), true)
})

it('should return true when given a valid subdomain', function () {
assert.strictEqual(util.isValidDomainName('foo.foo.bar'), true)
})

it('should return true when given a single-character domain', function () {
assert.strictEqual(util.isValidDomainName('f.bar'), true)
})

it('should return true when given a unicode TLD', function () {
assert.strictEqual(util.isValidDomainName('台灣.中国'), true)
})

it('should return false when given a domain with unacceptable ASCII characters', function () {
assert.strictEqual(util.isValidDomainName('$.bar'), false)
})

it('should return false when given a TLD that starts with a dash', function () {
assert.strictEqual(util.isValidDomainName('foo.-bar'), false)
})

it('should return false when given a TLD that ends with a dash', function () {
assert.strictEqual(util.isValidDomainName('foo.bar-'), false)
})

it('should return false when given a domain name with a chunk that starts with a dash', function () {
assert.strictEqual(util.isValidDomainName('-foo.bar'), false)
})

it('should return false when given a domain name with a chunk that ends with a dash', function () {
assert.strictEqual(util.isValidDomainName('foo-.bar'), false)
})

it('should return false when given a bare TLD', function () {
assert.strictEqual(util.isValidDomainName('bar'), false)
})

it('should return false when given a domain that starts with a period', function () {
assert.strictEqual(util.isValidDomainName('.bar'), false)
})

it('should return false when given a subdomain that starts with a period', function () {
assert.strictEqual(util.isValidDomainName('.foo.bar'), false)
})

it('should return false when given a domain that ends with a period', function () {
assert.strictEqual(util.isValidDomainName('bar.'), false)
})

it('should return false when given a 1-character TLD', function () {
assert.strictEqual(util.isValidDomainName('foo.b'), false)
})
})

describe('#numericBalance', function () {
it('should return a BN 0 if given nothing', function () {
var result = util.numericBalance()
Expand Down
13 changes: 10 additions & 3 deletions ui/app/helpers/utils/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const abi = require('human-standard-token-abi')
const ethUtil = require('ethereumjs-util')
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
import { DateTime } from 'luxon'
import punycode from 'punycode'

const MIN_GAS_PRICE_GWEI_BN = new ethUtil.BN(1)
const GWEI_FACTOR = new ethUtil.BN(1e9)
Expand Down Expand Up @@ -36,7 +37,7 @@ module.exports = {
miniAddressSummary: miniAddressSummary,
isAllOneCase: isAllOneCase,
isValidAddress: isValidAddress,
isValidENSAddress,
isValidDomainName,
numericBalance: numericBalance,
parseBalance: parseBalance,
formatBalance: formatBalance,
Expand Down Expand Up @@ -99,8 +100,14 @@ function isValidAddress (address) {
return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
}

function isValidENSAddress (address) {
return address.match(/^.{3,}\.(eth|test|xyz)$/)
function isValidDomainName (address) {
const match = punycode.toASCII(address)
.toLowerCase()
// Checks that the domain consists of at least one valid domain pieces separated by periods, followed by a tld
// Each piece of domain name has only the characters a-z, 0-9, and a hyphen (but not at the start or end of chunk)
// A chunk has minimum length of 1, but minimum tld is set to 2 for now (no 1-character tlds exist yet)
.match(/^(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)+[a-z0-9][-a-z0-9]*[a-z0-9]$/)
return match !== null
}

function isInvalidChecksumAddress (address) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import c from 'classnames'
import { isValidENSAddress, isValidAddress, isValidAddressHead } from '../../../../helpers/utils/util'
import {ellipsify} from '../../send.utils'
import { isValidDomainName, isValidAddress, isValidAddressHead } from '../../../../helpers/utils/util'
import { ellipsify } from '../../send.utils'

import debounce from 'debounce'
import copyToClipboard from 'copy-to-clipboard/index'
Expand Down Expand Up @@ -91,7 +91,7 @@ export default class EnsInput extends Component {
this.props.updateEnsResolution(address)
})
.catch((reason) => {
if (isValidENSAddress(recipient) && reason.message === 'ENS name not defined.') {
if (isValidDomainName(recipient) && reason.message === 'ENS name not defined.') {
this.props.updateEnsResolutionError(this.context.t('ensNotFoundOnCurrentNetwork'))
} else {
log.error(reason)
Expand Down Expand Up @@ -124,7 +124,7 @@ export default class EnsInput extends Component {
return
}

if (isValidENSAddress(input)) {
if (isValidDomainName(input)) {
this.lookupEnsName(input)
} else if (onValidAddressTyped && isValidAddress(input)) {
onValidAddressTyped(input)
Expand Down
6 changes: 3 additions & 3 deletions ui/app/pages/send/send.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import {
calcGasTotal,
} from './send.utils.js'
import {
isValidENSAddress,
isValidDomainName,
} from '../../helpers/utils/util'

import {
Expand Down Expand Up @@ -126,8 +126,8 @@ function mapDispatchToProps (dispatch) {
updateSendEnsResolution: (ensResolution) => dispatch(updateSendEnsResolution(ensResolution)),
updateSendEnsResolutionError: (message) => dispatch(updateSendEnsResolutionError(message)),
updateToNicknameIfNecessary: (to, toNickname, addressBook) => {
if (isValidENSAddress(toNickname)) {
const addressBookEntry = addressBook.find(({ address}) => to === address) || {}
if (isValidDomainName(toNickname)) {
const addressBookEntry = addressBook.find(({ address }) => to === address) || {}
if (!addressBookEntry.name !== toNickname) {
dispatch(updateSendTo(to, addressBookEntry.name || ''))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PropTypes from 'prop-types'
import Identicon from '../../../../components/ui/identicon'
import TextField from '../../../../components/ui/text-field'
import { CONTACT_LIST_ROUTE } from '../../../../helpers/constants/routes'
import { isValidAddress, isValidENSAddress } from '../../../../helpers/utils/util'
import EnsInput from '../../../../pages/send/send-content/add-recipient/ens-input'
import { isValidAddress, isValidDomainName } from '../../../../helpers/utils/util'
import EnsInput from '../../../send/send-content/add-recipient/ens-input'
import PageContainerFooter from '../../../../components/ui/page-container/page-container-footer'
import debounce from 'lodash.debounce'

Expand Down Expand Up @@ -51,7 +51,7 @@ export default class AddContact extends PureComponent {

validate = address => {
const valid = isValidAddress(address)
const validEnsAddress = isValidENSAddress(address)
const validEnsAddress = isValidDomainName(address)
if (valid || validEnsAddress || address === '') {
this.setState({ error: '', ethAddress: address })
} else {
Expand Down

0 comments on commit 5b030bf

Please sign in to comment.