mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #10072 from MetaMask/Version-v8.1.9
Version v8.1.9 RC
This commit is contained in:
commit
47734b2c63
@ -195,6 +195,7 @@ module.exports = {
|
|||||||
'babel.config.js',
|
'babel.config.js',
|
||||||
'nyc.config.js',
|
'nyc.config.js',
|
||||||
'stylelint.config.js',
|
'stylelint.config.js',
|
||||||
|
'app/scripts/runLockdown.js',
|
||||||
'development/**/*.js',
|
'development/**/*.js',
|
||||||
'test/e2e/**/*.js',
|
'test/e2e/**/*.js',
|
||||||
'test/lib/wait-until-called.js',
|
'test/lib/wait-until-called.js',
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
## Current Develop Branch
|
## Current Develop Branch
|
||||||
|
|
||||||
|
## 8.1.9 Tue Dec 15 2020
|
||||||
|
- [#10034](https://github.com/MetaMask/metamask-extension/pull/10034): Fix contentscript injection failure on Firefox 56
|
||||||
|
- [#10045](https://github.com/MetaMask/metamask-extension/pull/10045): Fix token validation in Send flow
|
||||||
|
- [#10048](https://github.com/MetaMask/metamask-extension/pull/10048): Display boolean values when signing typed data
|
||||||
|
- [#10070](https://github.com/MetaMask/metamask-extension/pull/10070): Add eth_getProof
|
||||||
|
- [#10043](https://github.com/MetaMask/metamask-extension/pull/10043): Improve swaps maximum gas estimation
|
||||||
|
- [#10069](https://github.com/MetaMask/metamask-extension/pull/10069): Fetch swap quote refresh time from API
|
||||||
|
|
||||||
## 8.1.8 Wed Dec 09 2020
|
## 8.1.8 Wed Dec 09 2020
|
||||||
- [#9992](https://github.com/MetaMask/metamask-extension/pull/9992): Improve transaction params validation
|
- [#9992](https://github.com/MetaMask/metamask-extension/pull/9992): Improve transaction params validation
|
||||||
- [#9991](https://github.com/MetaMask/metamask-extension/pull/9991): Don't allow more than 15% slippage
|
- [#9991](https://github.com/MetaMask/metamask-extension/pull/9991): Don't allow more than 15% slippage
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
{
|
{
|
||||||
"matches": ["file://*/*", "http://*/*", "https://*/*"],
|
"matches": ["file://*/*", "http://*/*", "https://*/*"],
|
||||||
"js": [
|
"js": [
|
||||||
|
"disable-console.js",
|
||||||
"globalthis.js",
|
"globalthis.js",
|
||||||
"lockdown.js",
|
"lockdown.js",
|
||||||
"runLockdown.js",
|
"runLockdown.js",
|
||||||
@ -77,6 +78,6 @@
|
|||||||
"notifications"
|
"notifications"
|
||||||
],
|
],
|
||||||
"short_name": "__MSG_appName__",
|
"short_name": "__MSG_appName__",
|
||||||
"version": "8.1.8",
|
"version": "8.1.9",
|
||||||
"web_accessible_resources": ["inpage.js", "phishing.html"]
|
"web_accessible_resources": ["inpage.js", "phishing.html"]
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,8 @@ function injectScript(content) {
|
|||||||
scriptTag.textContent = content
|
scriptTag.textContent = content
|
||||||
container.insertBefore(scriptTag, container.children[0])
|
container.insertBefore(scriptTag, container.children[0])
|
||||||
container.removeChild(scriptTag)
|
container.removeChild(scriptTag)
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
console.error('MetaMask provider injection failed.', e)
|
console.error('MetaMask: Provider injection failed.', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,10 +95,10 @@ async function setupStreams() {
|
|||||||
function forwardTrafficBetweenMuxers(channelName, muxA, muxB) {
|
function forwardTrafficBetweenMuxers(channelName, muxA, muxB) {
|
||||||
const channelA = muxA.createStream(channelName)
|
const channelA = muxA.createStream(channelName)
|
||||||
const channelB = muxB.createStream(channelName)
|
const channelB = muxB.createStream(channelName)
|
||||||
pump(channelA, channelB, channelA, (err) =>
|
pump(channelA, channelB, channelA, (error) =>
|
||||||
logStreamDisconnectWarning(
|
console.debug(
|
||||||
`MetaMask muxed traffic for channel "${channelName}" failed.`,
|
`MetaMask: Muxed traffic for channel "${channelName}" failed.`,
|
||||||
err,
|
error,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -107,14 +107,13 @@ function forwardTrafficBetweenMuxers(channelName, muxA, muxB) {
|
|||||||
* Error handler for page to extension stream disconnections
|
* Error handler for page to extension stream disconnections
|
||||||
*
|
*
|
||||||
* @param {string} remoteLabel - Remote stream name
|
* @param {string} remoteLabel - Remote stream name
|
||||||
* @param {Error} err - Stream connection error
|
* @param {Error} error - Stream connection error
|
||||||
*/
|
*/
|
||||||
function logStreamDisconnectWarning(remoteLabel, err) {
|
function logStreamDisconnectWarning(remoteLabel, error) {
|
||||||
let warningMsg = `MetamaskContentscript - lost connection to ${remoteLabel}`
|
console.debug(
|
||||||
if (err) {
|
`MetaMask: Content script lost connection to "${remoteLabel}".`,
|
||||||
warningMsg += `\n${err.stack}`
|
error,
|
||||||
}
|
)
|
||||||
console.warn(warningMsg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,7 +213,7 @@ function blockedDomainCheck() {
|
|||||||
* Redirects the current page to a phishing information page
|
* Redirects the current page to a phishing information page
|
||||||
*/
|
*/
|
||||||
function redirectToPhishingWarning() {
|
function redirectToPhishingWarning() {
|
||||||
console.log('MetaMask - routing to Phishing Warning component')
|
console.debug('MetaMask: Routing to Phishing Warning component.')
|
||||||
const extensionURL = extension.runtime.getURL('phishing.html')
|
const extensionURL = extension.runtime.getURL('phishing.html')
|
||||||
window.location.href = `${extensionURL}#${querystring.stringify({
|
window.location.href = `${extensionURL}#${querystring.stringify({
|
||||||
hostname: window.location.hostname,
|
hostname: window.location.hostname,
|
||||||
|
@ -32,15 +32,11 @@ export const LOG_METHOD_TYPES = {
|
|||||||
export const LOG_LIMIT = 100
|
export const LOG_LIMIT = 100
|
||||||
|
|
||||||
export const SAFE_METHODS = [
|
export const SAFE_METHODS = [
|
||||||
'web3_sha3',
|
|
||||||
'web3_clientVersion',
|
|
||||||
'net_listening',
|
|
||||||
'net_peerCount',
|
|
||||||
'net_version',
|
|
||||||
'eth_blockNumber',
|
'eth_blockNumber',
|
||||||
'eth_call',
|
'eth_call',
|
||||||
'eth_chainId',
|
'eth_chainId',
|
||||||
'eth_coinbase',
|
'eth_coinbase',
|
||||||
|
'eth_decrypt',
|
||||||
'eth_estimateGas',
|
'eth_estimateGas',
|
||||||
'eth_gasPrice',
|
'eth_gasPrice',
|
||||||
'eth_getBalance',
|
'eth_getBalance',
|
||||||
@ -49,9 +45,11 @@ export const SAFE_METHODS = [
|
|||||||
'eth_getBlockTransactionCountByHash',
|
'eth_getBlockTransactionCountByHash',
|
||||||
'eth_getBlockTransactionCountByNumber',
|
'eth_getBlockTransactionCountByNumber',
|
||||||
'eth_getCode',
|
'eth_getCode',
|
||||||
|
'eth_getEncryptionPublicKey',
|
||||||
'eth_getFilterChanges',
|
'eth_getFilterChanges',
|
||||||
'eth_getFilterLogs',
|
'eth_getFilterLogs',
|
||||||
'eth_getLogs',
|
'eth_getLogs',
|
||||||
|
'eth_getProof',
|
||||||
'eth_getStorageAt',
|
'eth_getStorageAt',
|
||||||
'eth_getTransactionByBlockHashAndIndex',
|
'eth_getTransactionByBlockHashAndIndex',
|
||||||
'eth_getTransactionByBlockNumberAndIndex',
|
'eth_getTransactionByBlockNumberAndIndex',
|
||||||
@ -72,8 +70,6 @@ export const SAFE_METHODS = [
|
|||||||
'eth_sendRawTransaction',
|
'eth_sendRawTransaction',
|
||||||
'eth_sendTransaction',
|
'eth_sendTransaction',
|
||||||
'eth_sign',
|
'eth_sign',
|
||||||
'personal_sign',
|
|
||||||
'personal_ecRecover',
|
|
||||||
'eth_signTypedData',
|
'eth_signTypedData',
|
||||||
'eth_signTypedData_v1',
|
'eth_signTypedData_v1',
|
||||||
'eth_signTypedData_v3',
|
'eth_signTypedData_v3',
|
||||||
@ -83,7 +79,12 @@ export const SAFE_METHODS = [
|
|||||||
'eth_syncing',
|
'eth_syncing',
|
||||||
'eth_uninstallFilter',
|
'eth_uninstallFilter',
|
||||||
'metamask_watchAsset',
|
'metamask_watchAsset',
|
||||||
|
'net_listening',
|
||||||
|
'net_peerCount',
|
||||||
|
'net_version',
|
||||||
|
'personal_ecRecover',
|
||||||
|
'personal_sign',
|
||||||
'wallet_watchAsset',
|
'wallet_watchAsset',
|
||||||
'eth_getEncryptionPublicKey',
|
'web3_clientVersion',
|
||||||
'eth_decrypt',
|
'web3_sha3',
|
||||||
]
|
]
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
fetchTradesInfo as defaultFetchTradesInfo,
|
fetchTradesInfo as defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
|
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
|
||||||
|
fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime,
|
||||||
} from '../../../ui/app/pages/swaps/swaps.util'
|
} from '../../../ui/app/pages/swaps/swaps.util'
|
||||||
|
|
||||||
const METASWAP_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c'
|
const METASWAP_ADDRESS = '0x881d40237659c251811cec9c364ef91dc08d300c'
|
||||||
@ -28,6 +29,14 @@ const MAX_GAS_LIMIT = 2500000
|
|||||||
// 3 seems to be an appropriate balance of giving users the time they need when MetaMask is not left idle, and turning polling off when it is.
|
// 3 seems to be an appropriate balance of giving users the time they need when MetaMask is not left idle, and turning polling off when it is.
|
||||||
const POLL_COUNT_LIMIT = 3
|
const POLL_COUNT_LIMIT = 3
|
||||||
|
|
||||||
|
// If for any reason the MetaSwap API fails to provide a refresh time,
|
||||||
|
// provide a reasonable fallback to avoid further errors
|
||||||
|
const FALLBACK_QUOTE_REFRESH_TIME = 60000
|
||||||
|
|
||||||
|
// This is the amount of time to wait, after successfully fetching quotes
|
||||||
|
// and their gas estimates, before fetching for new quotes
|
||||||
|
const QUOTE_POLLING_DIFFERENCE_INTERVAL = 10 * 1000
|
||||||
|
|
||||||
function calculateGasEstimateWithRefund(
|
function calculateGasEstimateWithRefund(
|
||||||
maxGas = MAX_GAS_LIMIT,
|
maxGas = MAX_GAS_LIMIT,
|
||||||
estimatedRefund = 0,
|
estimatedRefund = 0,
|
||||||
@ -42,9 +51,6 @@ function calculateGasEstimateWithRefund(
|
|||||||
return gasEstimateWithRefund
|
return gasEstimateWithRefund
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the amount of time to wait, after successfully fetching quotes and their gas estimates, before fetching for new quotes
|
|
||||||
const QUOTE_POLLING_INTERVAL = 50 * 1000
|
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
swapsState: {
|
swapsState: {
|
||||||
quotes: {},
|
quotes: {},
|
||||||
@ -61,6 +67,7 @@ const initialState = {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: false,
|
swapsFeatureIsLive: false,
|
||||||
|
swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +80,7 @@ export default class SwapsController {
|
|||||||
tokenRatesStore,
|
tokenRatesStore,
|
||||||
fetchTradesInfo = defaultFetchTradesInfo,
|
fetchTradesInfo = defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness = defaultFetchSwapsFeatureLiveness,
|
fetchSwapsFeatureLiveness = defaultFetchSwapsFeatureLiveness,
|
||||||
|
fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime,
|
||||||
}) {
|
}) {
|
||||||
this.store = new ObservableStore({
|
this.store = new ObservableStore({
|
||||||
swapsState: { ...initialState.swapsState },
|
swapsState: { ...initialState.swapsState },
|
||||||
@ -80,6 +88,7 @@ export default class SwapsController {
|
|||||||
|
|
||||||
this._fetchTradesInfo = fetchTradesInfo
|
this._fetchTradesInfo = fetchTradesInfo
|
||||||
this._fetchSwapsFeatureLiveness = fetchSwapsFeatureLiveness
|
this._fetchSwapsFeatureLiveness = fetchSwapsFeatureLiveness
|
||||||
|
this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime
|
||||||
|
|
||||||
this.getBufferedGasLimit = getBufferedGasLimit
|
this.getBufferedGasLimit = getBufferedGasLimit
|
||||||
this.tokenRatesStore = tokenRatesStore
|
this.tokenRatesStore = tokenRatesStore
|
||||||
@ -101,11 +110,31 @@ export default class SwapsController {
|
|||||||
this._setupSwapsLivenessFetching()
|
this._setupSwapsLivenessFetching()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the refresh rate for quote updates from the MetaSwap API
|
||||||
|
async _setSwapsQuoteRefreshTime() {
|
||||||
|
// Default to fallback time unless API returns valid response
|
||||||
|
let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME
|
||||||
|
try {
|
||||||
|
swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Request for swaps quote refresh time failed: ', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { swapsState } = this.store.getState()
|
||||||
|
this.store.updateState({
|
||||||
|
swapsState: { ...swapsState, swapsQuoteRefreshTime },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Once quotes are fetched, we poll for new ones to keep the quotes up to date. Market and aggregator contract conditions can change fast enough
|
// Once quotes are fetched, we poll for new ones to keep the quotes up to date. Market and aggregator contract conditions can change fast enough
|
||||||
// that quotes will no longer be available after 1 or 2 minutes. When fetchAndSetQuotes is first called it, receives fetch that parameters are stored in
|
// that quotes will no longer be available after 1 or 2 minutes. When fetchAndSetQuotes is first called it, receives fetch that parameters are stored in
|
||||||
// state. These stored parameters are used on subsequent calls made during polling.
|
// state. These stored parameters are used on subsequent calls made during polling.
|
||||||
// Note: we stop polling after 3 requests, until new quotes are explicitly asked for. The logic that enforces that maximum is in the body of fetchAndSetQuotes
|
// Note: we stop polling after 3 requests, until new quotes are explicitly asked for. The logic that enforces that maximum is in the body of fetchAndSetQuotes
|
||||||
pollForNewQuotes() {
|
pollForNewQuotes() {
|
||||||
|
const {
|
||||||
|
swapsState: { swapsQuoteRefreshTime },
|
||||||
|
} = this.store.getState()
|
||||||
|
|
||||||
this.pollingTimeout = setTimeout(() => {
|
this.pollingTimeout = setTimeout(() => {
|
||||||
const { swapsState } = this.store.getState()
|
const { swapsState } = this.store.getState()
|
||||||
this.fetchAndSetQuotes(
|
this.fetchAndSetQuotes(
|
||||||
@ -113,7 +142,7 @@ export default class SwapsController {
|
|||||||
swapsState.fetchParams?.metaData,
|
swapsState.fetchParams?.metaData,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}, QUOTE_POLLING_INTERVAL)
|
}, swapsQuoteRefreshTime - QUOTE_POLLING_DIFFERENCE_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopPollingForQuotes() {
|
stopPollingForQuotes() {
|
||||||
@ -128,7 +157,6 @@ export default class SwapsController {
|
|||||||
if (!fetchParams) {
|
if (!fetchParams) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every time we get a new request that is not from the polling, we reset the poll count so we can poll for up to three more sets of quotes with these new params.
|
// Every time we get a new request that is not from the polling, we reset the poll count so we can poll for up to three more sets of quotes with these new params.
|
||||||
if (!isPolledRequest) {
|
if (!isPolledRequest) {
|
||||||
this.pollCount = 0
|
this.pollCount = 0
|
||||||
@ -144,7 +172,10 @@ export default class SwapsController {
|
|||||||
const indexOfCurrentCall = this.indexOfNewestCallInFlight + 1
|
const indexOfCurrentCall = this.indexOfNewestCallInFlight + 1
|
||||||
this.indexOfNewestCallInFlight = indexOfCurrentCall
|
this.indexOfNewestCallInFlight = indexOfCurrentCall
|
||||||
|
|
||||||
let newQuotes = await this._fetchTradesInfo(fetchParams)
|
let [newQuotes] = await Promise.all([
|
||||||
|
this._fetchTradesInfo(fetchParams),
|
||||||
|
this._setSwapsQuoteRefreshTime(),
|
||||||
|
])
|
||||||
|
|
||||||
newQuotes = mapValues(newQuotes, (quote) => ({
|
newQuotes = mapValues(newQuotes, (quote) => ({
|
||||||
...quote,
|
...quote,
|
||||||
@ -422,6 +453,7 @@ export default class SwapsController {
|
|||||||
tokens: swapsState.tokens,
|
tokens: swapsState.tokens,
|
||||||
fetchParams: swapsState.fetchParams,
|
fetchParams: swapsState.fetchParams,
|
||||||
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
||||||
|
swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
clearTimeout(this.pollingTimeout)
|
clearTimeout(this.pollingTimeout)
|
||||||
@ -435,6 +467,7 @@ export default class SwapsController {
|
|||||||
...initialState.swapsState,
|
...initialState.swapsState,
|
||||||
tokens: swapsState.tokens,
|
tokens: swapsState.tokens,
|
||||||
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
||||||
|
swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
clearTimeout(this.pollingTimeout)
|
clearTimeout(this.pollingTimeout)
|
||||||
|
9
app/scripts/disable-console.js
Normal file
9
app/scripts/disable-console.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Disable console.log in contentscript to prevent SES/lockdown logging to external page
|
||||||
|
// eslint-disable-next-line import/unambiguous
|
||||||
|
if (
|
||||||
|
!(typeof process !== 'undefined' && process.env.METAMASK_DEBUG) &&
|
||||||
|
typeof console !== undefined
|
||||||
|
) {
|
||||||
|
console.log = () => undefined
|
||||||
|
console.info = () => undefined
|
||||||
|
}
|
@ -1,8 +1,19 @@
|
|||||||
// Freezes all intrinsics
|
// Freezes all intrinsics
|
||||||
// eslint-disable-next-line no-undef,import/unambiguous
|
try {
|
||||||
lockdown({
|
// eslint-disable-next-line no-undef,import/unambiguous
|
||||||
consoleTaming: 'unsafe',
|
lockdown({
|
||||||
errorTaming: 'unsafe',
|
consoleTaming: 'unsafe',
|
||||||
mathTaming: 'unsafe',
|
errorTaming: 'unsafe',
|
||||||
dateTaming: 'unsafe',
|
mathTaming: 'unsafe',
|
||||||
})
|
dateTaming: 'unsafe',
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
// If the `lockdown` call throws an exception, it interferes with the
|
||||||
|
// contentscript injection on some versions of Firefox. The error is
|
||||||
|
// caught and logged here so that the contentscript still gets injected.
|
||||||
|
// This affects Firefox v56 and Waterfox Classic
|
||||||
|
console.error('Lockdown failed:', error)
|
||||||
|
if (window.sentry && window.sentry.captureException) {
|
||||||
|
window.sentry.captureException(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -115,6 +115,7 @@ function createScriptTasks({ browserPlatforms, livereload }) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// inpage must be built before contentscript
|
// inpage must be built before contentscript
|
||||||
// because inpage bundle result is included inside contentscript
|
// because inpage bundle result is included inside contentscript
|
||||||
const contentscriptSubtask = createTask(
|
const contentscriptSubtask = createTask(
|
||||||
@ -122,6 +123,12 @@ function createScriptTasks({ browserPlatforms, livereload }) {
|
|||||||
createTaskForBuildJsExtensionContentscript({ devMode, testing }),
|
createTaskForBuildJsExtensionContentscript({ devMode, testing }),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// this can run whenever
|
||||||
|
const disableConsoleSubtask = createTask(
|
||||||
|
`${taskPrefix}:disable-console`,
|
||||||
|
createTaskForBuildJsExtensionDisableConsole({ devMode }),
|
||||||
|
)
|
||||||
|
|
||||||
// task for initiating livereload
|
// task for initiating livereload
|
||||||
const initiateLiveReload = async () => {
|
const initiateLiveReload = async () => {
|
||||||
if (devMode) {
|
if (devMode) {
|
||||||
@ -142,6 +149,7 @@ function createScriptTasks({ browserPlatforms, livereload }) {
|
|||||||
const allSubtasks = [
|
const allSubtasks = [
|
||||||
...standardSubtasks,
|
...standardSubtasks,
|
||||||
contentscriptSubtask,
|
contentscriptSubtask,
|
||||||
|
disableConsoleSubtask,
|
||||||
].map((subtask) => runInChildProcess(subtask))
|
].map((subtask) => runInChildProcess(subtask))
|
||||||
// const allSubtasks = [...standardSubtasks, contentscriptSubtask].map(subtask => (subtask))
|
// const allSubtasks = [...standardSubtasks, contentscriptSubtask].map(subtask => (subtask))
|
||||||
// make a parent task that runs each task in a child thread
|
// make a parent task that runs each task in a child thread
|
||||||
@ -165,6 +173,16 @@ function createScriptTasks({ browserPlatforms, livereload }) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTaskForBuildJsExtensionDisableConsole({ devMode }) {
|
||||||
|
const filename = 'disable-console'
|
||||||
|
return bundleTask({
|
||||||
|
label: filename,
|
||||||
|
filename: `${filename}.js`,
|
||||||
|
filepath: `./app/scripts/${filename}.js`,
|
||||||
|
devMode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function createTaskForBuildJsExtensionContentscript({ devMode, testing }) {
|
function createTaskForBuildJsExtensionContentscript({ devMode, testing }) {
|
||||||
const inpage = 'inpage'
|
const inpage = 'inpage'
|
||||||
const contentscript = 'contentscript'
|
const contentscript = 'contentscript'
|
||||||
|
@ -121,12 +121,14 @@ const EMPTY_INIT_STATE = {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: false,
|
swapsFeatureIsLive: false,
|
||||||
|
swapsQuoteRefreshTime: 60000,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const sandbox = sinon.createSandbox()
|
const sandbox = sinon.createSandbox()
|
||||||
const fetchTradesInfoStub = sandbox.stub()
|
const fetchTradesInfoStub = sandbox.stub()
|
||||||
const fetchSwapsFeatureLivenessStub = sandbox.stub()
|
const fetchSwapsFeatureLivenessStub = sandbox.stub()
|
||||||
|
const fetchSwapsQuoteRefreshTimeStub = sandbox.stub()
|
||||||
|
|
||||||
describe('SwapsController', function () {
|
describe('SwapsController', function () {
|
||||||
let provider
|
let provider
|
||||||
@ -140,6 +142,7 @@ describe('SwapsController', function () {
|
|||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
||||||
|
fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,9 +642,9 @@ describe('SwapsController', function () {
|
|||||||
const quotes = await swapsController.fetchAndSetQuotes(undefined)
|
const quotes = await swapsController.fetchAndSetQuotes(undefined)
|
||||||
assert.strictEqual(quotes, null)
|
assert.strictEqual(quotes, null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls fetchTradesInfo with the given fetchParams and returns the correct quotes', async function () {
|
it('calls fetchTradesInfo with the given fetchParams and returns the correct quotes', async function () {
|
||||||
fetchTradesInfoStub.resolves(getMockQuotes())
|
fetchTradesInfoStub.resolves(getMockQuotes())
|
||||||
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime())
|
||||||
|
|
||||||
// Make it so approval is not required
|
// Make it so approval is not required
|
||||||
sandbox
|
sandbox
|
||||||
@ -682,9 +685,9 @@ describe('SwapsController', function () {
|
|||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('performs the allowance check', async function () {
|
it('performs the allowance check', async function () {
|
||||||
fetchTradesInfoStub.resolves(getMockQuotes())
|
fetchTradesInfoStub.resolves(getMockQuotes())
|
||||||
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime())
|
||||||
|
|
||||||
// Make it so approval is not required
|
// Make it so approval is not required
|
||||||
const allowanceStub = sandbox
|
const allowanceStub = sandbox
|
||||||
@ -707,6 +710,7 @@ describe('SwapsController', function () {
|
|||||||
|
|
||||||
it('gets the gas limit if approval is required', async function () {
|
it('gets the gas limit if approval is required', async function () {
|
||||||
fetchTradesInfoStub.resolves(MOCK_QUOTES_APPROVAL_REQUIRED)
|
fetchTradesInfoStub.resolves(MOCK_QUOTES_APPROVAL_REQUIRED)
|
||||||
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime())
|
||||||
|
|
||||||
// Ensure approval is required
|
// Ensure approval is required
|
||||||
sandbox
|
sandbox
|
||||||
@ -732,6 +736,7 @@ describe('SwapsController', function () {
|
|||||||
|
|
||||||
it('marks the best quote', async function () {
|
it('marks the best quote', async function () {
|
||||||
fetchTradesInfoStub.resolves(getMockQuotes())
|
fetchTradesInfoStub.resolves(getMockQuotes())
|
||||||
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime())
|
||||||
|
|
||||||
// Make it so approval is not required
|
// Make it so approval is not required
|
||||||
sandbox
|
sandbox
|
||||||
@ -762,6 +767,7 @@ describe('SwapsController', function () {
|
|||||||
}
|
}
|
||||||
const quotes = { ...getMockQuotes(), [bestAggId]: bestQuote }
|
const quotes = { ...getMockQuotes(), [bestAggId]: bestQuote }
|
||||||
fetchTradesInfoStub.resolves(quotes)
|
fetchTradesInfoStub.resolves(quotes)
|
||||||
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime())
|
||||||
|
|
||||||
// Make it so approval is not required
|
// Make it so approval is not required
|
||||||
sandbox
|
sandbox
|
||||||
@ -779,6 +785,7 @@ describe('SwapsController', function () {
|
|||||||
|
|
||||||
it('does not mark as best quote if no conversion rate exists for destination token', async function () {
|
it('does not mark as best quote if no conversion rate exists for destination token', async function () {
|
||||||
fetchTradesInfoStub.resolves(getMockQuotes())
|
fetchTradesInfoStub.resolves(getMockQuotes())
|
||||||
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime())
|
||||||
|
|
||||||
// Make it so approval is not required
|
// Make it so approval is not required
|
||||||
sandbox
|
sandbox
|
||||||
@ -805,6 +812,7 @@ describe('SwapsController', function () {
|
|||||||
assert.deepStrictEqual(swapsState, {
|
assert.deepStrictEqual(swapsState, {
|
||||||
...EMPTY_INIT_STATE.swapsState,
|
...EMPTY_INIT_STATE.swapsState,
|
||||||
tokens: old.tokens,
|
tokens: old.tokens,
|
||||||
|
swapsQuoteRefreshTime: old.swapsQuoteRefreshTime,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -850,8 +858,14 @@ describe('SwapsController', function () {
|
|||||||
const tokens = 'test'
|
const tokens = 'test'
|
||||||
const fetchParams = 'test'
|
const fetchParams = 'test'
|
||||||
const swapsFeatureIsLive = false
|
const swapsFeatureIsLive = false
|
||||||
|
const swapsQuoteRefreshTime = 0
|
||||||
swapsController.store.updateState({
|
swapsController.store.updateState({
|
||||||
swapsState: { tokens, fetchParams, swapsFeatureIsLive },
|
swapsState: {
|
||||||
|
tokens,
|
||||||
|
fetchParams,
|
||||||
|
swapsFeatureIsLive,
|
||||||
|
swapsQuoteRefreshTime,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
swapsController.resetPostFetchState()
|
swapsController.resetPostFetchState()
|
||||||
@ -862,6 +876,7 @@ describe('SwapsController', function () {
|
|||||||
tokens,
|
tokens,
|
||||||
fetchParams,
|
fetchParams,
|
||||||
swapsFeatureIsLive,
|
swapsFeatureIsLive,
|
||||||
|
swapsQuoteRefreshTime,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -1615,3 +1630,7 @@ function getTopQuoteAndSavingsBaseExpectedResults() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMockQuoteRefreshTime() {
|
||||||
|
return 45000
|
||||||
|
}
|
||||||
|
@ -29,7 +29,7 @@ export default class SignatureRequestMessage extends PureComponent {
|
|||||||
this.renderNode(value)
|
this.renderNode(value)
|
||||||
) : (
|
) : (
|
||||||
<span className="signature-request-message--node-value">
|
<span className="signature-request-message--node-value">
|
||||||
{value}
|
{`${value}`}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,7 +39,6 @@ import { calcGasTotal } from '../../pages/send/send.utils'
|
|||||||
import {
|
import {
|
||||||
decimalToHex,
|
decimalToHex,
|
||||||
getValueFromWeiHex,
|
getValueFromWeiHex,
|
||||||
hexMax,
|
|
||||||
decGWEIToHexWEI,
|
decGWEIToHexWEI,
|
||||||
hexToDecimal,
|
hexToDecimal,
|
||||||
hexWEIToDecGWEI,
|
hexWEIToDecGWEI,
|
||||||
@ -67,6 +66,8 @@ const GAS_PRICES_LOADING_STATES = {
|
|||||||
COMPLETED: 'COMPLETED',
|
COMPLETED: 'COMPLETED',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FALLBACK_GAS_MULTIPLIER = 1.5
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
aggregatorMetadata: null,
|
aggregatorMetadata: null,
|
||||||
approveTxId: null,
|
approveTxId: null,
|
||||||
@ -223,6 +224,9 @@ const getSwapsState = (state) => state.metamask.swapsState
|
|||||||
export const getSwapsFeatureLiveness = (state) =>
|
export const getSwapsFeatureLiveness = (state) =>
|
||||||
state.metamask.swapsState.swapsFeatureIsLive
|
state.metamask.swapsState.swapsFeatureIsLive
|
||||||
|
|
||||||
|
export const getSwapsQuoteRefreshTime = (state) =>
|
||||||
|
state.metamask.swapsState.swapsQuoteRefreshTime
|
||||||
|
|
||||||
export const getBackgroundSwapRouteState = (state) =>
|
export const getBackgroundSwapRouteState = (state) =>
|
||||||
state.metamask.swapsState.routeState
|
state.metamask.swapsState.routeState
|
||||||
|
|
||||||
@ -593,20 +597,16 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
|||||||
const usedQuote = getUsedQuote(state)
|
const usedQuote = getUsedQuote(state)
|
||||||
const usedTradeTxParams = usedQuote.trade
|
const usedTradeTxParams = usedQuote.trade
|
||||||
|
|
||||||
const estimatedGasLimit = new BigNumber(
|
const estimatedGasLimit = new BigNumber(usedQuote?.gasEstimate || `0x0`, 16)
|
||||||
usedQuote?.gasEstimate || decimalToHex(usedQuote?.averageGas || 0),
|
|
||||||
16,
|
|
||||||
)
|
|
||||||
const estimatedGasLimitWithMultiplier = estimatedGasLimit
|
const estimatedGasLimitWithMultiplier = estimatedGasLimit
|
||||||
.times(1.4, 10)
|
.times(usedQuote?.gasMultiplier || FALLBACK_GAS_MULTIPLIER, 10)
|
||||||
.round(0)
|
.round(0)
|
||||||
.toString(16)
|
.toString(16)
|
||||||
const maxGasLimit =
|
const maxGasLimit =
|
||||||
customSwapsGas ||
|
customSwapsGas ||
|
||||||
hexMax(
|
(usedQuote?.gasEstimate
|
||||||
`0x${decimalToHex(usedQuote?.maxGas || 0)}`,
|
? estimatedGasLimitWithMultiplier
|
||||||
estimatedGasLimitWithMultiplier,
|
: usedQuote?.maxGas)
|
||||||
)
|
|
||||||
|
|
||||||
const usedGasPrice = getUsedSwapsGasPrice(state)
|
const usedGasPrice = getUsedSwapsGasPrice(state)
|
||||||
usedTradeTxParams.gas = maxGasLimit
|
usedTradeTxParams.gas = maxGasLimit
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import BigNumber from 'bignumber.js'
|
|
||||||
import { ETH, GWEI, WEI } from '../constants/common'
|
import { ETH, GWEI, WEI } from '../constants/common'
|
||||||
import { addHexPrefix } from '../../../../app/scripts/lib/util'
|
import { addHexPrefix } from '../../../../app/scripts/lib/util'
|
||||||
import {
|
import {
|
||||||
@ -163,16 +162,6 @@ export function hexWEIToDecETH(hexWEI) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hexMax(...hexNumbers) {
|
|
||||||
let max = hexNumbers[0]
|
|
||||||
hexNumbers.slice(1).forEach((hexNumber) => {
|
|
||||||
if (new BigNumber(hexNumber, 16).gt(max, 16)) {
|
|
||||||
max = hexNumber
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addHexes(aHexWEI, bHexWEI) {
|
export function addHexes(aHexWEI, bHexWEI) {
|
||||||
return addCurrencies(aHexWEI, bHexWEI, {
|
return addCurrencies(aHexWEI, bHexWEI, {
|
||||||
aBase: 16,
|
aBase: 16,
|
||||||
|
@ -16,7 +16,6 @@ const fetchWithCache = async (
|
|||||||
fetchOptions.headers = new window.Headers(fetchOptions.headers)
|
fetchOptions.headers = new window.Headers(fetchOptions.headers)
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
fetchOptions.headers &&
|
|
||||||
fetchOptions.headers.has('Content-Type') &&
|
fetchOptions.headers.has('Content-Type') &&
|
||||||
fetchOptions.headers.get('Content-Type') !== 'application/json'
|
fetchOptions.headers.get('Content-Type') !== 'application/json'
|
||||||
) {
|
) {
|
||||||
@ -24,8 +23,8 @@ const fetchWithCache = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentTime = Date.now()
|
const currentTime = Date.now()
|
||||||
const cachedFetch = (await getStorageItem('cachedFetch')) || {}
|
const cacheKey = `cachedFetch:${url}`
|
||||||
const { cachedResponse, cachedTime } = cachedFetch[url] || {}
|
const { cachedResponse, cachedTime } = (await getStorageItem(cacheKey)) || {}
|
||||||
if (cachedResponse && currentTime - cachedTime < cacheRefreshTime) {
|
if (cachedResponse && currentTime - cachedTime < cacheRefreshTime) {
|
||||||
return cachedResponse
|
return cachedResponse
|
||||||
}
|
}
|
||||||
@ -48,8 +47,8 @@ const fetchWithCache = async (
|
|||||||
cachedResponse: responseJson,
|
cachedResponse: responseJson,
|
||||||
cachedTime: currentTime,
|
cachedTime: currentTime,
|
||||||
}
|
}
|
||||||
cachedFetch[url] = cacheEntry
|
|
||||||
await setStorageItem('cachedFetch', cachedFetch)
|
await setStorageItem(cacheKey, cacheEntry)
|
||||||
return responseJson
|
return responseJson
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,10 +37,8 @@ describe('Fetch with cache', function () {
|
|||||||
.reply(200, '{"average": 2}')
|
.reply(200, '{"average": 2}')
|
||||||
|
|
||||||
fakeStorage.getStorageItem.returns({
|
fakeStorage.getStorageItem.returns({
|
||||||
'https://fetchwithcache.metamask.io/price': {
|
cachedResponse: { average: 1 },
|
||||||
cachedResponse: { average: 1 },
|
cachedTime: Date.now(),
|
||||||
cachedTime: Date.now(),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
@ -57,10 +55,8 @@ describe('Fetch with cache', function () {
|
|||||||
.reply(200, '{"average": 3}')
|
.reply(200, '{"average": 3}')
|
||||||
|
|
||||||
fakeStorage.getStorageItem.returns({
|
fakeStorage.getStorageItem.returns({
|
||||||
'https://fetchwithcache.metamask.io/cached': {
|
cachedResponse: { average: 1 },
|
||||||
cachedResponse: { average: 1 },
|
cachedTime: Date.now() - 1000,
|
||||||
cachedTime: Date.now() - 1000,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
@ -135,4 +131,43 @@ describe('Fetch with cache', function () {
|
|||||||
{ message: 'fetchWithCache only supports JSON responses' },
|
{ message: 'fetchWithCache only supports JSON responses' },
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should correctly cache responses from interwoven requests', async function () {
|
||||||
|
nock('https://fetchwithcache.metamask.io')
|
||||||
|
.get('/foo')
|
||||||
|
.reply(200, '{"average": 9}')
|
||||||
|
nock('https://fetchwithcache.metamask.io')
|
||||||
|
.get('/bar')
|
||||||
|
.reply(200, '{"average": 9}')
|
||||||
|
|
||||||
|
const testCache = {}
|
||||||
|
fakeStorage.getStorageItem.callsFake((key) => testCache[key])
|
||||||
|
fakeStorage.setStorageItem.callsFake((key, value) => {
|
||||||
|
testCache[key] = value
|
||||||
|
})
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
fetchWithCache(
|
||||||
|
'https://fetchwithcache.metamask.io/foo',
|
||||||
|
{},
|
||||||
|
{ cacheRefreshTime: 123 },
|
||||||
|
),
|
||||||
|
fetchWithCache(
|
||||||
|
'https://fetchwithcache.metamask.io/bar',
|
||||||
|
{},
|
||||||
|
{ cacheRefreshTime: 123 },
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
testCache['cachedFetch:https://fetchwithcache.metamask.io/foo']
|
||||||
|
.cachedResponse,
|
||||||
|
{ average: 9 },
|
||||||
|
)
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
testCache['cachedFetch:https://fetchwithcache.metamask.io/bar']
|
||||||
|
.cachedResponse,
|
||||||
|
{ average: 9 },
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -105,7 +105,10 @@ export default class MetaMetricsOptIn extends Component {
|
|||||||
await setParticipateInMetaMetrics(false)
|
await setParticipateInMetaMetrics(false)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (participateInMetaMetrics === true) {
|
if (
|
||||||
|
participateInMetaMetrics === null ||
|
||||||
|
participateInMetaMetrics === true
|
||||||
|
) {
|
||||||
await metricsEvent({
|
await metricsEvent({
|
||||||
eventOpts: {
|
eventOpts: {
|
||||||
category: 'Onboarding',
|
category: 'Onboarding',
|
||||||
@ -128,7 +131,10 @@ export default class MetaMetricsOptIn extends Component {
|
|||||||
)
|
)
|
||||||
try {
|
try {
|
||||||
const metrics = []
|
const metrics = []
|
||||||
if (participateInMetaMetrics === false) {
|
if (
|
||||||
|
participateInMetaMetrics === null ||
|
||||||
|
participateInMetaMetrics === false
|
||||||
|
) {
|
||||||
metrics.push(
|
metrics.push(
|
||||||
metricsEvent({
|
metricsEvent({
|
||||||
eventOpts: {
|
eventOpts: {
|
||||||
|
@ -156,7 +156,7 @@ export default class SendTransactionScreen extends Component {
|
|||||||
|
|
||||||
if (sendTokenAddress && prevTokenAddress !== sendTokenAddress) {
|
if (sendTokenAddress && prevTokenAddress !== sendTokenAddress) {
|
||||||
this.updateSendToken()
|
this.updateSendToken()
|
||||||
this.validate(sendTokenAddress)
|
this.validate(this.state.query)
|
||||||
updateGas = true
|
updateGas = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React, { useState, useEffect, useContext, useRef } from 'react'
|
import React, { useState, useEffect, useContext, useRef } from 'react'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Duration } from 'luxon'
|
import { Duration } from 'luxon'
|
||||||
import { I18nContext } from '../../../contexts/i18n'
|
import { I18nContext } from '../../../contexts/i18n'
|
||||||
import InfoTooltip from '../../../components/ui/info-tooltip'
|
import InfoTooltip from '../../../components/ui/info-tooltip'
|
||||||
|
import { getSwapsQuoteRefreshTime } from '../../../ducks/swaps/swaps'
|
||||||
const TIMER_BASE = 60000
|
|
||||||
|
|
||||||
// Return the mm:ss start time of the countdown timer.
|
// Return the mm:ss start time of the countdown timer.
|
||||||
// If time has elapsed between `timeStarted` the time current time,
|
// If time has elapsed between `timeStarted` the time current time,
|
||||||
@ -31,7 +31,7 @@ function timeBelowWarningTime(timer, warningTime) {
|
|||||||
export default function CountdownTimer({
|
export default function CountdownTimer({
|
||||||
timeStarted,
|
timeStarted,
|
||||||
timeOnly,
|
timeOnly,
|
||||||
timerBase = TIMER_BASE,
|
timerBase,
|
||||||
warningTime,
|
warningTime,
|
||||||
labelKey,
|
labelKey,
|
||||||
infoTooltipLabelKey,
|
infoTooltipLabelKey,
|
||||||
@ -40,9 +40,12 @@ export default function CountdownTimer({
|
|||||||
const intervalRef = useRef()
|
const intervalRef = useRef()
|
||||||
const initialTimeStartedRef = useRef()
|
const initialTimeStartedRef = useRef()
|
||||||
|
|
||||||
|
const swapsQuoteRefreshTime = useSelector(getSwapsQuoteRefreshTime)
|
||||||
|
const timerStart = Number(timerBase) || swapsQuoteRefreshTime
|
||||||
|
|
||||||
const [currentTime, setCurrentTime] = useState(() => Date.now())
|
const [currentTime, setCurrentTime] = useState(() => Date.now())
|
||||||
const [timer, setTimer] = useState(() =>
|
const [timer, setTimer] = useState(() =>
|
||||||
getNewTimer(currentTime, timeStarted, timerBase),
|
getNewTimer(currentTime, timeStarted, timerStart),
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -67,14 +70,14 @@ export default function CountdownTimer({
|
|||||||
initialTimeStartedRef.current = timeStarted
|
initialTimeStartedRef.current = timeStarted
|
||||||
const newCurrentTime = Date.now()
|
const newCurrentTime = Date.now()
|
||||||
setCurrentTime(newCurrentTime)
|
setCurrentTime(newCurrentTime)
|
||||||
setTimer(getNewTimer(newCurrentTime, timeStarted, timerBase))
|
setTimer(getNewTimer(newCurrentTime, timeStarted, timerStart))
|
||||||
|
|
||||||
clearInterval(intervalRef.current)
|
clearInterval(intervalRef.current)
|
||||||
intervalRef.current = setInterval(() => {
|
intervalRef.current = setInterval(() => {
|
||||||
setTimer(decreaseTimerByOne)
|
setTimer(decreaseTimerByOne)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
}, [timeStarted, timer, timerBase])
|
}, [timeStarted, timer, timerStart])
|
||||||
|
|
||||||
const formattedTimer = Duration.fromMillis(timer).toFormat('m:ss')
|
const formattedTimer = Duration.fromMillis(timer).toFormat('m:ss')
|
||||||
let time
|
let time
|
||||||
|
@ -24,20 +24,24 @@ const TOKEN_TRANSFER_LOG_TOPIC_HASH =
|
|||||||
|
|
||||||
const CACHE_REFRESH_ONE_HOUR = 3600000
|
const CACHE_REFRESH_ONE_HOUR = 3600000
|
||||||
|
|
||||||
|
const METASWAP_API_HOST = 'https://api.metaswap.codefi.network'
|
||||||
|
|
||||||
const getBaseApi = function (type) {
|
const getBaseApi = function (type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'trade':
|
case 'trade':
|
||||||
return `https://api.metaswap.codefi.network/trades?`
|
return `${METASWAP_API_HOST}/trades?`
|
||||||
case 'tokens':
|
case 'tokens':
|
||||||
return `https://api.metaswap.codefi.network/tokens`
|
return `${METASWAP_API_HOST}/tokens`
|
||||||
case 'topAssets':
|
case 'topAssets':
|
||||||
return `https://api.metaswap.codefi.network/topAssets`
|
return `${METASWAP_API_HOST}/topAssets`
|
||||||
case 'featureFlag':
|
case 'featureFlag':
|
||||||
return `https://api.metaswap.codefi.network/featureFlag`
|
return `${METASWAP_API_HOST}/featureFlag`
|
||||||
case 'aggregatorMetadata':
|
case 'aggregatorMetadata':
|
||||||
return `https://api.metaswap.codefi.network/aggregatorMetadata`
|
return `${METASWAP_API_HOST}/aggregatorMetadata`
|
||||||
case 'gasPrices':
|
case 'gasPrices':
|
||||||
return `https://api.metaswap.codefi.network/gasPrices`
|
return `${METASWAP_API_HOST}/gasPrices`
|
||||||
|
case 'refreshTime':
|
||||||
|
return `${METASWAP_API_HOST}/quoteRefreshRate`
|
||||||
default:
|
default:
|
||||||
throw new Error('getBaseApi requires an api call type')
|
throw new Error('getBaseApi requires an api call type')
|
||||||
}
|
}
|
||||||
@ -112,6 +116,11 @@ const QUOTE_VALIDATORS = [
|
|||||||
property: 'maxGas',
|
property: 'maxGas',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
property: 'gasEstimate',
|
||||||
|
type: 'number|undefined',
|
||||||
|
validator: (gasEstimate) => gasEstimate === undefined || gasEstimate > 0,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const TOKEN_VALIDATORS = [
|
const TOKEN_VALIDATORS = [
|
||||||
@ -323,6 +332,23 @@ export async function fetchSwapsFeatureLiveness() {
|
|||||||
return status?.active
|
return status?.active
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchSwapsQuoteRefreshTime() {
|
||||||
|
const response = await fetchWithCache(
|
||||||
|
getBaseApi('refreshTime'),
|
||||||
|
{ method: 'GET' },
|
||||||
|
{ cacheRefreshTime: 600000 },
|
||||||
|
)
|
||||||
|
|
||||||
|
// We presently use milliseconds in the UI
|
||||||
|
if (typeof response?.seconds === 'number' && response.seconds > 0) {
|
||||||
|
return response.seconds * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`MetaMask - refreshTime provided invalid response: ${response}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchTokenPrice(address) {
|
export async function fetchTokenPrice(address) {
|
||||||
const query = `contract_addresses=${address}&vs_currencies=eth`
|
const query = `contract_addresses=${address}&vs_currencies=eth`
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import { useSwapsEthToken } from '../../../hooks/useSwapsEthToken'
|
|||||||
import { MetaMetricsContext } from '../../../contexts/metametrics.new'
|
import { MetaMetricsContext } from '../../../contexts/metametrics.new'
|
||||||
import FeeCard from '../fee-card'
|
import FeeCard from '../fee-card'
|
||||||
import {
|
import {
|
||||||
|
FALLBACK_GAS_MULTIPLIER,
|
||||||
getQuotes,
|
getQuotes,
|
||||||
getSelectedQuote,
|
getSelectedQuote,
|
||||||
getApproveTxParams,
|
getApproveTxParams,
|
||||||
@ -27,6 +28,7 @@ import {
|
|||||||
signAndSendTransactions,
|
signAndSendTransactions,
|
||||||
getBackgroundSwapRouteState,
|
getBackgroundSwapRouteState,
|
||||||
swapsQuoteSelected,
|
swapsQuoteSelected,
|
||||||
|
getSwapsQuoteRefreshTime,
|
||||||
} from '../../../ducks/swaps/swaps'
|
} from '../../../ducks/swaps/swaps'
|
||||||
import {
|
import {
|
||||||
conversionRateSelector,
|
conversionRateSelector,
|
||||||
@ -57,7 +59,6 @@ import {
|
|||||||
} from '../../../helpers/utils/token-util'
|
} from '../../../helpers/utils/token-util'
|
||||||
import {
|
import {
|
||||||
decimalToHex,
|
decimalToHex,
|
||||||
hexMax,
|
|
||||||
hexToDecimal,
|
hexToDecimal,
|
||||||
getValueFromWeiHex,
|
getValueFromWeiHex,
|
||||||
} from '../../../helpers/utils/conversions.util'
|
} from '../../../helpers/utils/conversions.util'
|
||||||
@ -115,6 +116,7 @@ export default function ViewQuote() {
|
|||||||
const topQuote = useSelector(getTopQuote)
|
const topQuote = useSelector(getTopQuote)
|
||||||
const usedQuote = selectedQuote || topQuote
|
const usedQuote = selectedQuote || topQuote
|
||||||
const tradeValue = usedQuote?.trade?.value ?? '0x0'
|
const tradeValue = usedQuote?.trade?.value ?? '0x0'
|
||||||
|
const swapsQuoteRefreshTime = useSelector(getSwapsQuoteRefreshTime)
|
||||||
|
|
||||||
const { isBestQuote } = usedQuote
|
const { isBestQuote } = usedQuote
|
||||||
const fetchParamsSourceToken = fetchParams?.sourceToken
|
const fetchParamsSourceToken = fetchParams?.sourceToken
|
||||||
@ -123,18 +125,16 @@ export default function ViewQuote() {
|
|||||||
usedQuote?.gasEstimateWithRefund ||
|
usedQuote?.gasEstimateWithRefund ||
|
||||||
`0x${decimalToHex(usedQuote?.averageGas || 0)}`
|
`0x${decimalToHex(usedQuote?.averageGas || 0)}`
|
||||||
|
|
||||||
const gasLimitForMax =
|
const gasLimitForMax = usedQuote?.gasEstimate || `0x0`
|
||||||
usedQuote?.gasEstimate || `0x${decimalToHex(usedQuote?.averageGas || 0)}`
|
|
||||||
|
|
||||||
const usedGasLimitWithMultiplier = new BigNumber(gasLimitForMax, 16)
|
const usedGasLimitWithMultiplier = new BigNumber(gasLimitForMax, 16)
|
||||||
.times(1.4, 10)
|
.times(usedQuote?.gasMultiplier || FALLBACK_GAS_MULTIPLIER, 10)
|
||||||
.round(0)
|
.round(0)
|
||||||
.toString(16)
|
.toString(16)
|
||||||
|
|
||||||
const nonCustomMaxGasLimit = hexMax(
|
const nonCustomMaxGasLimit = usedQuote?.gasEstimate
|
||||||
`0x${decimalToHex(usedQuote?.maxGas || 0)}`,
|
? usedGasLimitWithMultiplier
|
||||||
usedGasLimitWithMultiplier,
|
: `0x${decimalToHex(usedQuote?.maxGas || 0)}`
|
||||||
)
|
|
||||||
const maxGasLimit = customMaxGas || nonCustomMaxGasLimit
|
const maxGasLimit = customMaxGas || nonCustomMaxGasLimit
|
||||||
|
|
||||||
const gasTotalInWeiHex = calcGasTotal(maxGasLimit, gasPrice)
|
const gasTotalInWeiHex = calcGasTotal(maxGasLimit, gasPrice)
|
||||||
@ -265,14 +265,23 @@ export default function ViewQuote() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentTime = Date.now()
|
const currentTime = Date.now()
|
||||||
const timeSinceLastFetched = currentTime - quotesLastFetched
|
const timeSinceLastFetched = currentTime - quotesLastFetched
|
||||||
if (timeSinceLastFetched > 60000 && !dispatchedSafeRefetch) {
|
if (
|
||||||
|
timeSinceLastFetched > swapsQuoteRefreshTime &&
|
||||||
|
!dispatchedSafeRefetch
|
||||||
|
) {
|
||||||
setDispatchedSafeRefetch(true)
|
setDispatchedSafeRefetch(true)
|
||||||
dispatch(safeRefetchQuotes())
|
dispatch(safeRefetchQuotes())
|
||||||
} else if (timeSinceLastFetched > 60000) {
|
} else if (timeSinceLastFetched > swapsQuoteRefreshTime) {
|
||||||
dispatch(setSwapsErrorKey(QUOTES_EXPIRED_ERROR))
|
dispatch(setSwapsErrorKey(QUOTES_EXPIRED_ERROR))
|
||||||
history.push(SWAPS_ERROR_ROUTE)
|
history.push(SWAPS_ERROR_ROUTE)
|
||||||
}
|
}
|
||||||
}, [quotesLastFetched, dispatchedSafeRefetch, dispatch, history])
|
}, [
|
||||||
|
quotesLastFetched,
|
||||||
|
dispatchedSafeRefetch,
|
||||||
|
dispatch,
|
||||||
|
history,
|
||||||
|
swapsQuoteRefreshTime,
|
||||||
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!originalApproveAmount && approveAmount) {
|
if (!originalApproveAmount && approveAmount) {
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -24922,9 +24922,9 @@ tunnel@^0.0.6:
|
|||||||
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
|
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
|
||||||
|
|
||||||
tweetnacl-util@^0.15.0:
|
tweetnacl-util@^0.15.0:
|
||||||
version "0.15.0"
|
version "0.15.1"
|
||||||
resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.0.tgz#4576c1cee5e2d63d207fee52f1ba02819480bc75"
|
resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b"
|
||||||
integrity sha1-RXbBzuXi1j0gf+5S8boCgZSAvHU=
|
integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==
|
||||||
|
|
||||||
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||||
version "0.14.5"
|
version "0.14.5"
|
||||||
@ -24932,9 +24932,9 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
|||||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||||
|
|
||||||
tweetnacl@^1.0.0, tweetnacl@^1.0.1:
|
tweetnacl@^1.0.0, tweetnacl@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.1.tgz#2594d42da73cd036bd0d2a54683dd35a6b55ca17"
|
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||||
integrity sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==
|
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||||
|
|
||||||
type-check@^0.4.0, type-check@~0.4.0:
|
type-check@^0.4.0, type-check@~0.4.0:
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user