diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 45728a415..78e00d6b2 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1266,6 +1266,9 @@ "networkNameEthereum": { "message": "Ethereum" }, + "networkNamePolygon": { + "message": "Polygon" + }, "networkNameTestnet": { "message": "Testnet" }, diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index 9b2c21204..424039408 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -19,7 +19,6 @@ import { isSwapsDefaultTokenAddress } from '../../../shared/modules/swaps.utils' import { fetchTradesInfo as defaultFetchTradesInfo, - fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness, fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime, } from '../../../ui/pages/swaps/swaps.util'; import { MINUTE, SECOND } from '../../../shared/constants/time'; @@ -73,6 +72,7 @@ const initialState = { topAggId: null, routeState: '', swapsFeatureIsLive: true, + useNewSwapsApi: false, swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME, }, }; @@ -85,7 +85,6 @@ export default class SwapsController { getProviderConfig, tokenRatesStore, fetchTradesInfo = defaultFetchTradesInfo, - fetchSwapsFeatureLiveness = defaultFetchSwapsFeatureLiveness, fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime, getCurrentChainId, }) { @@ -94,7 +93,6 @@ export default class SwapsController { }); this._fetchTradesInfo = fetchTradesInfo; - this._fetchSwapsFeatureLiveness = fetchSwapsFeatureLiveness; this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime; this._getCurrentChainId = getCurrentChainId; @@ -119,15 +117,19 @@ export default class SwapsController { // Sets the refresh rate for quote updates from the MetaSwap API async _setSwapsQuoteRefreshTime() { const chainId = this._getCurrentChainId(); + const { swapsState } = this.store.getState(); + // Default to fallback time unless API returns valid response let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME; try { - swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime(chainId); + swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime( + chainId, + swapsState.useNewSwapsApi, + ); } catch (e) { console.error('Request for swaps quote refresh time failed: ', e); } - const { swapsState } = this.store.getState(); this.store.updateState({ swapsState: { ...swapsState, swapsQuoteRefreshTime }, }); @@ -162,6 +164,9 @@ export default class SwapsController { isPolledRequest, ) { const { chainId } = fetchParamsMetaData; + const { + swapsState: { useNewSwapsApi }, + } = this.store.getState(); if (!fetchParams) { return null; @@ -182,7 +187,10 @@ export default class SwapsController { this.indexOfNewestCallInFlight = indexOfCurrentCall; let [newQuotes] = await Promise.all([ - this._fetchTradesInfo(fetchParams, fetchParamsMetaData), + this._fetchTradesInfo(fetchParams, { + ...fetchParamsMetaData, + useNewSwapsApi, + }), this._setSwapsQuoteRefreshTime(), ]); @@ -449,22 +457,23 @@ export default class SwapsController { this.store.updateState({ swapsState: { ...swapsState, routeState } }); } - setSwapsLiveness(swapsFeatureIsLive) { + setSwapsLiveness(swapsLiveness) { const { swapsState } = this.store.getState(); + const { swapsFeatureIsLive, useNewSwapsApi } = swapsLiveness; this.store.updateState({ - swapsState: { ...swapsState, swapsFeatureIsLive }, + swapsState: { ...swapsState, swapsFeatureIsLive, useNewSwapsApi }, }); } resetPostFetchState() { const { swapsState } = this.store.getState(); - this.store.updateState({ swapsState: { ...initialState.swapsState, tokens: swapsState.tokens, fetchParams: swapsState.fetchParams, swapsFeatureIsLive: swapsState.swapsFeatureIsLive, + useNewSwapsApi: swapsState.useNewSwapsApi, swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime, }, }); @@ -473,7 +482,6 @@ export default class SwapsController { resetSwapsState() { const { swapsState } = this.store.getState(); - this.store.updateState({ swapsState: { ...initialState.swapsState, diff --git a/app/scripts/controllers/swaps.test.js b/app/scripts/controllers/swaps.test.js index d3b96a098..642191f92 100644 --- a/app/scripts/controllers/swaps.test.js +++ b/app/scripts/controllers/swaps.test.js @@ -128,13 +128,13 @@ const EMPTY_INIT_STATE = { topAggId: null, routeState: '', swapsFeatureIsLive: true, + useNewSwapsApi: false, swapsQuoteRefreshTime: 60000, }, }; const sandbox = sinon.createSandbox(); const fetchTradesInfoStub = sandbox.stub(); -const fetchSwapsFeatureLivenessStub = sandbox.stub(); const fetchSwapsQuoteRefreshTimeStub = sandbox.stub(); const getCurrentChainIdStub = sandbox.stub(); getCurrentChainIdStub.returns(MAINNET_CHAIN_ID); @@ -150,7 +150,6 @@ describe('SwapsController', function () { getProviderConfig: MOCK_GET_PROVIDER_CONFIG, tokenRatesStore: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, - fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub, fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub, getCurrentChainId: getCurrentChainIdStub, }); @@ -201,7 +200,6 @@ describe('SwapsController', function () { getProviderConfig: MOCK_GET_PROVIDER_CONFIG, tokenRatesStore: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, - fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub, getCurrentChainId: getCurrentChainIdStub, }); const currentEthersInstance = swapsController.ethersProvider; @@ -226,7 +224,6 @@ describe('SwapsController', function () { getProviderConfig: MOCK_GET_PROVIDER_CONFIG, tokenRatesStore: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, - fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub, getCurrentChainId: getCurrentChainIdStub, }); const currentEthersInstance = swapsController.ethersProvider; @@ -251,7 +248,6 @@ describe('SwapsController', function () { getProviderConfig: MOCK_GET_PROVIDER_CONFIG, tokenRatesStore: MOCK_TOKEN_RATES_STORE, fetchTradesInfo: fetchTradesInfoStub, - fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub, getCurrentChainId: getCurrentChainIdStub, }); const currentEthersInstance = swapsController.ethersProvider; @@ -658,6 +654,7 @@ describe('SwapsController', function () { const quotes = await swapsController.fetchAndSetQuotes(undefined); assert.strictEqual(quotes, null); }); + it('calls fetchTradesInfo with the given fetchParams and returns the correct quotes', async function () { fetchTradesInfoStub.resolves(getMockQuotes()); fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime()); @@ -695,15 +692,15 @@ describe('SwapsController', function () { metaMaskFeeInEth: '0.5050505050505050505', ethValueOfTokens: '50', }); - assert.strictEqual( - fetchTradesInfoStub.calledOnceWithExactly( - MOCK_FETCH_PARAMS, - MOCK_FETCH_METADATA, - ), + fetchTradesInfoStub.calledOnceWithExactly(MOCK_FETCH_PARAMS, { + ...MOCK_FETCH_METADATA, + useNewSwapsApi: false, + }), true, ); }); + it('performs the allowance check', async function () { fetchTradesInfoStub.resolves(getMockQuotes()); fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime()); @@ -878,12 +875,14 @@ describe('SwapsController', function () { const tokens = 'test'; const fetchParams = 'test'; const swapsFeatureIsLive = false; + const useNewSwapsApi = false; const swapsQuoteRefreshTime = 0; swapsController.store.updateState({ swapsState: { tokens, fetchParams, swapsFeatureIsLive, + useNewSwapsApi, swapsQuoteRefreshTime, }, }); diff --git a/jest.config.js b/jest.config.js index 3219d5eba..425f7580f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,10 +5,10 @@ module.exports = { coveragePathIgnorePatterns: ['.stories.js', '.snap'], coverageThreshold: { global: { - branches: 45.45, - functions: 55.29, - lines: 60.22, - statements: 60.43, + branches: 45.24, + functions: 51.94, + lines: 58.36, + statements: 58.6, }, }, setupFiles: ['./test/setup.js', './test/env.js'], diff --git a/shared/constants/network.js b/shared/constants/network.js index db4fe9a2e..d937f7d9a 100644 --- a/shared/constants/network.js +++ b/shared/constants/network.js @@ -21,6 +21,7 @@ export const LOCALHOST_CHAIN_ID = '0x539'; export const BSC_CHAIN_ID = '0x38'; export const OPTIMISM_CHAIN_ID = '0xa'; export const OPTIMISM_TESTNET_CHAIN_ID = '0x45'; +export const POLYGON_CHAIN_ID = '0x89'; /** * The largest possible chain ID we can handle. diff --git a/shared/constants/swaps.js b/shared/constants/swaps.js index 42a2167f3..00512c0cc 100644 --- a/shared/constants/swaps.js +++ b/shared/constants/swaps.js @@ -93,3 +93,7 @@ export const SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP = { [BSC_CHAIN_ID]: BSC_DEFAULT_BLOCK_EXPLORER_URL, [MAINNET_CHAIN_ID]: MAINNET_DEFAULT_BLOCK_EXPLORER_URL, }; + +export const ETHEREUM = 'ethereum'; +export const POLYGON = 'polygon'; +export const BSC = 'bsc'; diff --git a/test/data/fetch-mocks.json b/test/data/fetch-mocks.json index 886f87697..9299b4602 100644 --- a/test/data/fetch-mocks.json +++ b/test/data/fetch-mocks.json @@ -8,9 +8,21 @@ "mockMetaMetricsResponse": true }, "swaps": { - "featureFlag": { - "status": { - "active": true + "featureFlags": { + "bsc": { + "mobile_active": false, + "extension_active": true, + "fallback_to_v1": true + }, + "ethereum": { + "mobile_active": false, + "extension_active": true, + "fallback_to_v1": true + }, + "polygon": { + "mobile_active": false, + "extension_active": true, + "fallback_to_v1": false } } } diff --git a/test/e2e/webdriver/index.js b/test/e2e/webdriver/index.js index b7647f96a..075718631 100644 --- a/test/e2e/webdriver/index.js +++ b/test/e2e/webdriver/index.js @@ -48,9 +48,9 @@ async function setupFetchMocking(driver) { return { json: async () => clone(mockResponses.gasPricesBasic) }; } else if (url.match(/chromeextensionmm/u)) { return { json: async () => clone(mockResponses.metametrics) }; - } else if (url.match(/^https:\/\/(api\.metaswap|.*airswap-dev)/u)) { - if (url.match(/featureFlag$/u)) { - return { json: async () => clone(mockResponses.swaps.featureFlag) }; + } else if (url.match(/^https:\/\/(api2\.metaswap\.codefi\.network)/u)) { + if (url.match(/featureFlags$/u)) { + return { json: async () => clone(mockResponses.swaps.featureFlags) }; } } return window.origFetch(...args); diff --git a/test/jest/constants.js b/test/jest/constants.js index 2ed360574..da5c54bdf 100644 --- a/test/jest/constants.js +++ b/test/jest/constants.js @@ -1 +1,2 @@ export const METASWAP_BASE_URL = 'https://api.metaswap.codefi.network'; +export const METASWAP_API_V2_BASE_URL = 'https://api2.metaswap.codefi.network'; diff --git a/test/jest/mock-store.js b/test/jest/mock-store.js index 35e52682f..325c9f2b5 100644 --- a/test/jest/mock-store.js +++ b/test/jest/mock-store.js @@ -220,6 +220,7 @@ export const createSwapsMockStore = () => { topAggId: null, routeState: '', swapsFeatureIsLive: false, + useNewSwapsApi: false, }, }, appState: { diff --git a/test/jest/mocks.js b/test/jest/mocks.js index c57b2068c..4d7b4cd68 100644 --- a/test/jest/mocks.js +++ b/test/jest/mocks.js @@ -59,3 +59,23 @@ export const TOKENS_GET_RESPONSE = [ address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF', }, ]; + +export const createFeatureFlagsResponse = () => { + return { + bsc: { + mobile_active: false, + extension_active: true, + fallback_to_v1: true, + }, + ethereum: { + mobile_active: false, + extension_active: true, + fallback_to_v1: true, + }, + polygon: { + mobile_active: false, + extension_active: true, + fallback_to_v1: false, + }, + }; +}; diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index 3fbd63676..c047d4134 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -34,9 +34,10 @@ import { SWAPS_MAINTENANCE_ROUTE, } from '../../helpers/constants/routes'; import { - fetchSwapsFeatureLiveness, + fetchSwapsFeatureFlags, fetchSwapsGasPrices, isContractAddressValid, + getSwapsLivenessForNetwork, } from '../../pages/swaps/swaps.util'; import { calcGasTotal } from '../../pages/send/send.utils'; import { @@ -223,9 +224,12 @@ export function shouldShowCustomPriceTooLowWarning(state) { const getSwapsState = (state) => state.metamask.swapsState; -export const getSwapsFeatureLiveness = (state) => +export const getSwapsFeatureIsLive = (state) => state.metamask.swapsState.swapsFeatureIsLive; +export const getUseNewSwapsApi = (state) => + state.metamask.swapsState.useNewSwapsApi; + export const getSwapsQuoteRefreshTime = (state) => state.metamask.swapsState.swapsQuoteRefreshTime; @@ -373,16 +377,21 @@ export const fetchAndSetSwapsGasPriceInfo = () => { export const fetchSwapsLiveness = () => { return async (dispatch, getState) => { - let swapsFeatureIsLive = false; + let swapsLivenessForNetwork = { + swapsFeatureIsLive: false, + useNewSwapsApi: false, + }; try { - swapsFeatureIsLive = await fetchSwapsFeatureLiveness( + const swapsFeatureFlags = await fetchSwapsFeatureFlags(); + swapsLivenessForNetwork = getSwapsLivenessForNetwork( + swapsFeatureFlags, getCurrentChainId(getState()), ); } catch (error) { log.error('Failed to fetch Swaps liveness, defaulting to false.', error); } - await dispatch(setSwapsLiveness(swapsFeatureIsLive)); - return swapsFeatureIsLive; + await dispatch(setSwapsLiveness(swapsLivenessForNetwork)); + return swapsLivenessForNetwork; }; }; @@ -395,15 +404,22 @@ export const fetchQuotesAndSetQuoteState = ( return async (dispatch, getState) => { const state = getState(); const chainId = getCurrentChainId(state); - let swapsFeatureIsLive = false; + let swapsLivenessForNetwork = { + swapsFeatureIsLive: false, + useNewSwapsApi: false, + }; try { - swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId); + const swapsFeatureFlags = await fetchSwapsFeatureFlags(); + swapsLivenessForNetwork = getSwapsLivenessForNetwork( + swapsFeatureFlags, + chainId, + ); } catch (error) { log.error('Failed to fetch Swaps liveness, defaulting to false.', error); } - await dispatch(setSwapsLiveness(swapsFeatureIsLive)); + await dispatch(setSwapsLiveness(swapsLivenessForNetwork)); - if (!swapsFeatureIsLive) { + if (!swapsLivenessForNetwork.swapsFeatureIsLive) { await history.push(SWAPS_MAINTENANCE_ROUTE); return; } @@ -600,15 +616,22 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => { const state = getState(); const chainId = getCurrentChainId(state); const hardwareWalletUsed = isHardwareWallet(state); - let swapsFeatureIsLive = false; + let swapsLivenessForNetwork = { + swapsFeatureIsLive: false, + useNewSwapsApi: false, + }; try { - swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId); + const swapsFeatureFlags = await fetchSwapsFeatureFlags(); + swapsLivenessForNetwork = getSwapsLivenessForNetwork( + swapsFeatureFlags, + chainId, + ); } catch (error) { log.error('Failed to fetch Swaps liveness, defaulting to false.', error); } - await dispatch(setSwapsLiveness(swapsFeatureIsLive)); + await dispatch(setSwapsLiveness(swapsLivenessForNetwork)); - if (!swapsFeatureIsLive) { + if (!swapsLivenessForNetwork.swapsFeatureIsLive) { await history.push(SWAPS_MAINTENANCE_ROUTE); return; } @@ -808,12 +831,13 @@ export function fetchMetaSwapsGasPriceEstimates() { return async (dispatch, getState) => { const state = getState(); const chainId = getCurrentChainId(state); + const useNewSwapsApi = getUseNewSwapsApi(state); dispatch(swapGasPriceEstimatesFetchStarted()); let priceEstimates; try { - priceEstimates = await fetchSwapsGasPrices(chainId); + priceEstimates = await fetchSwapsGasPrices(chainId, useNewSwapsApi); } catch (e) { log.warn('Fetching swaps gas prices failed:', e); diff --git a/ui/ducks/swaps/swaps.test.js b/ui/ducks/swaps/swaps.test.js index a94869267..c3a9c9e3e 100644 --- a/ui/ducks/swaps/swaps.test.js +++ b/ui/ducks/swaps/swaps.test.js @@ -1,5 +1,6 @@ import nock from 'nock'; +import { MOCKS } from '../../../test/jest'; import { setSwapsLiveness } from '../../store/actions'; import { setStorageItem } from '../../helpers/utils/storage-helpers'; import * as swaps from './swaps'; @@ -25,7 +26,7 @@ describe('Ducks - Swaps', () => { describe('fetchSwapsLiveness', () => { const cleanFeatureFlagApiCache = () => { setStorageItem( - 'cachedFetch:https://api.metaswap.codefi.network/featureFlag', + 'cachedFetch:https://api2.metaswap.codefi.network/featureFlags', null, ); }; @@ -34,12 +35,12 @@ describe('Ducks - Swaps', () => { cleanFeatureFlagApiCache(); }); - const mockFeatureFlagApiResponse = ({ - active = false, + const mockFeatureFlagsApiResponse = ({ + featureFlagsResponse, replyWithError = false, } = {}) => { - const apiNock = nock('https://api.metaswap.codefi.network').get( - '/featureFlag', + const apiNock = nock('https://api2.metaswap.codefi.network').get( + '/featureFlags', ); if (replyWithError) { return apiNock.replyWithError({ @@ -47,9 +48,7 @@ describe('Ducks - Swaps', () => { code: 'serverSideError', }); } - return apiNock.reply(200, { - active, - }); + return apiNock.reply(200, featureFlagsResponse); }; const createGetState = () => { @@ -58,61 +57,111 @@ describe('Ducks - Swaps', () => { }); }; - it('returns true if the Swaps feature is enabled', async () => { + it('checks that Swaps for ETH are enabled and can use new API', async () => { const mockDispatch = jest.fn(); - const featureFlagApiNock = mockFeatureFlagApiResponse({ active: true }); - const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()( + const expectedSwapsLiveness = { + swapsFeatureIsLive: true, + useNewSwapsApi: true, + }; + const featureFlagsResponse = MOCKS.createFeatureFlagsResponse(); + const featureFlagApiNock = mockFeatureFlagsApiResponse({ + featureFlagsResponse, + }); + const swapsLiveness = await swaps.fetchSwapsLiveness()( mockDispatch, createGetState(), ); expect(featureFlagApiNock.isDone()).toBe(true); expect(mockDispatch).toHaveBeenCalledTimes(1); - expect(setSwapsLiveness).toHaveBeenCalledWith(true); - expect(isSwapsFeatureEnabled).toBe(true); + expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness); + expect(swapsLiveness).toMatchObject(expectedSwapsLiveness); }); - it('returns false if the Swaps feature is disabled', async () => { + it('checks that Swaps for ETH are disabled for API v2 and enabled for API v1', async () => { const mockDispatch = jest.fn(); - const featureFlagApiNock = mockFeatureFlagApiResponse({ active: false }); - const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()( + const expectedSwapsLiveness = { + swapsFeatureIsLive: true, + useNewSwapsApi: false, + }; + const featureFlagsResponse = MOCKS.createFeatureFlagsResponse(); + featureFlagsResponse.ethereum.extension_active = false; + const featureFlagApiNock = mockFeatureFlagsApiResponse({ + featureFlagsResponse, + }); + const swapsLiveness = await swaps.fetchSwapsLiveness()( mockDispatch, createGetState(), ); expect(featureFlagApiNock.isDone()).toBe(true); expect(mockDispatch).toHaveBeenCalledTimes(1); - expect(setSwapsLiveness).toHaveBeenCalledWith(false); - expect(isSwapsFeatureEnabled).toBe(false); + expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness); + expect(swapsLiveness).toMatchObject(expectedSwapsLiveness); }); - it('returns false if the /featureFlag API call throws an error', async () => { + it('checks that Swaps for ETH are disabled for API v1 and v2', async () => { const mockDispatch = jest.fn(); - const featureFlagApiNock = mockFeatureFlagApiResponse({ + const expectedSwapsLiveness = { + swapsFeatureIsLive: false, + useNewSwapsApi: false, + }; + const featureFlagsResponse = MOCKS.createFeatureFlagsResponse(); + featureFlagsResponse.ethereum.extension_active = false; + featureFlagsResponse.ethereum.fallback_to_v1 = false; + const featureFlagApiNock = mockFeatureFlagsApiResponse({ + featureFlagsResponse, + }); + const swapsLiveness = await swaps.fetchSwapsLiveness()( + mockDispatch, + createGetState(), + ); + expect(featureFlagApiNock.isDone()).toBe(true); + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness); + expect(swapsLiveness).toMatchObject(expectedSwapsLiveness); + }); + + it('checks that Swaps for ETH are disabled if the /featureFlags API call throws an error', async () => { + const mockDispatch = jest.fn(); + const expectedSwapsLiveness = { + swapsFeatureIsLive: false, + useNewSwapsApi: false, + }; + const featureFlagApiNock = mockFeatureFlagsApiResponse({ replyWithError: true, }); - const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()( + const swapsLiveness = await swaps.fetchSwapsLiveness()( mockDispatch, createGetState(), ); expect(featureFlagApiNock.isDone()).toBe(true); expect(mockDispatch).toHaveBeenCalledTimes(1); - expect(setSwapsLiveness).toHaveBeenCalledWith(false); - expect(isSwapsFeatureEnabled).toBe(false); + expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness); + expect(swapsLiveness).toMatchObject(expectedSwapsLiveness); }); - it('only calls the API once and returns true from cache for the second call', async () => { + it('only calls the API once and returns response from cache for the second call', async () => { const mockDispatch = jest.fn(); - const featureFlagApiNock = mockFeatureFlagApiResponse({ active: true }); + const expectedSwapsLiveness = { + swapsFeatureIsLive: true, + useNewSwapsApi: true, + }; + const featureFlagsResponse = MOCKS.createFeatureFlagsResponse(); + const featureFlagApiNock = mockFeatureFlagsApiResponse({ + featureFlagsResponse, + }); await swaps.fetchSwapsLiveness()(mockDispatch, createGetState()); expect(featureFlagApiNock.isDone()).toBe(true); - const featureFlagApiNock2 = mockFeatureFlagApiResponse({ active: true }); - const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()( + const featureFlagApiNock2 = mockFeatureFlagsApiResponse({ + featureFlagsResponse, + }); + const swapsLiveness = await swaps.fetchSwapsLiveness()( mockDispatch, createGetState(), ); expect(featureFlagApiNock2.isDone()).toBe(false); // Second API call wasn't made, cache was used instead. expect(mockDispatch).toHaveBeenCalledTimes(2); - expect(setSwapsLiveness).toHaveBeenCalledWith(true); - expect(isSwapsFeatureEnabled).toBe(true); + expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness); + expect(swapsLiveness).toMatchObject(expectedSwapsLiveness); }); }); }); diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js index 2b989ae03..4d179abf9 100644 --- a/ui/pages/home/home.container.js +++ b/ui/pages/home/home.container.js @@ -31,7 +31,7 @@ import { } from '../../store/actions'; import { setThreeBoxLastUpdated, hideWhatsNewPopup } from '../../ducks/app/app'; import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask'; -import { getSwapsFeatureLiveness } from '../../ducks/swaps/swaps'; +import { getSwapsFeatureIsLive } from '../../ducks/swaps/swaps'; import { getEnvironmentType } from '../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_NOTIFICATION, @@ -60,7 +60,7 @@ const mapStateToProps = (state) => { const accountBalance = getCurrentEthBalance(state); const { forgottenPassword, threeBoxLastUpdated } = appState; const totalUnapprovedCount = getTotalUnapprovedCount(state); - const swapsEnabled = getSwapsFeatureLiveness(state); + const swapsEnabled = getSwapsFeatureIsLive(state); const pendingConfirmations = getUnapprovedTemplatedConfirmations(state); const envType = getEnvironmentType(); diff --git a/ui/pages/swaps/build-quote/build-quote.js b/ui/pages/swaps/build-quote/build-quote.js index 1d5220258..4985a8f6d 100644 --- a/ui/pages/swaps/build-quote/build-quote.js +++ b/ui/pages/swaps/build-quote/build-quote.js @@ -141,7 +141,9 @@ export default function BuildQuote({ const toTokenIsNotDefault = selectedToToken?.address && !isSwapsDefaultTokenAddress(selectedToToken?.address, chainId); - const occurances = Number(selectedToToken?.occurances || 0); + const occurrences = Number( + selectedToToken?.occurances || selectedToToken?.occurrences || 0, + ); const { address: fromTokenAddress, symbol: fromTokenSymbol, @@ -354,11 +356,11 @@ export default function BuildQuote({ let tokenVerificationDescription = ''; if (blockExplorerTokenLink) { - if (occurances === 1) { + if (occurrences === 1) { tokenVerificationDescription = t('verifyThisTokenOn', [ , ]); - } else if (occurances === 0) { + } else if (occurrences === 0) { tokenVerificationDescription = t('verifyThisUnconfirmedTokenOn', [ , ]); @@ -470,13 +472,13 @@ export default function BuildQuote({ /> {toTokenIsNotDefault && - (occurances < 2 ? ( + (occurrences < 2 ? (
- {occurances === 1 + {occurrences === 1 ? t('swapTokenVerificationOnlyOneSource') : t('swapTokenVerificationAddedManually')}
@@ -503,7 +505,7 @@ export default function BuildQuote({ className="build-quote__bold" key="token-verification-bold-text" > - {t('swapTokenVerificationSources', [occurances])} + {t('swapTokenVerificationSources', [occurrences])} {blockExplorerTokenLink && ( <> @@ -563,7 +565,7 @@ export default function BuildQuote({ !selectedToToken?.address || Number(maxSlippage) < 0 || Number(maxSlippage) > MAX_ALLOWED_SLIPPAGE || - (toTokenIsNotDefault && occurances < 2 && !verificationClicked) + (toTokenIsNotDefault && occurrences < 2 && !verificationClicked) } hideCancel showTermsOfService diff --git a/ui/pages/swaps/fee-card/fee-card.js b/ui/pages/swaps/fee-card/fee-card.js index 5e7269f18..e29da8e1e 100644 --- a/ui/pages/swaps/fee-card/fee-card.js +++ b/ui/pages/swaps/fee-card/fee-card.js @@ -6,6 +6,7 @@ import { MAINNET_CHAIN_ID, BSC_CHAIN_ID, LOCALHOST_CHAIN_ID, + POLYGON_CHAIN_ID, } from '../../../../shared/constants/network'; export default function FeeCard({ @@ -38,6 +39,8 @@ export default function FeeCard({ return t('networkNameEthereum'); case BSC_CHAIN_ID: return t('networkNameBSC'); + case POLYGON_CHAIN_ID: + return t('networkNamePolygon'); case LOCALHOST_CHAIN_ID: return t('networkNameTestnet'); default: diff --git a/ui/pages/swaps/index.js b/ui/pages/swaps/index.js index 05a0d6d42..d2a30b904 100644 --- a/ui/pages/swaps/index.js +++ b/ui/pages/swaps/index.js @@ -29,10 +29,11 @@ import { getAggregatorMetadata, getBackgroundSwapRouteState, getSwapsErrorKey, - getSwapsFeatureLiveness, + getSwapsFeatureIsLive, prepareToLeaveSwaps, fetchAndSetSwapsGasPriceInfo, fetchSwapsLiveness, + getUseNewSwapsApi, } from '../../ducks/swaps/swaps'; import { AWAITING_SIGNATURES_ROUTE, @@ -103,9 +104,10 @@ export default function Swap() { const aggregatorMetadata = useSelector(getAggregatorMetadata); const fetchingQuotes = useSelector(getFetchingQuotes); let swapsErrorKey = useSelector(getSwapsErrorKey); - const swapsEnabled = useSelector(getSwapsFeatureLiveness); + const swapsEnabled = useSelector(getSwapsFeatureIsLive); const chainId = useSelector(getCurrentChainId); const isSwapsChain = useSelector(getIsSwapsChain); + const useNewSwapsApi = useSelector(getUseNewSwapsApi); const { balance: ethBalance, @@ -165,27 +167,28 @@ export default function Swap() { }; }, []); + // eslint-disable-next-line useEffect(() => { - fetchTokens(chainId) - .then((tokens) => { - dispatch(setSwapsTokens(tokens)); - }) - .catch((error) => console.error(error)); - - fetchTopAssets(chainId).then((topAssets) => { - dispatch(setTopAssets(topAssets)); - }); - - fetchAggregatorMetadata(chainId).then((newAggregatorMetadata) => { - dispatch(setAggregatorMetadata(newAggregatorMetadata)); - }); - - dispatch(fetchAndSetSwapsGasPriceInfo(chainId)); - - return () => { - dispatch(prepareToLeaveSwaps()); - }; - }, [dispatch, chainId]); + if (isFeatureFlagLoaded) { + fetchTokens(chainId, useNewSwapsApi) + .then((tokens) => { + dispatch(setSwapsTokens(tokens)); + }) + .catch((error) => console.error(error)); + fetchTopAssets(chainId, useNewSwapsApi).then((topAssets) => { + dispatch(setTopAssets(topAssets)); + }); + fetchAggregatorMetadata(chainId, useNewSwapsApi).then( + (newAggregatorMetadata) => { + dispatch(setAggregatorMetadata(newAggregatorMetadata)); + }, + ); + dispatch(fetchAndSetSwapsGasPriceInfo(chainId)); + return () => { + dispatch(prepareToLeaveSwaps()); + }; + } + }, [dispatch, chainId, isFeatureFlagLoaded, useNewSwapsApi]); const hardwareWalletUsed = useSelector(isHardwareWallet); const hardwareWalletType = useSelector(getHardwareWalletType); diff --git a/ui/pages/swaps/index.test.js b/ui/pages/swaps/index.test.js index a3162ae2a..ed2323e40 100644 --- a/ui/pages/swaps/index.test.js +++ b/ui/pages/swaps/index.test.js @@ -24,7 +24,7 @@ setBackgroundConnection({ }); describe('Swap', () => { - let tokensNock; + let featureFlagsNock; beforeEach(() => { nock(CONSTANTS.METASWAP_BASE_URL) @@ -43,9 +43,13 @@ describe('Swap', () => { .get('/gasPrices') .reply(200, MOCKS.GAS_PRICES_GET_RESPONSE); - tokensNock = nock(CONSTANTS.METASWAP_BASE_URL) + nock(CONSTANTS.METASWAP_BASE_URL) .get('/tokens') .reply(200, MOCKS.TOKENS_GET_RESPONSE); + + featureFlagsNock = nock(CONSTANTS.METASWAP_API_V2_BASE_URL) + .get('/featureFlags') + .reply(200, MOCKS.createFeatureFlagsResponse()); }); afterAll(() => { @@ -55,7 +59,7 @@ describe('Swap', () => { it('renders the component with initial props', async () => { const store = configureMockStore(middleware)(createSwapsMockStore()); const { container, getByText } = renderWithProvider(, store); - await waitFor(() => expect(tokensNock.isDone()).toBe(true)); + await waitFor(() => expect(featureFlagsNock.isDone()).toBe(true)); expect(getByText('Swap')).toBeInTheDocument(); expect(getByText('Cancel')).toBeInTheDocument(); expect(container).toMatchSnapshot(); diff --git a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js index 26fbc0124..ed1b9e257 100644 --- a/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js +++ b/ui/pages/swaps/searchable-item-list/list-item-search/list-item-search.component.js @@ -9,6 +9,7 @@ import { usePrevious } from '../../../../hooks/usePrevious'; import { isValidHexAddress } from '../../../../../shared/modules/hexstring-utils'; import { fetchToken } from '../../swaps.util'; import { getCurrentChainId } from '../../../../selectors/selectors'; +import { getUseNewSwapsApi } from '../../../../ducks/swaps/swaps'; const renderAdornment = () => ( @@ -28,6 +29,7 @@ export default function ListItemSearch({ const fuseRef = useRef(); const [searchQuery, setSearchQuery] = useState(''); const chainId = useSelector(getCurrentChainId); + const useNewSwapsApi = useSelector(getUseNewSwapsApi); /** * Search a custom token for import based on a contract address. @@ -36,7 +38,7 @@ export default function ListItemSearch({ const handleSearchTokenForImport = async (contractAddress) => { setSearchQuery(contractAddress); try { - const token = await fetchToken(contractAddress, chainId); + const token = await fetchToken(contractAddress, chainId, useNewSwapsApi); if (token) { token.primaryLabel = token.symbol; token.secondaryLabel = token.name; diff --git a/ui/pages/swaps/swaps.util.js b/ui/pages/swaps/swaps.util.js index 11907c8c1..cb417ab24 100644 --- a/ui/pages/swaps/swaps.util.js +++ b/ui/pages/swaps/swaps.util.js @@ -6,6 +6,9 @@ import { METASWAP_CHAINID_API_HOST_MAP, SWAPS_CHAINID_CONTRACT_ADDRESS_MAP, ETH_WETH_CONTRACT_ADDRESS, + ETHEREUM, + POLYGON, + BSC, } from '../../../shared/constants/swaps'; import { isSwapsDefaultTokenAddress, @@ -15,6 +18,9 @@ import { ETH_SYMBOL, WETH_SYMBOL, MAINNET_CHAIN_ID, + BSC_CHAIN_ID, + POLYGON_CHAIN_ID, + LOCALHOST_CHAIN_ID, } from '../../../shared/constants/network'; import { SECOND } from '../../../shared/constants/time'; import { @@ -42,24 +48,50 @@ const TOKEN_TRANSFER_LOG_TOPIC_HASH = const CACHE_REFRESH_FIVE_MINUTES = 300000; -const getBaseApi = function (type, chainId = MAINNET_CHAIN_ID) { +const SWAPS_API_V2_BASE_URL = 'https://api2.metaswap.codefi.network'; +const GAS_API_BASE_URL = 'https://gas-api.metaswap.codefi.network'; + +/** + * @param {string} type Type of an API call, e.g. "tokens" + * @param {string} chainId + * @returns string + */ +const getBaseUrlForNewSwapsApi = (type, chainId) => { + const noNetworkSpecificTypes = ['refreshTime']; // These types don't need network info in the URL. + if (noNetworkSpecificTypes.includes(type)) { + return SWAPS_API_V2_BASE_URL; + } + const chainIdDecimal = chainId && parseInt(chainId, 16); + const gasApiTypes = ['gasPrices']; + if (gasApiTypes.includes(type)) { + return `${GAS_API_BASE_URL}/networks/${chainIdDecimal}`; // Gas calculations are in its own repo. + } + return `${SWAPS_API_V2_BASE_URL}/networks/${chainIdDecimal}`; +}; + +const getBaseApi = function ( + type, + chainId = MAINNET_CHAIN_ID, + useNewSwapsApi = false, +) { + const baseUrl = useNewSwapsApi + ? getBaseUrlForNewSwapsApi(type, chainId) + : METASWAP_CHAINID_API_HOST_MAP[chainId]; switch (type) { case 'trade': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/trades?`; + return `${baseUrl}/trades?`; case 'tokens': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/tokens`; + return `${baseUrl}/tokens`; case 'token': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/token`; + return `${baseUrl}/token`; case 'topAssets': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/topAssets`; - case 'featureFlag': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/featureFlag`; + return `${baseUrl}/topAssets`; case 'aggregatorMetadata': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/aggregatorMetadata`; + return `${baseUrl}/aggregatorMetadata`; case 'gasPrices': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/gasPrices`; + return `${baseUrl}/gasPrices`; case 'refreshTime': - return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/quoteRefreshRate`; + return `${baseUrl}/quoteRefreshRate`; default: throw new Error('getBaseApi requires an api call type'); } @@ -233,7 +265,7 @@ export async function fetchTradesInfo( fromAddress, exchangeList, }, - { chainId }, + { chainId, useNewSwapsApi }, ) { const urlParams = { destinationToken, @@ -249,7 +281,11 @@ export async function fetchTradesInfo( } const queryString = new URLSearchParams(urlParams).toString(); - const tradeURL = `${getBaseApi('trade', chainId)}${queryString}`; + const tradeURL = `${getBaseApi( + 'trade', + chainId, + useNewSwapsApi, + )}${queryString}`; const tradesResponse = await fetchWithCache( tradeURL, { method: 'GET' }, @@ -293,8 +329,8 @@ export async function fetchTradesInfo( return newQuotes; } -export async function fetchToken(contractAddress, chainId) { - const tokenUrl = getBaseApi('token', chainId); +export async function fetchToken(contractAddress, chainId, useNewSwapsApi) { + const tokenUrl = getBaseApi('token', chainId, useNewSwapsApi); const token = await fetchWithCache( `${tokenUrl}?address=${contractAddress}`, { method: 'GET' }, @@ -303,8 +339,8 @@ export async function fetchToken(contractAddress, chainId) { return token; } -export async function fetchTokens(chainId) { - const tokensUrl = getBaseApi('tokens', chainId); +export async function fetchTokens(chainId, useNewSwapsApi) { + const tokensUrl = getBaseApi('tokens', chainId, useNewSwapsApi); const tokens = await fetchWithCache( tokensUrl, { method: 'GET' }, @@ -325,8 +361,12 @@ export async function fetchTokens(chainId) { return filteredTokens; } -export async function fetchAggregatorMetadata(chainId) { - const aggregatorMetadataUrl = getBaseApi('aggregatorMetadata', chainId); +export async function fetchAggregatorMetadata(chainId, useNewSwapsApi) { + const aggregatorMetadataUrl = getBaseApi( + 'aggregatorMetadata', + chainId, + useNewSwapsApi, + ); const aggregators = await fetchWithCache( aggregatorMetadataUrl, { method: 'GET' }, @@ -347,8 +387,8 @@ export async function fetchAggregatorMetadata(chainId) { return filteredAggregators; } -export async function fetchTopAssets(chainId) { - const topAssetsUrl = getBaseApi('topAssets', chainId); +export async function fetchTopAssets(chainId, useNewSwapsApi) { + const topAssetsUrl = getBaseApi('topAssets', chainId, useNewSwapsApi); const response = await fetchWithCache( topAssetsUrl, { method: 'GET' }, @@ -363,18 +403,18 @@ export async function fetchTopAssets(chainId) { return topAssetsMap; } -export async function fetchSwapsFeatureLiveness(chainId) { - const status = await fetchWithCache( - getBaseApi('featureFlag', chainId), +export async function fetchSwapsFeatureFlags() { + const response = await fetchWithCache( + `${SWAPS_API_V2_BASE_URL}/featureFlags`, { method: 'GET' }, { cacheRefreshTime: 600000 }, ); - return status?.active; + return response; } -export async function fetchSwapsQuoteRefreshTime(chainId) { +export async function fetchSwapsQuoteRefreshTime(chainId, useNewSwapsApi) { const response = await fetchWithCache( - getBaseApi('refreshTime', chainId), + getBaseApi('refreshTime', chainId, useNewSwapsApi), { method: 'GET' }, { cacheRefreshTime: 600000 }, ); @@ -409,8 +449,8 @@ export async function fetchTokenBalance(address, userAddress) { return usersToken; } -export async function fetchSwapsGasPrices(chainId) { - const gasPricesUrl = getBaseApi('gasPrices', chainId); +export async function fetchSwapsGasPrices(chainId, useNewSwapsApi) { + const gasPricesUrl = getBaseApi('gasPrices', chainId, useNewSwapsApi); const response = await fetchWithCache( gasPricesUrl, { method: 'GET' }, @@ -730,3 +770,56 @@ export const isContractAddressValid = ( contractAddressForChainId.toUpperCase() === contractAddress.toUpperCase() ); }; + +/** + * @param {string} chainId + * @returns string e.g. ethereum, bsc or polygon + */ +export const getNetworkNameByChainId = (chainId) => { + switch (chainId) { + case MAINNET_CHAIN_ID: + return ETHEREUM; + case BSC_CHAIN_ID: + return BSC; + case POLYGON_CHAIN_ID: + return POLYGON; + default: + return ''; + } +}; + +/** + * It returns info about if Swaps are enabled and if we should use our new APIs for it. + * @param {object} swapsFeatureFlags + * @param {string} chainId + * @returns object with 2 items: "swapsFeatureIsLive" and "useNewSwapsApi" + */ +export const getSwapsLivenessForNetwork = (swapsFeatureFlags = {}, chainId) => { + const networkName = getNetworkNameByChainId(chainId); + // Use old APIs for testnet. + if (chainId === LOCALHOST_CHAIN_ID) { + return { + swapsFeatureIsLive: true, + useNewSwapsApi: false, + }; + } + // If a network name is not found in the list of feature flags, disable Swaps. + if (!swapsFeatureFlags[networkName]) { + return { + swapsFeatureIsLive: false, + useNewSwapsApi: false, + }; + } + const isNetworkEnabledForNewApi = + swapsFeatureFlags[networkName].extension_active; + if (isNetworkEnabledForNewApi) { + return { + swapsFeatureIsLive: true, + useNewSwapsApi: true, + }; + } + return { + swapsFeatureIsLive: swapsFeatureFlags[networkName].fallback_to_v1, + useNewSwapsApi: false, + }; +}; diff --git a/ui/pages/swaps/swaps.util.test.js b/ui/pages/swaps/swaps.util.test.js index 936864f0f..81220cbd4 100644 --- a/ui/pages/swaps/swaps.util.test.js +++ b/ui/pages/swaps/swaps.util.test.js @@ -1,14 +1,20 @@ import nock from 'nock'; +import { MOCKS } from '../../../test/jest'; import { ETH_SYMBOL, WETH_SYMBOL, MAINNET_CHAIN_ID, BSC_CHAIN_ID, + POLYGON_CHAIN_ID, LOCALHOST_CHAIN_ID, + RINKEBY_CHAIN_ID, } from '../../../shared/constants/network'; import { SWAPS_CHAINID_CONTRACT_ADDRESS_MAP, ETH_WETH_CONTRACT_ADDRESS, + ETHEREUM, + POLYGON, + BSC, } from '../../../shared/constants/swaps'; import { TOKENS, @@ -17,13 +23,14 @@ import { AGGREGATOR_METADATA, TOP_ASSETS, } from './swaps-util-test-constants'; - import { fetchTradesInfo, fetchTokens, fetchAggregatorMetadata, fetchTopAssets, isContractAddressValid, + getNetworkNameByChainId, + getSwapsLivenessForNetwork, } from './swaps.util'; jest.mock('../../helpers/utils/storage-helpers.js', () => ({ @@ -372,4 +379,75 @@ describe('Swaps Util', () => { ).toBe(false); }); }); + + describe('getNetworkNameByChainId', () => { + it('returns "ethereum" for mainnet chain ID', () => { + expect(getNetworkNameByChainId(MAINNET_CHAIN_ID)).toBe(ETHEREUM); + }); + + it('returns "bsc" for mainnet chain ID', () => { + expect(getNetworkNameByChainId(BSC_CHAIN_ID)).toBe(BSC); + }); + + it('returns "polygon" for mainnet chain ID', () => { + expect(getNetworkNameByChainId(POLYGON_CHAIN_ID)).toBe(POLYGON); + }); + + it('returns an empty string for an unsupported network', () => { + expect(getNetworkNameByChainId(RINKEBY_CHAIN_ID)).toBe(''); + }); + }); + + describe('getSwapsLivenessForNetwork', () => { + it('returns info that Swaps are enabled and cannot use API v2 for localhost chain ID', () => { + const expectedSwapsLiveness = { + swapsFeatureIsLive: true, + useNewSwapsApi: false, + }; + expect( + getSwapsLivenessForNetwork( + MOCKS.createFeatureFlagsResponse(), + LOCALHOST_CHAIN_ID, + ), + ).toMatchObject(expectedSwapsLiveness); + }); + + it('returns info that Swaps are disabled and cannot use API v2 if network name is not found', () => { + const expectedSwapsLiveness = { + swapsFeatureIsLive: false, + useNewSwapsApi: false, + }; + expect( + getSwapsLivenessForNetwork( + MOCKS.createFeatureFlagsResponse(), + RINKEBY_CHAIN_ID, + ), + ).toMatchObject(expectedSwapsLiveness); + }); + + it('returns info that Swaps are enabled and can use API v2 for mainnet chain ID', () => { + const expectedSwapsLiveness = { + swapsFeatureIsLive: true, + useNewSwapsApi: true, + }; + expect( + getSwapsLivenessForNetwork( + MOCKS.createFeatureFlagsResponse(), + MAINNET_CHAIN_ID, + ), + ).toMatchObject(expectedSwapsLiveness); + }); + + it('returns info that Swaps are enabled but can only use API v1 for mainnet chain ID', () => { + const expectedSwapsLiveness = { + swapsFeatureIsLive: true, + useNewSwapsApi: false, + }; + const swapsFeatureFlags = MOCKS.createFeatureFlagsResponse(); + swapsFeatureFlags[ETHEREUM].extension_active = false; + expect( + getSwapsLivenessForNetwork(swapsFeatureFlags, MAINNET_CHAIN_ID), + ).toMatchObject(expectedSwapsLiveness); + }); + }); }); diff --git a/ui/store/actions.js b/ui/store/actions.js index 7c5be2fe9..d9f629952 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -2121,9 +2121,9 @@ export function setPendingTokens(pendingTokens) { // Swaps -export function setSwapsLiveness(swapsFeatureIsLive) { +export function setSwapsLiveness(swapsLiveness) { return async (dispatch) => { - await promisifiedBackground.setSwapsLiveness(swapsFeatureIsLive); + await promisifiedBackground.setSwapsLiveness(swapsLiveness); await forceUpdateMetamaskState(dispatch); }; }