mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 18:00:18 +01:00
Merge pull request #7922 from MetaMask/Version-v7.7.4
Version v7.7.4 RC
This commit is contained in:
commit
de3b98c9ee
@ -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
|
||||
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 dataWithoutCurrentAmount = matches[1]
|
||||
const customPermissionValue = decimalToHex(calcTokenValue(Number(customPermissionAmount), decimals))
|
||||
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
|
||||
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…
Reference in New Issue
Block a user