Merge pull request #7922 from MetaMask/Version-v7.7.4

Version v7.7.4 RC
feature/default_network_editable
Dan Finlay 5 years ago committed by GitHub
commit de3b98c9ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      CHANGELOG.md
  2. 6
      app/_locales/en/messages.json
  3. 2
      app/manifest.json
  4. 5
      app/scripts/background.js
  5. 20
      app/scripts/lib/ens-ipfs/resolver.js
  6. 7
      app/scripts/lib/ens-ipfs/setup.js
  7. 4
      app/scripts/metamask-controller.js
  8. 58
      ui/app/components/app/modals/edit-approval-permission/edit-approval-permission.component.js
  9. 18
      ui/app/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js
  10. 22
      ui/app/pages/confirm-approve/confirm-approve.component.js
  11. 20
      ui/app/pages/confirm-approve/confirm-approve.container.js
  12. 45
      ui/app/pages/confirm-approve/confirm-approve.util.js
  13. 8
      yarn.lock

@ -2,6 +2,13 @@
## Current Develop Branch
## 7.7.4 Wed Jan 29 2020
- [#7918](https://github.com/MetaMask/metamask-extension/pull/7918): Update data on Approve screen after updating custom spend limit
- [#7919](https://github.com/MetaMask/metamask-extension/pull/7919): Allow editing max spend limit
- [#7920](https://github.com/MetaMask/metamask-extension/pull/7920): Validate custom spend limit
- [#7944](https://github.com/MetaMask/metamask-extension/pull/7944): Only resolve ENS on mainnet
- [#7954](https://github.com/MetaMask/metamask-extension/pull/7954): Update ENS registry addresses
## 7.7.3 Fri Jan 24 2020
- [#7894](https://github.com/MetaMask/metamask-extension/pull/7894): Update GABA dependency version
- [#7901](https://github.com/MetaMask/metamask-extension/pull/7901): Use eth-contract-metadata@1.12.1

@ -1261,6 +1261,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"
},

@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
"version": "7.7.3",
"version": "7.7.4",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",

@ -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) => {

@ -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

@ -1,12 +1,18 @@
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,
@ -14,7 +20,7 @@ export default class EditApprovalPermission extends PureComponent {
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>
)
}

@ -15,6 +15,7 @@ export default class ConfirmApproveContent extends Component {
static propTypes = {
amount: PropTypes.string,
txFeeTotal: PropTypes.string,
decimals: PropTypes.number,
tokenAmount: PropTypes.string,
customTokenAmount: PropTypes.string,
tokenSymbol: PropTypes.string,
@ -100,7 +101,7 @@ export default class ConfirmApproveContent extends Component {
<div className="confirm-approve-content__small-text">{ t('accessAndSpendNotice', [origin]) }</div>
<div className="flex-row">
<div className="confirm-approve-content__label">{ t('amountWithColon') }</div>
<div className="confirm-approve-content__medium-text">{ `${customTokenAmount || tokenAmount} ${tokenSymbol}` }</div>
<div className="confirm-approve-content__medium-text">{ `${Number(customTokenAmount || tokenAmount)} ${tokenSymbol}` }</div>
</div>
<div className="flex-row">
<div className="confirm-approve-content__label">{ t('toWithColon') }</div>
@ -124,6 +125,7 @@ export default class ConfirmApproveContent extends Component {
render () {
const { t } = this.context
const {
decimals,
siteImage,
tokenAmount,
customTokenAmount,
@ -159,7 +161,15 @@ export default class ConfirmApproveContent extends Component {
>
<div
className="confirm-approve-content__medium-link-text cursor-pointer"
onClick={() => showEditApprovalPermissionModal({ customTokenAmount, tokenAmount, tokenSymbol, setCustomAmount, tokenBalance, origin })}
onClick={() => showEditApprovalPermissionModal({
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenSymbol,
tokenBalance,
})}
>
{ t('editPermission') }
</div>
@ -201,10 +211,12 @@ export default class ConfirmApproveContent extends Component {
showEdit: true,
onEditClick: () => showEditApprovalPermissionModal({
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenSymbol,
tokenBalance,
setCustomAmount,
}),
})}
</div>

@ -15,7 +15,7 @@ export default class ConfirmApprove extends Component {
static propTypes = {
tokenAddress: PropTypes.string,
toAddress: PropTypes.string,
tokenAmount: PropTypes.number,
tokenAmount: PropTypes.string,
tokenSymbol: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
ethTransactionTotal: PropTypes.string,
@ -33,7 +33,7 @@ export default class ConfirmApprove extends Component {
}
static defaultProps = {
tokenAmount: 0,
tokenAmount: '0',
}
state = {
@ -69,12 +69,16 @@ export default class ConfirmApprove extends Component {
} = this.props
const { customPermissionAmount } = this.state
const tokensText = `${tokenAmount} ${tokenSymbol}`
const tokensText = `${Number(tokenAmount)} ${tokenSymbol}`
const tokenBalance = tokenTrackerBalance
? Number(calcTokenAmount(tokenTrackerBalance, decimals)).toPrecision(9)
? calcTokenAmount(tokenTrackerBalance, decimals).toString(10)
: ''
const customData = customPermissionAmount
? getCustomTxParamsData(data, { customPermissionAmount, decimals })
: null
return (
<ConfirmTransactionBase
toAddress={toAddress}
@ -82,29 +86,27 @@ export default class ConfirmApprove extends Component {
showAccountInHeader
title={tokensText}
contentComponent={<ConfirmApproveContent
decimals={decimals}
siteImage={siteImage}
tokenAddress={tokenAddress}
setCustomAmount={(newAmount) => {
this.setState({ customPermissionAmount: newAmount })
}}
customTokenAmount={String(customPermissionAmount)}
tokenAmount={String(tokenAmount)}
tokenAmount={tokenAmount}
origin={origin}
tokenSymbol={tokenSymbol}
tokenBalance={tokenBalance}
showCustomizeGasModal={() => showCustomizeGasModal(txData)}
showEditApprovalPermissionModal={showEditApprovalPermissionModal}
data={data}
data={customData || data}
toAddress={toAddress}
currentCurrency={currentCurrency}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
/>}
hideSenderToRecipient
customTxParamsData={customPermissionAmount
? getCustomTxParamsData(data, { customPermissionAmount, tokenAmount, decimals })
: null
}
customTxParamsData={customData}
{...restProps}
/>
)

@ -43,7 +43,7 @@ const mapStateToProps = (state, ownProps) => {
const tokenData = getTokenData(data)
const tokenValue = tokenData && getTokenValue(tokenData.params)
const toAddress = tokenData && getTokenToAddress(tokenData.params)
const tokenAmount = tokenData && calcTokenAmount(tokenValue, decimals).toNumber()
const tokenAmount = tokenData && calcTokenAmount(tokenValue, decimals).toString(10)
const contractExchangeRate = contractExchangeRateSelector(state)
const { origin } = transaction
@ -76,20 +76,22 @@ const mapDispatchToProps = (dispatch) => {
return {
showCustomizeGasModal: (txData) => dispatch(showModal({ name: 'CUSTOMIZE_GAS', txData })),
showEditApprovalPermissionModal: ({
tokenAmount,
customTokenAmount,
tokenSymbol,
tokenBalance,
setCustomAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
}) => dispatch(showModal({
name: 'EDIT_APPROVAL_PERMISSION',
tokenAmount,
customTokenAmount,
tokenSymbol,
tokenBalance,
setCustomAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
})),
}
}

@ -1,28 +1,33 @@
import { decimalToHex } from '../../helpers/utils/conversions.util'
import { calcTokenValue } from '../../helpers/utils/token-util.js'
import { getTokenData } from '../../helpers/utils/transactions.util'
export function getCustomTxParamsData (data, { customPermissionAmount, tokenAmount, decimals }) {
if (customPermissionAmount) {
const tokenValue = decimalToHex(calcTokenValue(tokenAmount, decimals))
export function getCustomTxParamsData (data, { customPermissionAmount, decimals }) {
const tokenData = getTokenData(data)
const re = new RegExp('(^.+)' + tokenValue + '$')
const matches = re.exec(data)
if (!matches || !matches[1]) {
return data
}
let dataWithoutCurrentAmount = matches[1]
const customPermissionValue = decimalToHex(calcTokenValue(Number(customPermissionAmount), decimals))
if (!tokenData) {
throw new Error('Invalid data')
} else if (tokenData.name !== 'approve') {
throw new Error(`Invalid data; should be 'approve' method, but instead is '${tokenData.name}'`)
}
let spender = tokenData.params[0].value
if (spender.startsWith('0x')) {
spender = spender.substring(2)
}
const [signature, tokenValue] = data.split(spender)
const differenceInLengths = customPermissionValue.length - tokenValue.length
const zeroModifier = dataWithoutCurrentAmount.length - differenceInLengths
if (differenceInLengths > 0) {
dataWithoutCurrentAmount = dataWithoutCurrentAmount.slice(0, zeroModifier)
} else if (differenceInLengths < 0) {
dataWithoutCurrentAmount = dataWithoutCurrentAmount.padEnd(zeroModifier, 0)
}
if (!signature || !tokenValue) {
throw new Error('Invalid data')
} else if (tokenValue.length !== 64) {
throw new Error('Invalid token value; should be exactly 64 hex digits long (u256)')
}
const customTxParamsData = dataWithoutCurrentAmount + customPermissionValue
return customTxParamsData
let customPermissionValue = decimalToHex(calcTokenValue(customPermissionAmount, decimals))
if (customPermissionValue.length > 64) {
throw new Error('Custom value is larger than u256')
}
customPermissionValue = customPermissionValue.padStart(tokenValue.length, '0')
const customTxParamsData = `${signature}${spender}${customPermissionValue}`
return customTxParamsData
}

@ -7576,7 +7576,7 @@ cross-spawn@^4:
lru-cache "^4.0.1"
which "^1.2.9"
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
@ -10370,9 +10370,9 @@ ethereum-common@^0.0.18:
integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=
ethereum-ens-network-map@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ethereum-ens-network-map/-/ethereum-ens-network-map-1.0.0.tgz#43cd7669ce950a789e151001118d4d65f210eeb7"
integrity sha1-Q812ac6VCnieFRABEY1NZfIQ7rc=
version "1.0.2"
resolved "https://registry.yarnpkg.com/ethereum-ens-network-map/-/ethereum-ens-network-map-1.0.2.tgz#4e27bad18dae7bd95d84edbcac2c9e739fc959b9"
integrity sha512-5qwJ5n3YhjSpE6O/WEBXCAb2nagUgyagJ6C0lGUBWC4LjKp/rRzD+pwtDJ6KCiITFEAoX4eIrWOjRy0Sylq5Hg==
ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.4, ethereumjs-abi@^0.6.5:
version "0.6.5"

Loading…
Cancel
Save