mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-23 02:10:12 +01:00
Merge branch 'develop' into v4.8.0
This commit is contained in:
commit
ea0dcdd312
@ -11,6 +11,12 @@
|
||||
- [#4557](https://github.com/MetaMask/metamask-extension/pull/4557): Fix bug where nonce mutex was never released.
|
||||
- [#4558](https://github.com/MetaMask/metamask-extension/pull/4558): Stop reloading browser page on Ethereum network change.
|
||||
- [#4566](https://github.com/MetaMask/metamask-extension/pull/4566): Add phishing notice.
|
||||
- Attempting to import an empty private key will now show a clear error.
|
||||
- Fix bug where metamask data would stop being written to disk after prolonged use
|
||||
- Fix bug where account reset did not work with custom RPC providers.
|
||||
- Fix for Brave i18n getAcceptLanguages [#4270](https://github.com/MetaMask/metamask-extension/issues/4270)
|
||||
- Fix bug where nonce mutex was never released
|
||||
- Add phishing notice
|
||||
|
||||
## 4.7.4 Tue Jun 05 2018
|
||||
|
||||
|
@ -146,6 +146,9 @@
|
||||
"copy": {
|
||||
"message": "Copy"
|
||||
},
|
||||
"copyContractAddress": {
|
||||
"message": "Copy Contract Address"
|
||||
},
|
||||
"copyToClipboard": {
|
||||
"message": "Copy to clipboard"
|
||||
},
|
||||
@ -955,6 +958,9 @@
|
||||
"viewAccount": {
|
||||
"message": "View Account"
|
||||
},
|
||||
"viewOnEtherscan": {
|
||||
"message": "View on Etherscan"
|
||||
},
|
||||
"visitWebSite": {
|
||||
"message": "Visit our web site"
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
const Config = require('./recipient-blacklist-config.json')
|
||||
const Config = require('./recipient-blacklist.js')
|
||||
|
||||
/** @module*/
|
||||
module.exports = {
|
||||
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"blacklist": [
|
||||
"0x627306090abab3a6e1400e9345bc60c78a8bef57",
|
||||
"0xf17f52151ebef6c7334fad080c5704d77216b732",
|
||||
"0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef",
|
||||
"0x821aea9a577a9b44299b9c15c88cf3087f3b5544",
|
||||
"0x0d1d4e623d10f9fba5db95830f7d3839406c6af2",
|
||||
"0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e",
|
||||
"0x2191ef87e392377ec08e7c08eb105ef5448eced5",
|
||||
"0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5",
|
||||
"0x6330a553fc93768f612722bb8c2ec78ac90b3bbc",
|
||||
"0x5aeda56215b167893e80b4fe645ba6d5bab767de"
|
||||
]
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
'blacklist': [
|
||||
// IDEX phisher
|
||||
'0x9bcb0A9d99d815Bb87ee3191b1399b1Bcc46dc77',
|
||||
// Ganache default seed phrases
|
||||
'0x627306090abab3a6e1400e9345bc60c78a8bef57',
|
||||
'0xf17f52151ebef6c7334fad080c5704d77216b732',
|
||||
'0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
|
||||
'0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
|
||||
'0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
|
||||
'0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e',
|
||||
'0x2191ef87e392377ec08e7c08eb105ef5448eced5',
|
||||
'0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5',
|
||||
'0x6330a553fc93768f612722bb8c2ec78ac90b3bbc',
|
||||
'0x5aeda56215b167893e80b4fe645ba6d5bab767de',
|
||||
],
|
||||
}
|
@ -3,6 +3,7 @@ cleanContextForImports()
|
||||
require('web3/dist/web3.min.js')
|
||||
const log = require('loglevel')
|
||||
const LocalMessageDuplexStream = require('post-message-stream')
|
||||
const setupDappAutoReload = require('./lib/auto-reload.js')
|
||||
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
|
||||
restoreContextAfterImports()
|
||||
|
||||
@ -38,7 +39,11 @@ web3.setProvider = function () {
|
||||
}
|
||||
log.debug('MetaMask - injected web3')
|
||||
|
||||
setupDappAutoReload(web3, inpageProvider.publicConfigStore)
|
||||
|
||||
// export global web3, with usage-detection and deprecation warning
|
||||
|
||||
/* TODO: Uncomment this area once auto-reload.js has been deprecated:
|
||||
let hasBeenWarned = false
|
||||
global.web3 = new Proxy(web3, {
|
||||
get: (_web3, key) => {
|
||||
@ -55,6 +60,7 @@ global.web3 = new Proxy(web3, {
|
||||
_web3[key] = value
|
||||
},
|
||||
})
|
||||
*/
|
||||
|
||||
// set web3 defaultAccount
|
||||
inpageProvider.publicConfigStore.subscribe(function (state) {
|
||||
|
61
app/scripts/lib/auto-reload.js
Normal file
61
app/scripts/lib/auto-reload.js
Normal file
@ -0,0 +1,61 @@
|
||||
module.exports = setupDappAutoReload
|
||||
|
||||
function setupDappAutoReload (web3, observable) {
|
||||
// export web3 as a global, checking for usage
|
||||
let hasBeenWarned = false
|
||||
let reloadInProgress = false
|
||||
let lastTimeUsed
|
||||
let lastSeenNetwork
|
||||
|
||||
global.web3 = new Proxy(web3, {
|
||||
get: (_web3, key) => {
|
||||
// show warning once on web3 access
|
||||
if (!hasBeenWarned && key !== 'currentProvider') {
|
||||
console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation')
|
||||
hasBeenWarned = true
|
||||
}
|
||||
// get the time of use
|
||||
lastTimeUsed = Date.now()
|
||||
// return value normally
|
||||
return _web3[key]
|
||||
},
|
||||
set: (_web3, key, value) => {
|
||||
// set value normally
|
||||
_web3[key] = value
|
||||
},
|
||||
})
|
||||
|
||||
observable.subscribe(function (state) {
|
||||
// if reload in progress, no need to check reload logic
|
||||
if (reloadInProgress) return
|
||||
|
||||
const currentNetwork = state.networkVersion
|
||||
|
||||
// set the initial network
|
||||
if (!lastSeenNetwork) {
|
||||
lastSeenNetwork = currentNetwork
|
||||
return
|
||||
}
|
||||
|
||||
// skip reload logic if web3 not used
|
||||
if (!lastTimeUsed) return
|
||||
|
||||
// if network did not change, exit
|
||||
if (currentNetwork === lastSeenNetwork) return
|
||||
|
||||
// initiate page reload
|
||||
reloadInProgress = true
|
||||
const timeSinceUse = Date.now() - lastTimeUsed
|
||||
// if web3 was recently used then delay the reloading of the page
|
||||
if (timeSinceUse > 500) {
|
||||
triggerReset()
|
||||
} else {
|
||||
setTimeout(triggerReset, 500)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// reload the page
|
||||
function triggerReset () {
|
||||
global.location.reload()
|
||||
}
|
@ -13,12 +13,15 @@ module.exports = class NoticeController extends EventEmitter {
|
||||
this.firstVersion = opts.firstVersion
|
||||
this.version = opts.version
|
||||
const initState = extend({
|
||||
noticesList: this._filterNotices(hardCodedNotices),
|
||||
noticesList: [],
|
||||
}, opts.initState)
|
||||
this.store = new ObservableStore(initState)
|
||||
// setup memStore
|
||||
this.memStore = new ObservableStore({})
|
||||
this.store.subscribe(() => this._updateMemstore())
|
||||
this._updateMemstore()
|
||||
// pull in latest notices
|
||||
this.updateNoticesList()
|
||||
}
|
||||
|
||||
getNoticesList () {
|
||||
@ -84,8 +87,8 @@ module.exports = class NoticeController extends EventEmitter {
|
||||
}
|
||||
|
||||
async _retrieveNoticeData () {
|
||||
// Placeholder for the API.
|
||||
return []
|
||||
// Placeholder for remote notice API.
|
||||
return hardCodedNotices
|
||||
}
|
||||
|
||||
_updateMemstore () {
|
||||
|
@ -1,6 +1,6 @@
|
||||
Dear MetaMask Users,
|
||||
|
||||
There have been several instances of high-profile legitimate websites such as BTC Manager and Games Workshop that have had their websites temporarily compromised. This involves showing a fake MetaMask window on the page asking for user's seed phrases. MetaMask will never open itself in this way and users are encouraged to report these instances immediately to either [our phishing blacklist](https://github.com/MetaMask/eth-phishing-detect/issues) or our support email at [support@metamask.io](support@metamask.io).
|
||||
There have been several instances of high-profile legitimate websites such as BTC Manager and Games Workshop that have had their websites temporarily compromised. This involves showing a fake MetaMask window on the page asking for user's seed phrases. MetaMask will never open itself in this way and users are encouraged to report these instances immediately to either [our phishing blacklist](https://github.com/MetaMask/eth-phishing-detect/issues) or our support email at [support@metamask.io](mailto:support@metamask.io).
|
||||
|
||||
Please read our full article on this ongoing issue at [https://medium.com/metamask/new-phishing-strategy-becoming-common-1b1123837168](https://medium.com/metamask/new-phishing-strategy-becoming-common-1b1123837168).
|
||||
|
||||
|
@ -117,12 +117,12 @@ async function runSendFlowTest(assert, done) {
|
||||
const sendGasField = await queryAsync($, '.send-v2__gas-fee-display')
|
||||
assert.equal(
|
||||
sendGasField.find('.currency-display__input-wrapper > input').val(),
|
||||
'0.000198264',
|
||||
'0.000021',
|
||||
'send gas field should show estimated gas total'
|
||||
)
|
||||
assert.equal(
|
||||
sendGasField.find('.currency-display__converted-value')[0].textContent,
|
||||
'$0.24 USD',
|
||||
'$0.03 USD',
|
||||
'send gas field should show estimated gas total converted to USD'
|
||||
)
|
||||
|
||||
|
@ -4,14 +4,21 @@ const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('../../actions')
|
||||
|
||||
const genAccountLink = require('etherscan-link').createAccountLink
|
||||
const copyToClipboard = require('copy-to-clipboard')
|
||||
const { Menu, Item, CloseArea } = require('./components/menu')
|
||||
|
||||
TokenMenuDropdown.contextTypes = {
|
||||
t: PropTypes.func,
|
||||
}
|
||||
|
||||
module.exports = connect(null, mapDispatchToProps)(TokenMenuDropdown)
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(TokenMenuDropdown)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
network: state.metamask.network,
|
||||
}
|
||||
}
|
||||
|
||||
function mapDispatchToProps (dispatch) {
|
||||
return {
|
||||
@ -37,22 +44,34 @@ TokenMenuDropdown.prototype.onClose = function (e) {
|
||||
TokenMenuDropdown.prototype.render = function () {
|
||||
const { showHideTokenConfirmationModal } = this.props
|
||||
|
||||
return h('div.token-menu-dropdown', {}, [
|
||||
h('div.token-menu-dropdown__close-area', {
|
||||
return h(Menu, { className: 'token-menu-dropdown', isShowing: true }, [
|
||||
h(CloseArea, {
|
||||
onClick: this.onClose,
|
||||
}),
|
||||
h('div.token-menu-dropdown__container', {}, [
|
||||
h('div.token-menu-dropdown__options', {}, [
|
||||
|
||||
h('div.token-menu-dropdown__option', {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
showHideTokenConfirmationModal(this.props.token)
|
||||
this.props.onClose()
|
||||
},
|
||||
}, this.context.t('hideToken')),
|
||||
|
||||
]),
|
||||
]),
|
||||
h(Item, {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
showHideTokenConfirmationModal(this.props.token)
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('hideToken'),
|
||||
}),
|
||||
h(Item, {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
copyToClipboard(this.props.token.address)
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('copyContractAddress'),
|
||||
}),
|
||||
h(Item, {
|
||||
onClick: (e) => {
|
||||
e.stopPropagation()
|
||||
const url = genAccountLink(this.props.token.address, this.props.network)
|
||||
global.platform.openWindow({ url })
|
||||
this.props.onClose()
|
||||
},
|
||||
text: this.context.t('viewOnEtherscan'),
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ CurrencyDisplay.prototype.getValueToRender = function ({ selectedToken, conversi
|
||||
return selectedToken
|
||||
? conversionUtil(ethUtil.addHexPrefix(value), {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
toCurrency: symbol,
|
||||
conversionRate: multiplier,
|
||||
invertConversionRate: true,
|
||||
@ -91,8 +92,12 @@ CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValu
|
||||
: convertedValue
|
||||
}
|
||||
|
||||
function removeLeadingZeroes (str) {
|
||||
return str.replace(/^0*(?=\d)/, '')
|
||||
}
|
||||
|
||||
CurrencyDisplay.prototype.handleChange = function (newVal) {
|
||||
this.setState({ valueToRender: newVal })
|
||||
this.setState({ valueToRender: removeLeadingZeroes(newVal) })
|
||||
this.props.onChange(this.getAmount(newVal))
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ export default class SendAmountRow extends Component {
|
||||
tokenBalance: PropTypes.string,
|
||||
updateSendAmount: PropTypes.func,
|
||||
updateSendAmountError: PropTypes.func,
|
||||
updateGas: PropTypes.func,
|
||||
}
|
||||
|
||||
validateAmount (amount) {
|
||||
@ -56,6 +57,14 @@ export default class SendAmountRow extends Component {
|
||||
updateSendAmount(amount)
|
||||
}
|
||||
|
||||
updateGas (amount) {
|
||||
const { selectedToken, updateGas } = this.props
|
||||
|
||||
if (selectedToken) {
|
||||
updateGas({ amount })
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
amount,
|
||||
@ -77,12 +86,15 @@ export default class SendAmountRow extends Component {
|
||||
<CurrencyDisplay
|
||||
conversionRate={amountConversionRate}
|
||||
convertedCurrency={convertedCurrency}
|
||||
onBlur={newAmount => this.updateAmount(newAmount)}
|
||||
onBlur={newAmount => {
|
||||
this.updateGas(newAmount)
|
||||
this.updateAmount(newAmount)
|
||||
}}
|
||||
onChange={newAmount => this.validateAmount(newAmount)}
|
||||
inError={inError}
|
||||
primaryCurrency={primaryCurrency || 'ETH'}
|
||||
selectedToken={selectedToken}
|
||||
value={amount || '0x0'}
|
||||
value={amount}
|
||||
/>
|
||||
</SendRowWrapper>
|
||||
)
|
||||
|
@ -12,10 +12,12 @@ const propsMethodSpies = {
|
||||
setMaxModeTo: sinon.spy(),
|
||||
updateSendAmount: sinon.spy(),
|
||||
updateSendAmountError: sinon.spy(),
|
||||
updateGas: sinon.spy(),
|
||||
}
|
||||
|
||||
sinon.spy(SendAmountRow.prototype, 'updateAmount')
|
||||
sinon.spy(SendAmountRow.prototype, 'validateAmount')
|
||||
sinon.spy(SendAmountRow.prototype, 'updateGas')
|
||||
|
||||
describe('SendAmountRow Component', function () {
|
||||
let wrapper
|
||||
@ -36,6 +38,7 @@ describe('SendAmountRow Component', function () {
|
||||
tokenBalance={'mockTokenBalance'}
|
||||
updateSendAmount={propsMethodSpies.updateSendAmount}
|
||||
updateSendAmountError={propsMethodSpies.updateSendAmountError}
|
||||
updateGas={propsMethodSpies.updateGas}
|
||||
/>, { context: { t: str => str + '_t' } })
|
||||
instance = wrapper.instance()
|
||||
})
|
||||
@ -139,8 +142,14 @@ describe('SendAmountRow Component', function () {
|
||||
assert.equal(primaryCurrency, 'mockPrimaryCurrency')
|
||||
assert.deepEqual(selectedToken, { address: 'mockTokenAddress' })
|
||||
assert.equal(value, 'mockAmount')
|
||||
assert.equal(SendAmountRow.prototype.updateGas.callCount, 0)
|
||||
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 0)
|
||||
onBlur('mockNewAmount')
|
||||
assert.equal(SendAmountRow.prototype.updateGas.callCount, 1)
|
||||
assert.deepEqual(
|
||||
SendAmountRow.prototype.updateGas.getCall(0).args,
|
||||
['mockNewAmount']
|
||||
)
|
||||
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 1)
|
||||
assert.deepEqual(
|
||||
SendAmountRow.prototype.updateAmount.getCall(0).args,
|
||||
|
@ -18,7 +18,7 @@ export default class SendContent extends Component {
|
||||
<div className="send-v2__form">
|
||||
<SendFromRow />
|
||||
<SendToRow updateGas={(updateData) => this.props.updateGas(updateData)} />
|
||||
<SendAmountRow />
|
||||
<SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} />
|
||||
<SendGasRow />
|
||||
</div>
|
||||
</PageContainerContent>
|
||||
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
||||
import PersistentForm from '../../../lib/persistent-form'
|
||||
import {
|
||||
getAmountErrorObject,
|
||||
getToAddressForGasUpdate,
|
||||
doesAmountErrorRequireUpdate,
|
||||
} from './send.utils'
|
||||
|
||||
@ -38,7 +39,7 @@ export default class SendTransactionScreen extends PersistentForm {
|
||||
updateSendTokenBalance: PropTypes.func,
|
||||
};
|
||||
|
||||
updateGas ({ to } = {}) {
|
||||
updateGas ({ to: updatedToAddress, amount: value } = {}) {
|
||||
const {
|
||||
amount,
|
||||
blockGasLimit,
|
||||
@ -48,6 +49,7 @@ export default class SendTransactionScreen extends PersistentForm {
|
||||
recentBlocks,
|
||||
selectedAddress,
|
||||
selectedToken = {},
|
||||
to: currentToAddress,
|
||||
updateAndSetGasTotal,
|
||||
} = this.props
|
||||
|
||||
@ -59,8 +61,8 @@ export default class SendTransactionScreen extends PersistentForm {
|
||||
recentBlocks,
|
||||
selectedAddress,
|
||||
selectedToken,
|
||||
to: to && to.toLowerCase(),
|
||||
value: amount,
|
||||
to: getToAddressForGasUpdate(updatedToAddress, currentToAddress),
|
||||
value: value || amount,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
getSendAmount,
|
||||
getSendEditingTransactionId,
|
||||
getSendFromObject,
|
||||
getSendTo,
|
||||
getTokenBalance,
|
||||
} from './send.selectors'
|
||||
import {
|
||||
@ -54,6 +55,7 @@ function mapStateToProps (state) {
|
||||
recentBlocks: getRecentBlocks(state),
|
||||
selectedAddress: getSelectedAddress(state),
|
||||
selectedToken: getSelectedToken(state),
|
||||
to: getSendTo(state),
|
||||
tokenBalance: getTokenBalance(state),
|
||||
tokenContract: getSelectedTokenContract(state),
|
||||
tokenToFiatRate: getSelectedTokenToFiatRate(state),
|
||||
|
@ -4,6 +4,7 @@ const {
|
||||
conversionGTE,
|
||||
multiplyCurrencies,
|
||||
conversionGreaterThan,
|
||||
conversionLessThan,
|
||||
} = require('../../conversion-util')
|
||||
const {
|
||||
calcTokenAmount,
|
||||
@ -20,6 +21,7 @@ const abi = require('ethereumjs-abi')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
|
||||
module.exports = {
|
||||
addGasBuffer,
|
||||
calcGasTotal,
|
||||
calcTokenBalance,
|
||||
doesAmountErrorRequireUpdate,
|
||||
@ -27,6 +29,7 @@ module.exports = {
|
||||
estimateGasPriceFromRecentBlocks,
|
||||
generateTokenTransferData,
|
||||
getAmountErrorObject,
|
||||
getToAddressForGasUpdate,
|
||||
isBalanceSufficient,
|
||||
isTokenBalanceSufficient,
|
||||
}
|
||||
@ -175,9 +178,8 @@ async function estimateGas ({ selectedAddress, selectedToken, blockGasLimit, to,
|
||||
}
|
||||
|
||||
// if recipient has no code, gas is 21k max:
|
||||
const hasRecipient = Boolean(to)
|
||||
if (hasRecipient && !selectedToken) {
|
||||
const code = await global.eth.getCode(to)
|
||||
if (!selectedToken) {
|
||||
const code = Boolean(to) && await global.eth.getCode(to)
|
||||
if (!code || code === '0x') {
|
||||
return SIMPLE_GAS_COST
|
||||
}
|
||||
@ -201,16 +203,46 @@ async function estimateGas ({ selectedAddress, selectedToken, blockGasLimit, to,
|
||||
err.message.includes('gas required exceeds allowance or always failing transaction')
|
||||
)
|
||||
if (simulationFailed) {
|
||||
return resolve(paramsForGasEstimate.gas)
|
||||
const estimateWithBuffer = addGasBuffer(paramsForGasEstimate.gas, blockGasLimit, 1.5)
|
||||
return resolve(ethUtil.addHexPrefix(estimateWithBuffer))
|
||||
} else {
|
||||
return reject(err)
|
||||
}
|
||||
}
|
||||
return resolve(estimatedGas.toString(16))
|
||||
const estimateWithBuffer = addGasBuffer(estimatedGas.toString(16), blockGasLimit, 1.5)
|
||||
return resolve(ethUtil.addHexPrefix(estimateWithBuffer))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function addGasBuffer (initialGasLimitHex, blockGasLimitHex, bufferMultiplier = 1.5) {
|
||||
const upperGasLimit = multiplyCurrencies(blockGasLimitHex, 0.9, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 10,
|
||||
numberOfDecimals: '0',
|
||||
})
|
||||
const bufferedGasLimit = multiplyCurrencies(initialGasLimitHex, bufferMultiplier, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 10,
|
||||
numberOfDecimals: '0',
|
||||
})
|
||||
|
||||
// if initialGasLimit is above blockGasLimit, dont modify it
|
||||
if (conversionGreaterThan(
|
||||
{ value: initialGasLimitHex, fromNumericBase: 'hex' },
|
||||
{ value: upperGasLimit, fromNumericBase: 'hex' },
|
||||
)) return initialGasLimitHex
|
||||
// if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
|
||||
if (conversionLessThan(
|
||||
{ value: bufferedGasLimit, fromNumericBase: 'hex' },
|
||||
{ value: upperGasLimit, fromNumericBase: 'hex' },
|
||||
)) return bufferedGasLimit
|
||||
// otherwise use blockGasLimit
|
||||
return upperGasLimit
|
||||
}
|
||||
|
||||
function generateTokenTransferData ({ toAddress = '0x0', amount = '0x0', selectedToken }) {
|
||||
if (!selectedToken) return
|
||||
return TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
|
||||
@ -237,3 +269,7 @@ function estimateGasPriceFromRecentBlocks (recentBlocks) {
|
||||
|
||||
return lowestPrices[Math.floor(lowestPrices.length / 2)]
|
||||
}
|
||||
|
||||
function getToAddressForGasUpdate (...addresses) {
|
||||
return [...addresses, ''].find(str => str !== undefined && str !== null).toLowerCase()
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ describe('Send Component', function () {
|
||||
})
|
||||
|
||||
describe('updateGas', () => {
|
||||
it('should call updateAndSetGasTotal with the correct params', () => {
|
||||
it('should call updateAndSetGasTotal with the correct params if no to prop is passed', () => {
|
||||
propsMethodSpies.updateAndSetGasTotal.resetHistory()
|
||||
wrapper.instance().updateGas()
|
||||
assert.equal(propsMethodSpies.updateAndSetGasTotal.callCount, 1)
|
||||
@ -215,12 +215,22 @@ describe('Send Component', function () {
|
||||
recentBlocks: ['mockBlock'],
|
||||
selectedAddress: 'mockSelectedAddress',
|
||||
selectedToken: 'mockSelectedToken',
|
||||
to: undefined,
|
||||
to: '',
|
||||
value: 'mockAmount',
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('should call updateAndSetGasTotal with the correct params if a to prop is passed', () => {
|
||||
propsMethodSpies.updateAndSetGasTotal.resetHistory()
|
||||
wrapper.setProps({ to: 'someAddress' })
|
||||
wrapper.instance().updateGas()
|
||||
assert.equal(
|
||||
propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0].to,
|
||||
'someaddress',
|
||||
)
|
||||
})
|
||||
|
||||
it('should call updateAndSetGasTotal with to set to lowercase if passed', () => {
|
||||
propsMethodSpies.updateAndSetGasTotal.resetHistory()
|
||||
wrapper.instance().updateGas({ to: '0xABC' })
|
||||
|
@ -39,6 +39,7 @@ proxyquire('../send.container.js', {
|
||||
getSelectedTokenContract: (s) => `mockTokenContract:${s}`,
|
||||
getSelectedTokenToFiatRate: (s) => `mockTokenToFiatRate:${s}`,
|
||||
getSendAmount: (s) => `mockAmount:${s}`,
|
||||
getSendTo: (s) => `mockTo:${s}`,
|
||||
getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`,
|
||||
getSendFromObject: (s) => `mockFrom:${s}`,
|
||||
getTokenBalance: (s) => `mockTokenBalance:${s}`,
|
||||
@ -70,6 +71,7 @@ describe('send container', () => {
|
||||
recentBlocks: 'mockRecentBlocks:mockState',
|
||||
selectedAddress: 'mockSelectedAddress:mockState',
|
||||
selectedToken: 'mockSelectedToken:mockState',
|
||||
to: 'mockTo:mockState',
|
||||
tokenBalance: 'mockTokenBalance:mockState',
|
||||
tokenContract: 'mockTokenContract:mockState',
|
||||
tokenToFiatRate: 'mockTokenToFiatRate:mockState',
|
||||
|
@ -18,10 +18,12 @@ const {
|
||||
const stubs = {
|
||||
addCurrencies: sinon.stub().callsFake((a, b, obj) => a + b),
|
||||
conversionUtil: sinon.stub().callsFake((val, obj) => parseInt(val, 16)),
|
||||
conversionGTE: sinon.stub().callsFake((obj1, obj2) => obj1.value > obj2.value),
|
||||
conversionGTE: sinon.stub().callsFake((obj1, obj2) => obj1.value >= obj2.value),
|
||||
multiplyCurrencies: sinon.stub().callsFake((a, b) => `${a}x${b}`),
|
||||
calcTokenAmount: sinon.stub().callsFake((a, d) => 'calc:' + a + d),
|
||||
rawEncode: sinon.stub().returns([16, 1100]),
|
||||
conversionGreaterThan: sinon.stub().callsFake((obj1, obj2) => obj1.value > obj2.value),
|
||||
conversionLessThan: sinon.stub().callsFake((obj1, obj2) => obj1.value < obj2.value),
|
||||
}
|
||||
|
||||
const sendUtils = proxyquire('../send.utils.js', {
|
||||
@ -30,6 +32,8 @@ const sendUtils = proxyquire('../send.utils.js', {
|
||||
conversionUtil: stubs.conversionUtil,
|
||||
conversionGTE: stubs.conversionGTE,
|
||||
multiplyCurrencies: stubs.multiplyCurrencies,
|
||||
conversionGreaterThan: stubs.conversionGreaterThan,
|
||||
conversionLessThan: stubs.conversionLessThan,
|
||||
},
|
||||
'../../token-util': { calcTokenAmount: stubs.calcTokenAmount },
|
||||
'ethereumjs-abi': {
|
||||
@ -44,6 +48,7 @@ const {
|
||||
estimateGasPriceFromRecentBlocks,
|
||||
generateTokenTransferData,
|
||||
getAmountErrorObject,
|
||||
getToAddressForGasUpdate,
|
||||
calcTokenBalance,
|
||||
isBalanceSufficient,
|
||||
isTokenBalanceSufficient,
|
||||
@ -255,7 +260,7 @@ describe('send utils', () => {
|
||||
estimateGasMethod: sinon.stub().callsFake(
|
||||
(data, cb) => cb(
|
||||
data.to.match(/willFailBecauseOf:/) ? { message: data.to.match(/:(.+)$/)[1] } : null,
|
||||
{ toString: (n) => `mockToString:${n}` }
|
||||
{ toString: (n) => `0xabc${n}` }
|
||||
)
|
||||
),
|
||||
}
|
||||
@ -279,13 +284,23 @@ describe('send utils', () => {
|
||||
})
|
||||
|
||||
it('should call ethQuery.estimateGas with the expected params', async () => {
|
||||
const result = await estimateGas(baseMockParams)
|
||||
const result = await sendUtils.estimateGas(baseMockParams)
|
||||
assert.equal(baseMockParams.estimateGasMethod.callCount, 1)
|
||||
assert.deepEqual(
|
||||
baseMockParams.estimateGasMethod.getCall(0).args[0],
|
||||
Object.assign({ gasPrice: undefined, value: undefined }, baseExpectedCall)
|
||||
)
|
||||
assert.equal(result, 'mockToString:16')
|
||||
assert.equal(result, '0xabc16')
|
||||
})
|
||||
|
||||
it('should call ethQuery.estimateGas with the expected params when initialGasLimitHex is lower than the upperGasLimit', async () => {
|
||||
const result = await estimateGas(Object.assign({}, baseMockParams, { blockGasLimit: '0xbcd' }))
|
||||
assert.equal(baseMockParams.estimateGasMethod.callCount, 1)
|
||||
assert.deepEqual(
|
||||
baseMockParams.estimateGasMethod.getCall(0).args[0],
|
||||
Object.assign({ gasPrice: undefined, value: undefined }, baseExpectedCall, { gas: '0xbcdx0.95' })
|
||||
)
|
||||
assert.equal(result, '0xabc16x1.5')
|
||||
})
|
||||
|
||||
it('should call ethQuery.estimateGas with a value of 0x0 and the expected data and to if passed a selectedToken', async () => {
|
||||
@ -300,7 +315,7 @@ describe('send utils', () => {
|
||||
to: 'mockAddress',
|
||||
})
|
||||
)
|
||||
assert.equal(result, 'mockToString:16')
|
||||
assert.equal(result, '0xabc16')
|
||||
})
|
||||
|
||||
it(`should return ${SIMPLE_GAS_COST} if ethQuery.getCode does not return '0x'`, async () => {
|
||||
@ -309,6 +324,12 @@ describe('send utils', () => {
|
||||
assert.equal(result, SIMPLE_GAS_COST)
|
||||
})
|
||||
|
||||
it(`should return ${SIMPLE_GAS_COST} if not passed a selectedToken or truthy to address`, async () => {
|
||||
assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
|
||||
const result = await estimateGas(Object.assign({}, baseMockParams, { to: null }))
|
||||
assert.equal(result, SIMPLE_GAS_COST)
|
||||
})
|
||||
|
||||
it(`should not return ${SIMPLE_GAS_COST} if passed a selectedToken`, async () => {
|
||||
assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
|
||||
const result = await estimateGas(Object.assign({}, baseMockParams, { to: '0x123', selectedToken: { address: '' } }))
|
||||
@ -401,4 +422,15 @@ describe('send utils', () => {
|
||||
assert.equal(estimateGasPriceFromRecentBlocks(mockRecentBlocks), '0x5')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getToAddressForGasUpdate()', () => {
|
||||
it('should return empty string if all params are undefined or null', () => {
|
||||
assert.equal(getToAddressForGasUpdate(undefined, null), '')
|
||||
})
|
||||
|
||||
it('should return the first string that is not defined or null in lower case', () => {
|
||||
assert.equal(getToAddressForGasUpdate('A', null), 'a')
|
||||
assert.equal(getToAddressForGasUpdate(undefined, 'B'), 'b')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -190,6 +190,16 @@ const conversionGreaterThan = (
|
||||
return firstValue.gt(secondValue)
|
||||
}
|
||||
|
||||
const conversionLessThan = (
|
||||
{ ...firstProps },
|
||||
{ ...secondProps },
|
||||
) => {
|
||||
const firstValue = converter({ ...firstProps })
|
||||
const secondValue = converter({ ...secondProps })
|
||||
|
||||
return firstValue.lt(secondValue)
|
||||
}
|
||||
|
||||
const conversionMax = (
|
||||
{ ...firstProps },
|
||||
{ ...secondProps },
|
||||
@ -229,6 +239,7 @@ module.exports = {
|
||||
addCurrencies,
|
||||
multiplyCurrencies,
|
||||
conversionGreaterThan,
|
||||
conversionLessThan,
|
||||
conversionGTE,
|
||||
conversionLTE,
|
||||
conversionMax,
|
||||
|
@ -81,13 +81,9 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
}
|
||||
|
||||
.token-menu-dropdown {
|
||||
height: 55px;
|
||||
width: 80%;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(0, 0, 0, .82);
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .5);
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
top: 52px;
|
||||
right: 25px;
|
||||
z-index: 2000;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user