Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V7.7.5 Upstream Changes #124

Merged
merged 12 commits into from Feb 25, 2020
@@ -715,6 +715,12 @@
"knownAddressRecipient": {
"message": "Known contract address."
},
"knownTokenWarning": {
"message": "This action will edit tokens that are already listed in your wallet, which can be used to phish you. Only approve if you are certain that you mean to change what these tokens represent."
},
"reusedTokenNameWarning": {
"message": "A token here reuses a symbol from another token you watch, this can be confusing or deceptive."
},
"invalidAddressRecipientNotEthNetwork": {
"message": "Not ETH network, set to lowercase"
},
@@ -1261,6 +1267,12 @@
"message": "Spend limit requested by $1",
"description": "Origin of the site requesting the spend limit"
},
"spendLimitTooLarge": {
"message": "Spend limit too large"
},
"spendLimitInvalid": {
"message": "Spend limit invalid; must be a positive number"
},
"switchNetworks": {
"message": "Switch Networks"
},
@@ -251,7 +251,10 @@ function setupController (initState, initLangCode) {
})

const provider = controller.provider
setupEnsIpfsResolver({ provider })
setupEnsIpfsResolver({
getCurrentNetwork: controller.getCurrentNetwork,
provider,
})

// submit rpc requests to mesh-metrics
controller.networkController.on('rpc-req', (data) => {
@@ -9,7 +9,7 @@ const ObjectMultiplex = require('obj-multiplex')
const extension = require('extensionizer')
const PortStream = require('extension-port-stream')

const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString()
const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js'), 'utf8')
const inpageSuffix = '//# sourceURL=' + extension.runtime.getURL('inpage.js') + '\n'
const inpageBundle = inpageContent + inpageSuffix

@@ -17,7 +17,7 @@ function getBuyEthUrl ({ network, amount, address, service }) {

switch (service) {
case 'wyre':
return `https://dash.sendwyre.com/sign-up`
return `https://pay.sendwyre.com/?dest=ethereum:${address}&destCurrency=ETH&accountId=AC-7AG3W4XH4N2&paymentMethod=debit-card`
case 'coinswitch':
return `https://metamask.coinswitch.co/?address=${address}&to=eth`
case 'coinbase':
@@ -52,19 +52,23 @@ function hexValueIsEmpty (value) {
return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value)
}

/**
* Returns the registry address for the given chain ID
* @param {number} chainId the chain ID
* @returns {string|null} the registry address if known, null otherwise
*/
function getRegistryForChainId (chainId) {
switch (chainId) {
// mainnet
case 1:
return '0x314159265dd8dbb310642f98f50c066173c1259b'
// ropsten
// falls through
case 3:
return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
// rinkeby
// falls through
case 4:
return '0xe7410170f87102df0055eb195163a03b7f2bff4a'
// goerli
// falls through
case 5:
return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
// Mainnet, Ropsten, Rinkeby, and Goerli, respectively, use the same address
return '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
default:
return null
}
}
@@ -6,7 +6,7 @@ const supportedTopLevelDomains = ['eth']

module.exports = setupEnsIpfsResolver

function setupEnsIpfsResolver ({ provider }) {
function setupEnsIpfsResolver ({ provider, getCurrentNetwork }) {

// install listener
const urlPatterns = supportedTopLevelDomains.map(tld => `*://*.${tld}/*`)
@@ -23,7 +23,10 @@ function setupEnsIpfsResolver ({ provider }) {
async function webRequestDidFail (details) {
const { tabId, url } = details
// ignore requests that are not associated with tabs
if (tabId === -1) return
// only attempt ENS resolution on mainnet
if (tabId === -1 || getCurrentNetwork() !== '1') {
return
}
// parse ens name
const urlData = urlUtil.parse(url)
const { hostname: name, path, search, hash: fragment } = urlData
@@ -675,6 +675,10 @@ module.exports = class MetamaskController extends EventEmitter {
})
}

getCurrentNetwork = () => {
return this.networkController.store.getState().network
}

/**
* Collects all the information that we want to share
* with the mobile client for syncing purposes
@@ -20,7 +20,7 @@ describe('buy-eth-url', function () {
it('returns coinbase url with amount and address for network 1', function () {
const wyreUrl = getBuyEthUrl(mainnet)

assert.equal(wyreUrl, 'https://dash.sendwyre.com/sign-up')
assert.equal(wyreUrl, 'https://pay.sendwyre.com/?dest=ethereum:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc&destCurrency=ETH&accountId=AC-7AG3W4XH4N2&paymentMethod=debit-card')

})

@@ -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()
@@ -1,20 +1,26 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import log from 'loglevel'
import Modal from '../../modal'
import Identicon from '../../../ui/identicon'
import TextField from '../../../ui/text-field'
import { calcTokenAmount } from '../../../../helpers/utils/token-util'
import classnames from 'classnames'
import BigNumber from 'bignumber.js'

const MAX_UNSIGNED_256_INT = new BigNumber(2).pow(256).minus(1).toString(10)

export default class EditApprovalPermission extends PureComponent {
static propTypes = {
decimals: PropTypes.number,
hideModal: PropTypes.func.isRequired,
selectedIdentity: PropTypes.object,
tokenAmount: PropTypes.string,
customTokenAmount: PropTypes.string,
tokenSymbol: PropTypes.string,
tokenBalance: PropTypes.string,
setCustomAmount: PropTypes.func,
origin: PropTypes.string,
origin: PropTypes.string.isRequired,
}

static contextTypes = {
@@ -26,7 +32,7 @@ export default class EditApprovalPermission extends PureComponent {
selectedOptionIsUnlimited: !this.props.customTokenAmount,
}

renderModalContent () {
renderModalContent (error) {
const { t } = this.context
const {
hideModal,
@@ -61,7 +67,7 @@ export default class EditApprovalPermission extends PureComponent {
<div>{ t('balance') }</div>
</div>
<div className="edit-approval-permission__account-info__balance">
{`${tokenBalance} ${tokenSymbol}`}
{`${Number(tokenBalance).toPrecision(9)} ${tokenSymbol}`}
</div>
</div>
<div className="edit-approval-permission__edit-section">
@@ -89,7 +95,7 @@ export default class EditApprovalPermission extends PureComponent {
'edit-approval-permission__edit-section__option-label--selected': selectedOptionIsUnlimited,
})}>
{
tokenAmount < tokenBalance
(new BigNumber(tokenAmount)).lessThan(new BigNumber(tokenBalance))
? t('proposedApprovalLimit')
: t('unlimited')
}
@@ -98,7 +104,7 @@ export default class EditApprovalPermission extends PureComponent {
{ t('spendLimitRequestedBy', [origin]) }
</div>
<div className="edit-approval-permission__edit-section__option-value" >
{`${tokenAmount} ${tokenSymbol}`}
{`${Number(tokenAmount)} ${tokenSymbol}`}
</div>
</div>
</div>
@@ -127,8 +133,7 @@ export default class EditApprovalPermission extends PureComponent {
<div className="edit-approval-permission__edit-section__option-input" >
<TextField
type="number"
min="0"
placeholder={ `${customTokenAmount || tokenAmount} ${tokenSymbol}` }
placeholder={ `${Number(customTokenAmount || tokenAmount)} ${tokenSymbol}` }
onChange={(event) => {
this.setState({ customSpendLimit: event.target.value })
if (selectedOptionIsUnlimited) {
@@ -138,6 +143,7 @@ export default class EditApprovalPermission extends PureComponent {
fullWidth
margin="dense"
value={ this.state.customSpendLimit }
error={error}
/>
</div>
</div>
@@ -147,10 +153,44 @@ export default class EditApprovalPermission extends PureComponent {
)
}

validateSpendLimit () {
const { t } = this.context
const { decimals } = this.props
const { selectedOptionIsUnlimited, customSpendLimit } = this.state

if (selectedOptionIsUnlimited || !customSpendLimit) {
return
}

let customSpendLimitNumber
try {
customSpendLimitNumber = new BigNumber(customSpendLimit)
} catch (error) {
log.debug(`Error converting '${customSpendLimit}' to BigNumber:`, error)
return t('spendLimitInvalid')
}

if (customSpendLimitNumber.isNegative()) {
return t('spendLimitInvalid')
}

const maxTokenAmount = calcTokenAmount(MAX_UNSIGNED_256_INT, decimals)
if (customSpendLimitNumber.greaterThan(maxTokenAmount)) {
return t('spendLimitTooLarge')
}
}

render () {
const { t } = this.context
const { setCustomAmount, hideModal, customTokenAmount } = this.props
const { selectedOptionIsUnlimited, customSpendLimit } = this.state

const error = this.validateSpendLimit()
const disabled = Boolean(
(customSpendLimit === customTokenAmount && !selectedOptionIsUnlimited) ||
error
)

return (
<Modal
onSubmit={() => {
@@ -161,9 +201,9 @@ export default class EditApprovalPermission extends PureComponent {
submitType="primary"
contentClass="edit-approval-permission-modal-content"
containerClass="edit-approval-permission-modal-container"
submitDisabled={ (customSpendLimit === customTokenAmount) && !selectedOptionIsUnlimited }
submitDisabled={disabled}
>
{ this.renderModalContent() }
{ this.renderModalContent(error) }
</Modal>
)
}
@@ -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)
@@ -36,7 +37,7 @@ module.exports = {
miniAddressSummary: miniAddressSummary,
isAllOneCase: isAllOneCase,
isValidAddress: isValidAddress,
isValidENSAddress,
isValidDomainName,
numericBalance: numericBalance,
parseBalance: parseBalance,
formatBalance: formatBalance,
@@ -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) {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.