mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
Swaps: Add conditional routing to new APIs based on a feature flag (#11470)
This commit is contained in:
parent
f51a8451b8
commit
d438439661
@ -1266,6 +1266,9 @@
|
|||||||
"networkNameEthereum": {
|
"networkNameEthereum": {
|
||||||
"message": "Ethereum"
|
"message": "Ethereum"
|
||||||
},
|
},
|
||||||
|
"networkNamePolygon": {
|
||||||
|
"message": "Polygon"
|
||||||
|
},
|
||||||
"networkNameTestnet": {
|
"networkNameTestnet": {
|
||||||
"message": "Testnet"
|
"message": "Testnet"
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,6 @@ import { isSwapsDefaultTokenAddress } from '../../../shared/modules/swaps.utils'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
fetchTradesInfo as defaultFetchTradesInfo,
|
fetchTradesInfo as defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
|
|
||||||
fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime,
|
fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime,
|
||||||
} from '../../../ui/pages/swaps/swaps.util';
|
} from '../../../ui/pages/swaps/swaps.util';
|
||||||
import { MINUTE, SECOND } from '../../../shared/constants/time';
|
import { MINUTE, SECOND } from '../../../shared/constants/time';
|
||||||
@ -73,6 +72,7 @@ const initialState = {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: true,
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: false,
|
||||||
swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
|
swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -85,7 +85,6 @@ export default class SwapsController {
|
|||||||
getProviderConfig,
|
getProviderConfig,
|
||||||
tokenRatesStore,
|
tokenRatesStore,
|
||||||
fetchTradesInfo = defaultFetchTradesInfo,
|
fetchTradesInfo = defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness = defaultFetchSwapsFeatureLiveness,
|
|
||||||
fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime,
|
fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime,
|
||||||
getCurrentChainId,
|
getCurrentChainId,
|
||||||
}) {
|
}) {
|
||||||
@ -94,7 +93,6 @@ export default class SwapsController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this._fetchTradesInfo = fetchTradesInfo;
|
this._fetchTradesInfo = fetchTradesInfo;
|
||||||
this._fetchSwapsFeatureLiveness = fetchSwapsFeatureLiveness;
|
|
||||||
this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime;
|
this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime;
|
||||||
this._getCurrentChainId = getCurrentChainId;
|
this._getCurrentChainId = getCurrentChainId;
|
||||||
|
|
||||||
@ -119,15 +117,19 @@ export default class SwapsController {
|
|||||||
// Sets the refresh rate for quote updates from the MetaSwap API
|
// Sets the refresh rate for quote updates from the MetaSwap API
|
||||||
async _setSwapsQuoteRefreshTime() {
|
async _setSwapsQuoteRefreshTime() {
|
||||||
const chainId = this._getCurrentChainId();
|
const chainId = this._getCurrentChainId();
|
||||||
|
const { swapsState } = this.store.getState();
|
||||||
|
|
||||||
// Default to fallback time unless API returns valid response
|
// Default to fallback time unless API returns valid response
|
||||||
let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME;
|
let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME;
|
||||||
try {
|
try {
|
||||||
swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime(chainId);
|
swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime(
|
||||||
|
chainId,
|
||||||
|
swapsState.useNewSwapsApi,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Request for swaps quote refresh time failed: ', e);
|
console.error('Request for swaps quote refresh time failed: ', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { swapsState } = this.store.getState();
|
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: { ...swapsState, swapsQuoteRefreshTime },
|
swapsState: { ...swapsState, swapsQuoteRefreshTime },
|
||||||
});
|
});
|
||||||
@ -162,6 +164,9 @@ export default class SwapsController {
|
|||||||
isPolledRequest,
|
isPolledRequest,
|
||||||
) {
|
) {
|
||||||
const { chainId } = fetchParamsMetaData;
|
const { chainId } = fetchParamsMetaData;
|
||||||
|
const {
|
||||||
|
swapsState: { useNewSwapsApi },
|
||||||
|
} = this.store.getState();
|
||||||
|
|
||||||
if (!fetchParams) {
|
if (!fetchParams) {
|
||||||
return null;
|
return null;
|
||||||
@ -182,7 +187,10 @@ export default class SwapsController {
|
|||||||
this.indexOfNewestCallInFlight = indexOfCurrentCall;
|
this.indexOfNewestCallInFlight = indexOfCurrentCall;
|
||||||
|
|
||||||
let [newQuotes] = await Promise.all([
|
let [newQuotes] = await Promise.all([
|
||||||
this._fetchTradesInfo(fetchParams, fetchParamsMetaData),
|
this._fetchTradesInfo(fetchParams, {
|
||||||
|
...fetchParamsMetaData,
|
||||||
|
useNewSwapsApi,
|
||||||
|
}),
|
||||||
this._setSwapsQuoteRefreshTime(),
|
this._setSwapsQuoteRefreshTime(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -449,22 +457,23 @@ export default class SwapsController {
|
|||||||
this.store.updateState({ swapsState: { ...swapsState, routeState } });
|
this.store.updateState({ swapsState: { ...swapsState, routeState } });
|
||||||
}
|
}
|
||||||
|
|
||||||
setSwapsLiveness(swapsFeatureIsLive) {
|
setSwapsLiveness(swapsLiveness) {
|
||||||
const { swapsState } = this.store.getState();
|
const { swapsState } = this.store.getState();
|
||||||
|
const { swapsFeatureIsLive, useNewSwapsApi } = swapsLiveness;
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: { ...swapsState, swapsFeatureIsLive },
|
swapsState: { ...swapsState, swapsFeatureIsLive, useNewSwapsApi },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
resetPostFetchState() {
|
resetPostFetchState() {
|
||||||
const { swapsState } = this.store.getState();
|
const { swapsState } = this.store.getState();
|
||||||
|
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: {
|
swapsState: {
|
||||||
...initialState.swapsState,
|
...initialState.swapsState,
|
||||||
tokens: swapsState.tokens,
|
tokens: swapsState.tokens,
|
||||||
fetchParams: swapsState.fetchParams,
|
fetchParams: swapsState.fetchParams,
|
||||||
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
||||||
|
useNewSwapsApi: swapsState.useNewSwapsApi,
|
||||||
swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime,
|
swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -473,7 +482,6 @@ export default class SwapsController {
|
|||||||
|
|
||||||
resetSwapsState() {
|
resetSwapsState() {
|
||||||
const { swapsState } = this.store.getState();
|
const { swapsState } = this.store.getState();
|
||||||
|
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: {
|
swapsState: {
|
||||||
...initialState.swapsState,
|
...initialState.swapsState,
|
||||||
|
@ -128,13 +128,13 @@ const EMPTY_INIT_STATE = {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: true,
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: false,
|
||||||
swapsQuoteRefreshTime: 60000,
|
swapsQuoteRefreshTime: 60000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sandbox = sinon.createSandbox();
|
const sandbox = sinon.createSandbox();
|
||||||
const fetchTradesInfoStub = sandbox.stub();
|
const fetchTradesInfoStub = sandbox.stub();
|
||||||
const fetchSwapsFeatureLivenessStub = sandbox.stub();
|
|
||||||
const fetchSwapsQuoteRefreshTimeStub = sandbox.stub();
|
const fetchSwapsQuoteRefreshTimeStub = sandbox.stub();
|
||||||
const getCurrentChainIdStub = sandbox.stub();
|
const getCurrentChainIdStub = sandbox.stub();
|
||||||
getCurrentChainIdStub.returns(MAINNET_CHAIN_ID);
|
getCurrentChainIdStub.returns(MAINNET_CHAIN_ID);
|
||||||
@ -150,7 +150,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub,
|
fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub,
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
@ -201,7 +200,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
@ -226,7 +224,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
@ -251,7 +248,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
@ -658,6 +654,7 @@ 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());
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime());
|
||||||
@ -695,15 +692,15 @@ describe('SwapsController', function () {
|
|||||||
metaMaskFeeInEth: '0.5050505050505050505',
|
metaMaskFeeInEth: '0.5050505050505050505',
|
||||||
ethValueOfTokens: '50',
|
ethValueOfTokens: '50',
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
fetchTradesInfoStub.calledOnceWithExactly(
|
fetchTradesInfoStub.calledOnceWithExactly(MOCK_FETCH_PARAMS, {
|
||||||
MOCK_FETCH_PARAMS,
|
...MOCK_FETCH_METADATA,
|
||||||
MOCK_FETCH_METADATA,
|
useNewSwapsApi: false,
|
||||||
),
|
}),
|
||||||
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());
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime());
|
||||||
@ -878,12 +875,14 @@ describe('SwapsController', function () {
|
|||||||
const tokens = 'test';
|
const tokens = 'test';
|
||||||
const fetchParams = 'test';
|
const fetchParams = 'test';
|
||||||
const swapsFeatureIsLive = false;
|
const swapsFeatureIsLive = false;
|
||||||
|
const useNewSwapsApi = false;
|
||||||
const swapsQuoteRefreshTime = 0;
|
const swapsQuoteRefreshTime = 0;
|
||||||
swapsController.store.updateState({
|
swapsController.store.updateState({
|
||||||
swapsState: {
|
swapsState: {
|
||||||
tokens,
|
tokens,
|
||||||
fetchParams,
|
fetchParams,
|
||||||
swapsFeatureIsLive,
|
swapsFeatureIsLive,
|
||||||
|
useNewSwapsApi,
|
||||||
swapsQuoteRefreshTime,
|
swapsQuoteRefreshTime,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -5,10 +5,10 @@ module.exports = {
|
|||||||
coveragePathIgnorePatterns: ['.stories.js', '.snap'],
|
coveragePathIgnorePatterns: ['.stories.js', '.snap'],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
branches: 45.45,
|
branches: 45.24,
|
||||||
functions: 55.29,
|
functions: 51.94,
|
||||||
lines: 60.22,
|
lines: 58.36,
|
||||||
statements: 60.43,
|
statements: 58.6,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setupFiles: ['./test/setup.js', './test/env.js'],
|
setupFiles: ['./test/setup.js', './test/env.js'],
|
||||||
|
@ -21,6 +21,7 @@ export const LOCALHOST_CHAIN_ID = '0x539';
|
|||||||
export const BSC_CHAIN_ID = '0x38';
|
export const BSC_CHAIN_ID = '0x38';
|
||||||
export const OPTIMISM_CHAIN_ID = '0xa';
|
export const OPTIMISM_CHAIN_ID = '0xa';
|
||||||
export const OPTIMISM_TESTNET_CHAIN_ID = '0x45';
|
export const OPTIMISM_TESTNET_CHAIN_ID = '0x45';
|
||||||
|
export const POLYGON_CHAIN_ID = '0x89';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The largest possible chain ID we can handle.
|
* The largest possible chain ID we can handle.
|
||||||
|
@ -93,3 +93,7 @@ export const SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP = {
|
|||||||
[BSC_CHAIN_ID]: BSC_DEFAULT_BLOCK_EXPLORER_URL,
|
[BSC_CHAIN_ID]: BSC_DEFAULT_BLOCK_EXPLORER_URL,
|
||||||
[MAINNET_CHAIN_ID]: MAINNET_DEFAULT_BLOCK_EXPLORER_URL,
|
[MAINNET_CHAIN_ID]: MAINNET_DEFAULT_BLOCK_EXPLORER_URL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ETHEREUM = 'ethereum';
|
||||||
|
export const POLYGON = 'polygon';
|
||||||
|
export const BSC = 'bsc';
|
||||||
|
@ -8,9 +8,21 @@
|
|||||||
"mockMetaMetricsResponse": true
|
"mockMetaMetricsResponse": true
|
||||||
},
|
},
|
||||||
"swaps": {
|
"swaps": {
|
||||||
"featureFlag": {
|
"featureFlags": {
|
||||||
"status": {
|
"bsc": {
|
||||||
"active": true
|
"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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,9 @@ async function setupFetchMocking(driver) {
|
|||||||
return { json: async () => clone(mockResponses.gasPricesBasic) };
|
return { json: async () => clone(mockResponses.gasPricesBasic) };
|
||||||
} else if (url.match(/chromeextensionmm/u)) {
|
} else if (url.match(/chromeextensionmm/u)) {
|
||||||
return { json: async () => clone(mockResponses.metametrics) };
|
return { json: async () => clone(mockResponses.metametrics) };
|
||||||
} else if (url.match(/^https:\/\/(api\.metaswap|.*airswap-dev)/u)) {
|
} else if (url.match(/^https:\/\/(api2\.metaswap\.codefi\.network)/u)) {
|
||||||
if (url.match(/featureFlag$/u)) {
|
if (url.match(/featureFlags$/u)) {
|
||||||
return { json: async () => clone(mockResponses.swaps.featureFlag) };
|
return { json: async () => clone(mockResponses.swaps.featureFlags) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return window.origFetch(...args);
|
return window.origFetch(...args);
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export const METASWAP_BASE_URL = 'https://api.metaswap.codefi.network';
|
export const METASWAP_BASE_URL = 'https://api.metaswap.codefi.network';
|
||||||
|
export const METASWAP_API_V2_BASE_URL = 'https://api2.metaswap.codefi.network';
|
||||||
|
@ -220,6 +220,7 @@ export const createSwapsMockStore = () => {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: false,
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appState: {
|
appState: {
|
||||||
|
@ -59,3 +59,23 @@ export const TOKENS_GET_RESPONSE = [
|
|||||||
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
|
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,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -34,9 +34,10 @@ import {
|
|||||||
SWAPS_MAINTENANCE_ROUTE,
|
SWAPS_MAINTENANCE_ROUTE,
|
||||||
} from '../../helpers/constants/routes';
|
} from '../../helpers/constants/routes';
|
||||||
import {
|
import {
|
||||||
fetchSwapsFeatureLiveness,
|
fetchSwapsFeatureFlags,
|
||||||
fetchSwapsGasPrices,
|
fetchSwapsGasPrices,
|
||||||
isContractAddressValid,
|
isContractAddressValid,
|
||||||
|
getSwapsLivenessForNetwork,
|
||||||
} from '../../pages/swaps/swaps.util';
|
} from '../../pages/swaps/swaps.util';
|
||||||
import { calcGasTotal } from '../../pages/send/send.utils';
|
import { calcGasTotal } from '../../pages/send/send.utils';
|
||||||
import {
|
import {
|
||||||
@ -223,9 +224,12 @@ export function shouldShowCustomPriceTooLowWarning(state) {
|
|||||||
|
|
||||||
const getSwapsState = (state) => state.metamask.swapsState;
|
const getSwapsState = (state) => state.metamask.swapsState;
|
||||||
|
|
||||||
export const getSwapsFeatureLiveness = (state) =>
|
export const getSwapsFeatureIsLive = (state) =>
|
||||||
state.metamask.swapsState.swapsFeatureIsLive;
|
state.metamask.swapsState.swapsFeatureIsLive;
|
||||||
|
|
||||||
|
export const getUseNewSwapsApi = (state) =>
|
||||||
|
state.metamask.swapsState.useNewSwapsApi;
|
||||||
|
|
||||||
export const getSwapsQuoteRefreshTime = (state) =>
|
export const getSwapsQuoteRefreshTime = (state) =>
|
||||||
state.metamask.swapsState.swapsQuoteRefreshTime;
|
state.metamask.swapsState.swapsQuoteRefreshTime;
|
||||||
|
|
||||||
@ -373,16 +377,21 @@ export const fetchAndSetSwapsGasPriceInfo = () => {
|
|||||||
|
|
||||||
export const fetchSwapsLiveness = () => {
|
export const fetchSwapsLiveness = () => {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
let swapsFeatureIsLive = false;
|
let swapsLivenessForNetwork = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(
|
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
|
||||||
|
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
|
||||||
|
swapsFeatureFlags,
|
||||||
getCurrentChainId(getState()),
|
getCurrentChainId(getState()),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
||||||
}
|
}
|
||||||
await dispatch(setSwapsLiveness(swapsFeatureIsLive));
|
await dispatch(setSwapsLiveness(swapsLivenessForNetwork));
|
||||||
return swapsFeatureIsLive;
|
return swapsLivenessForNetwork;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -395,15 +404,22 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
let swapsFeatureIsLive = false;
|
let swapsLivenessForNetwork = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId);
|
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
|
||||||
|
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
|
||||||
|
swapsFeatureFlags,
|
||||||
|
chainId,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', 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);
|
await history.push(SWAPS_MAINTENANCE_ROUTE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -600,15 +616,22 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
|||||||
const state = getState();
|
const state = getState();
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
const hardwareWalletUsed = isHardwareWallet(state);
|
const hardwareWalletUsed = isHardwareWallet(state);
|
||||||
let swapsFeatureIsLive = false;
|
let swapsLivenessForNetwork = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId);
|
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
|
||||||
|
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
|
||||||
|
swapsFeatureFlags,
|
||||||
|
chainId,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', 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);
|
await history.push(SWAPS_MAINTENANCE_ROUTE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -808,12 +831,13 @@ export function fetchMetaSwapsGasPriceEstimates() {
|
|||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
|
const useNewSwapsApi = getUseNewSwapsApi(state);
|
||||||
|
|
||||||
dispatch(swapGasPriceEstimatesFetchStarted());
|
dispatch(swapGasPriceEstimatesFetchStarted());
|
||||||
|
|
||||||
let priceEstimates;
|
let priceEstimates;
|
||||||
try {
|
try {
|
||||||
priceEstimates = await fetchSwapsGasPrices(chainId);
|
priceEstimates = await fetchSwapsGasPrices(chainId, useNewSwapsApi);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warn('Fetching swaps gas prices failed:', e);
|
log.warn('Fetching swaps gas prices failed:', e);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
|
||||||
|
import { MOCKS } from '../../../test/jest';
|
||||||
import { setSwapsLiveness } from '../../store/actions';
|
import { setSwapsLiveness } from '../../store/actions';
|
||||||
import { setStorageItem } from '../../helpers/utils/storage-helpers';
|
import { setStorageItem } from '../../helpers/utils/storage-helpers';
|
||||||
import * as swaps from './swaps';
|
import * as swaps from './swaps';
|
||||||
@ -25,7 +26,7 @@ describe('Ducks - Swaps', () => {
|
|||||||
describe('fetchSwapsLiveness', () => {
|
describe('fetchSwapsLiveness', () => {
|
||||||
const cleanFeatureFlagApiCache = () => {
|
const cleanFeatureFlagApiCache = () => {
|
||||||
setStorageItem(
|
setStorageItem(
|
||||||
'cachedFetch:https://api.metaswap.codefi.network/featureFlag',
|
'cachedFetch:https://api2.metaswap.codefi.network/featureFlags',
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -34,12 +35,12 @@ describe('Ducks - Swaps', () => {
|
|||||||
cleanFeatureFlagApiCache();
|
cleanFeatureFlagApiCache();
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockFeatureFlagApiResponse = ({
|
const mockFeatureFlagsApiResponse = ({
|
||||||
active = false,
|
featureFlagsResponse,
|
||||||
replyWithError = false,
|
replyWithError = false,
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const apiNock = nock('https://api.metaswap.codefi.network').get(
|
const apiNock = nock('https://api2.metaswap.codefi.network').get(
|
||||||
'/featureFlag',
|
'/featureFlags',
|
||||||
);
|
);
|
||||||
if (replyWithError) {
|
if (replyWithError) {
|
||||||
return apiNock.replyWithError({
|
return apiNock.replyWithError({
|
||||||
@ -47,9 +48,7 @@ describe('Ducks - Swaps', () => {
|
|||||||
code: 'serverSideError',
|
code: 'serverSideError',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return apiNock.reply(200, {
|
return apiNock.reply(200, featureFlagsResponse);
|
||||||
active,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createGetState = () => {
|
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 mockDispatch = jest.fn();
|
||||||
const featureFlagApiNock = mockFeatureFlagApiResponse({ active: true });
|
const expectedSwapsLiveness = {
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: true,
|
||||||
|
};
|
||||||
|
const featureFlagsResponse = MOCKS.createFeatureFlagsResponse();
|
||||||
|
const featureFlagApiNock = mockFeatureFlagsApiResponse({
|
||||||
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(true);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(true);
|
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 mockDispatch = jest.fn();
|
||||||
const featureFlagApiNock = mockFeatureFlagApiResponse({ active: false });
|
const expectedSwapsLiveness = {
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
|
const featureFlagsResponse = MOCKS.createFeatureFlagsResponse();
|
||||||
|
featureFlagsResponse.ethereum.extension_active = false;
|
||||||
|
const featureFlagApiNock = mockFeatureFlagsApiResponse({
|
||||||
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(false);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(false);
|
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 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,
|
replyWithError: true,
|
||||||
});
|
});
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(false);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(false);
|
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 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());
|
await swaps.fetchSwapsLiveness()(mockDispatch, createGetState());
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
const featureFlagApiNock2 = mockFeatureFlagApiResponse({ active: true });
|
const featureFlagApiNock2 = mockFeatureFlagsApiResponse({
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock2.isDone()).toBe(false); // Second API call wasn't made, cache was used instead.
|
expect(featureFlagApiNock2.isDone()).toBe(false); // Second API call wasn't made, cache was used instead.
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(2);
|
expect(mockDispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(true);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(true);
|
expect(swapsLiveness).toMatchObject(expectedSwapsLiveness);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -31,7 +31,7 @@ import {
|
|||||||
} from '../../store/actions';
|
} from '../../store/actions';
|
||||||
import { setThreeBoxLastUpdated, hideWhatsNewPopup } from '../../ducks/app/app';
|
import { setThreeBoxLastUpdated, hideWhatsNewPopup } from '../../ducks/app/app';
|
||||||
import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask';
|
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 { getEnvironmentType } from '../../../app/scripts/lib/util';
|
||||||
import {
|
import {
|
||||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||||
@ -60,7 +60,7 @@ const mapStateToProps = (state) => {
|
|||||||
const accountBalance = getCurrentEthBalance(state);
|
const accountBalance = getCurrentEthBalance(state);
|
||||||
const { forgottenPassword, threeBoxLastUpdated } = appState;
|
const { forgottenPassword, threeBoxLastUpdated } = appState;
|
||||||
const totalUnapprovedCount = getTotalUnapprovedCount(state);
|
const totalUnapprovedCount = getTotalUnapprovedCount(state);
|
||||||
const swapsEnabled = getSwapsFeatureLiveness(state);
|
const swapsEnabled = getSwapsFeatureIsLive(state);
|
||||||
const pendingConfirmations = getUnapprovedTemplatedConfirmations(state);
|
const pendingConfirmations = getUnapprovedTemplatedConfirmations(state);
|
||||||
|
|
||||||
const envType = getEnvironmentType();
|
const envType = getEnvironmentType();
|
||||||
|
@ -141,7 +141,9 @@ export default function BuildQuote({
|
|||||||
const toTokenIsNotDefault =
|
const toTokenIsNotDefault =
|
||||||
selectedToToken?.address &&
|
selectedToToken?.address &&
|
||||||
!isSwapsDefaultTokenAddress(selectedToToken?.address, chainId);
|
!isSwapsDefaultTokenAddress(selectedToToken?.address, chainId);
|
||||||
const occurances = Number(selectedToToken?.occurances || 0);
|
const occurrences = Number(
|
||||||
|
selectedToToken?.occurances || selectedToToken?.occurrences || 0,
|
||||||
|
);
|
||||||
const {
|
const {
|
||||||
address: fromTokenAddress,
|
address: fromTokenAddress,
|
||||||
symbol: fromTokenSymbol,
|
symbol: fromTokenSymbol,
|
||||||
@ -354,11 +356,11 @@ export default function BuildQuote({
|
|||||||
|
|
||||||
let tokenVerificationDescription = '';
|
let tokenVerificationDescription = '';
|
||||||
if (blockExplorerTokenLink) {
|
if (blockExplorerTokenLink) {
|
||||||
if (occurances === 1) {
|
if (occurrences === 1) {
|
||||||
tokenVerificationDescription = t('verifyThisTokenOn', [
|
tokenVerificationDescription = t('verifyThisTokenOn', [
|
||||||
<BlockExplorerLink key="block-explorer-link" />,
|
<BlockExplorerLink key="block-explorer-link" />,
|
||||||
]);
|
]);
|
||||||
} else if (occurances === 0) {
|
} else if (occurrences === 0) {
|
||||||
tokenVerificationDescription = t('verifyThisUnconfirmedTokenOn', [
|
tokenVerificationDescription = t('verifyThisUnconfirmedTokenOn', [
|
||||||
<BlockExplorerLink key="block-explorer-link" />,
|
<BlockExplorerLink key="block-explorer-link" />,
|
||||||
]);
|
]);
|
||||||
@ -470,13 +472,13 @@ export default function BuildQuote({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{toTokenIsNotDefault &&
|
{toTokenIsNotDefault &&
|
||||||
(occurances < 2 ? (
|
(occurrences < 2 ? (
|
||||||
<ActionableMessage
|
<ActionableMessage
|
||||||
type={occurances === 1 ? 'warning' : 'danger'}
|
type={occurrences === 1 ? 'warning' : 'danger'}
|
||||||
message={
|
message={
|
||||||
<div className="build-quote__token-verification-warning-message">
|
<div className="build-quote__token-verification-warning-message">
|
||||||
<div className="build-quote__bold">
|
<div className="build-quote__bold">
|
||||||
{occurances === 1
|
{occurrences === 1
|
||||||
? t('swapTokenVerificationOnlyOneSource')
|
? t('swapTokenVerificationOnlyOneSource')
|
||||||
: t('swapTokenVerificationAddedManually')}
|
: t('swapTokenVerificationAddedManually')}
|
||||||
</div>
|
</div>
|
||||||
@ -503,7 +505,7 @@ export default function BuildQuote({
|
|||||||
className="build-quote__bold"
|
className="build-quote__bold"
|
||||||
key="token-verification-bold-text"
|
key="token-verification-bold-text"
|
||||||
>
|
>
|
||||||
{t('swapTokenVerificationSources', [occurances])}
|
{t('swapTokenVerificationSources', [occurrences])}
|
||||||
</span>
|
</span>
|
||||||
{blockExplorerTokenLink && (
|
{blockExplorerTokenLink && (
|
||||||
<>
|
<>
|
||||||
@ -563,7 +565,7 @@ export default function BuildQuote({
|
|||||||
!selectedToToken?.address ||
|
!selectedToToken?.address ||
|
||||||
Number(maxSlippage) < 0 ||
|
Number(maxSlippage) < 0 ||
|
||||||
Number(maxSlippage) > MAX_ALLOWED_SLIPPAGE ||
|
Number(maxSlippage) > MAX_ALLOWED_SLIPPAGE ||
|
||||||
(toTokenIsNotDefault && occurances < 2 && !verificationClicked)
|
(toTokenIsNotDefault && occurrences < 2 && !verificationClicked)
|
||||||
}
|
}
|
||||||
hideCancel
|
hideCancel
|
||||||
showTermsOfService
|
showTermsOfService
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
MAINNET_CHAIN_ID,
|
MAINNET_CHAIN_ID,
|
||||||
BSC_CHAIN_ID,
|
BSC_CHAIN_ID,
|
||||||
LOCALHOST_CHAIN_ID,
|
LOCALHOST_CHAIN_ID,
|
||||||
|
POLYGON_CHAIN_ID,
|
||||||
} from '../../../../shared/constants/network';
|
} from '../../../../shared/constants/network';
|
||||||
|
|
||||||
export default function FeeCard({
|
export default function FeeCard({
|
||||||
@ -38,6 +39,8 @@ export default function FeeCard({
|
|||||||
return t('networkNameEthereum');
|
return t('networkNameEthereum');
|
||||||
case BSC_CHAIN_ID:
|
case BSC_CHAIN_ID:
|
||||||
return t('networkNameBSC');
|
return t('networkNameBSC');
|
||||||
|
case POLYGON_CHAIN_ID:
|
||||||
|
return t('networkNamePolygon');
|
||||||
case LOCALHOST_CHAIN_ID:
|
case LOCALHOST_CHAIN_ID:
|
||||||
return t('networkNameTestnet');
|
return t('networkNameTestnet');
|
||||||
default:
|
default:
|
||||||
|
@ -29,10 +29,11 @@ import {
|
|||||||
getAggregatorMetadata,
|
getAggregatorMetadata,
|
||||||
getBackgroundSwapRouteState,
|
getBackgroundSwapRouteState,
|
||||||
getSwapsErrorKey,
|
getSwapsErrorKey,
|
||||||
getSwapsFeatureLiveness,
|
getSwapsFeatureIsLive,
|
||||||
prepareToLeaveSwaps,
|
prepareToLeaveSwaps,
|
||||||
fetchAndSetSwapsGasPriceInfo,
|
fetchAndSetSwapsGasPriceInfo,
|
||||||
fetchSwapsLiveness,
|
fetchSwapsLiveness,
|
||||||
|
getUseNewSwapsApi,
|
||||||
} from '../../ducks/swaps/swaps';
|
} from '../../ducks/swaps/swaps';
|
||||||
import {
|
import {
|
||||||
AWAITING_SIGNATURES_ROUTE,
|
AWAITING_SIGNATURES_ROUTE,
|
||||||
@ -103,9 +104,10 @@ export default function Swap() {
|
|||||||
const aggregatorMetadata = useSelector(getAggregatorMetadata);
|
const aggregatorMetadata = useSelector(getAggregatorMetadata);
|
||||||
const fetchingQuotes = useSelector(getFetchingQuotes);
|
const fetchingQuotes = useSelector(getFetchingQuotes);
|
||||||
let swapsErrorKey = useSelector(getSwapsErrorKey);
|
let swapsErrorKey = useSelector(getSwapsErrorKey);
|
||||||
const swapsEnabled = useSelector(getSwapsFeatureLiveness);
|
const swapsEnabled = useSelector(getSwapsFeatureIsLive);
|
||||||
const chainId = useSelector(getCurrentChainId);
|
const chainId = useSelector(getCurrentChainId);
|
||||||
const isSwapsChain = useSelector(getIsSwapsChain);
|
const isSwapsChain = useSelector(getIsSwapsChain);
|
||||||
|
const useNewSwapsApi = useSelector(getUseNewSwapsApi);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
balance: ethBalance,
|
balance: ethBalance,
|
||||||
@ -165,27 +167,28 @@ export default function Swap() {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchTokens(chainId)
|
if (isFeatureFlagLoaded) {
|
||||||
.then((tokens) => {
|
fetchTokens(chainId, useNewSwapsApi)
|
||||||
dispatch(setSwapsTokens(tokens));
|
.then((tokens) => {
|
||||||
})
|
dispatch(setSwapsTokens(tokens));
|
||||||
.catch((error) => console.error(error));
|
})
|
||||||
|
.catch((error) => console.error(error));
|
||||||
fetchTopAssets(chainId).then((topAssets) => {
|
fetchTopAssets(chainId, useNewSwapsApi).then((topAssets) => {
|
||||||
dispatch(setTopAssets(topAssets));
|
dispatch(setTopAssets(topAssets));
|
||||||
});
|
});
|
||||||
|
fetchAggregatorMetadata(chainId, useNewSwapsApi).then(
|
||||||
fetchAggregatorMetadata(chainId).then((newAggregatorMetadata) => {
|
(newAggregatorMetadata) => {
|
||||||
dispatch(setAggregatorMetadata(newAggregatorMetadata));
|
dispatch(setAggregatorMetadata(newAggregatorMetadata));
|
||||||
});
|
},
|
||||||
|
);
|
||||||
dispatch(fetchAndSetSwapsGasPriceInfo(chainId));
|
dispatch(fetchAndSetSwapsGasPriceInfo(chainId));
|
||||||
|
return () => {
|
||||||
return () => {
|
dispatch(prepareToLeaveSwaps());
|
||||||
dispatch(prepareToLeaveSwaps());
|
};
|
||||||
};
|
}
|
||||||
}, [dispatch, chainId]);
|
}, [dispatch, chainId, isFeatureFlagLoaded, useNewSwapsApi]);
|
||||||
|
|
||||||
const hardwareWalletUsed = useSelector(isHardwareWallet);
|
const hardwareWalletUsed = useSelector(isHardwareWallet);
|
||||||
const hardwareWalletType = useSelector(getHardwareWalletType);
|
const hardwareWalletType = useSelector(getHardwareWalletType);
|
||||||
|
@ -24,7 +24,7 @@ setBackgroundConnection({
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Swap', () => {
|
describe('Swap', () => {
|
||||||
let tokensNock;
|
let featureFlagsNock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
nock(CONSTANTS.METASWAP_BASE_URL)
|
nock(CONSTANTS.METASWAP_BASE_URL)
|
||||||
@ -43,9 +43,13 @@ describe('Swap', () => {
|
|||||||
.get('/gasPrices')
|
.get('/gasPrices')
|
||||||
.reply(200, MOCKS.GAS_PRICES_GET_RESPONSE);
|
.reply(200, MOCKS.GAS_PRICES_GET_RESPONSE);
|
||||||
|
|
||||||
tokensNock = nock(CONSTANTS.METASWAP_BASE_URL)
|
nock(CONSTANTS.METASWAP_BASE_URL)
|
||||||
.get('/tokens')
|
.get('/tokens')
|
||||||
.reply(200, MOCKS.TOKENS_GET_RESPONSE);
|
.reply(200, MOCKS.TOKENS_GET_RESPONSE);
|
||||||
|
|
||||||
|
featureFlagsNock = nock(CONSTANTS.METASWAP_API_V2_BASE_URL)
|
||||||
|
.get('/featureFlags')
|
||||||
|
.reply(200, MOCKS.createFeatureFlagsResponse());
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
@ -55,7 +59,7 @@ describe('Swap', () => {
|
|||||||
it('renders the component with initial props', async () => {
|
it('renders the component with initial props', async () => {
|
||||||
const store = configureMockStore(middleware)(createSwapsMockStore());
|
const store = configureMockStore(middleware)(createSwapsMockStore());
|
||||||
const { container, getByText } = renderWithProvider(<Swap />, store);
|
const { container, getByText } = renderWithProvider(<Swap />, store);
|
||||||
await waitFor(() => expect(tokensNock.isDone()).toBe(true));
|
await waitFor(() => expect(featureFlagsNock.isDone()).toBe(true));
|
||||||
expect(getByText('Swap')).toBeInTheDocument();
|
expect(getByText('Swap')).toBeInTheDocument();
|
||||||
expect(getByText('Cancel')).toBeInTheDocument();
|
expect(getByText('Cancel')).toBeInTheDocument();
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
|
@ -9,6 +9,7 @@ import { usePrevious } from '../../../../hooks/usePrevious';
|
|||||||
import { isValidHexAddress } from '../../../../../shared/modules/hexstring-utils';
|
import { isValidHexAddress } from '../../../../../shared/modules/hexstring-utils';
|
||||||
import { fetchToken } from '../../swaps.util';
|
import { fetchToken } from '../../swaps.util';
|
||||||
import { getCurrentChainId } from '../../../../selectors/selectors';
|
import { getCurrentChainId } from '../../../../selectors/selectors';
|
||||||
|
import { getUseNewSwapsApi } from '../../../../ducks/swaps/swaps';
|
||||||
|
|
||||||
const renderAdornment = () => (
|
const renderAdornment = () => (
|
||||||
<InputAdornment position="start" style={{ marginRight: '12px' }}>
|
<InputAdornment position="start" style={{ marginRight: '12px' }}>
|
||||||
@ -28,6 +29,7 @@ export default function ListItemSearch({
|
|||||||
const fuseRef = useRef();
|
const fuseRef = useRef();
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const chainId = useSelector(getCurrentChainId);
|
const chainId = useSelector(getCurrentChainId);
|
||||||
|
const useNewSwapsApi = useSelector(getUseNewSwapsApi);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search a custom token for import based on a contract address.
|
* Search a custom token for import based on a contract address.
|
||||||
@ -36,7 +38,7 @@ export default function ListItemSearch({
|
|||||||
const handleSearchTokenForImport = async (contractAddress) => {
|
const handleSearchTokenForImport = async (contractAddress) => {
|
||||||
setSearchQuery(contractAddress);
|
setSearchQuery(contractAddress);
|
||||||
try {
|
try {
|
||||||
const token = await fetchToken(contractAddress, chainId);
|
const token = await fetchToken(contractAddress, chainId, useNewSwapsApi);
|
||||||
if (token) {
|
if (token) {
|
||||||
token.primaryLabel = token.symbol;
|
token.primaryLabel = token.symbol;
|
||||||
token.secondaryLabel = token.name;
|
token.secondaryLabel = token.name;
|
||||||
|
@ -6,6 +6,9 @@ import {
|
|||||||
METASWAP_CHAINID_API_HOST_MAP,
|
METASWAP_CHAINID_API_HOST_MAP,
|
||||||
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
|
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
|
||||||
ETH_WETH_CONTRACT_ADDRESS,
|
ETH_WETH_CONTRACT_ADDRESS,
|
||||||
|
ETHEREUM,
|
||||||
|
POLYGON,
|
||||||
|
BSC,
|
||||||
} from '../../../shared/constants/swaps';
|
} from '../../../shared/constants/swaps';
|
||||||
import {
|
import {
|
||||||
isSwapsDefaultTokenAddress,
|
isSwapsDefaultTokenAddress,
|
||||||
@ -15,6 +18,9 @@ import {
|
|||||||
ETH_SYMBOL,
|
ETH_SYMBOL,
|
||||||
WETH_SYMBOL,
|
WETH_SYMBOL,
|
||||||
MAINNET_CHAIN_ID,
|
MAINNET_CHAIN_ID,
|
||||||
|
BSC_CHAIN_ID,
|
||||||
|
POLYGON_CHAIN_ID,
|
||||||
|
LOCALHOST_CHAIN_ID,
|
||||||
} from '../../../shared/constants/network';
|
} from '../../../shared/constants/network';
|
||||||
import { SECOND } from '../../../shared/constants/time';
|
import { SECOND } from '../../../shared/constants/time';
|
||||||
import {
|
import {
|
||||||
@ -42,24 +48,50 @@ const TOKEN_TRANSFER_LOG_TOPIC_HASH =
|
|||||||
|
|
||||||
const CACHE_REFRESH_FIVE_MINUTES = 300000;
|
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) {
|
switch (type) {
|
||||||
case 'trade':
|
case 'trade':
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/trades?`;
|
return `${baseUrl}/trades?`;
|
||||||
case 'tokens':
|
case 'tokens':
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/tokens`;
|
return `${baseUrl}/tokens`;
|
||||||
case 'token':
|
case 'token':
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/token`;
|
return `${baseUrl}/token`;
|
||||||
case 'topAssets':
|
case 'topAssets':
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/topAssets`;
|
return `${baseUrl}/topAssets`;
|
||||||
case 'featureFlag':
|
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/featureFlag`;
|
|
||||||
case 'aggregatorMetadata':
|
case 'aggregatorMetadata':
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/aggregatorMetadata`;
|
return `${baseUrl}/aggregatorMetadata`;
|
||||||
case 'gasPrices':
|
case 'gasPrices':
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/gasPrices`;
|
return `${baseUrl}/gasPrices`;
|
||||||
case 'refreshTime':
|
case 'refreshTime':
|
||||||
return `${METASWAP_CHAINID_API_HOST_MAP[chainId]}/quoteRefreshRate`;
|
return `${baseUrl}/quoteRefreshRate`;
|
||||||
default:
|
default:
|
||||||
throw new Error('getBaseApi requires an api call type');
|
throw new Error('getBaseApi requires an api call type');
|
||||||
}
|
}
|
||||||
@ -233,7 +265,7 @@ export async function fetchTradesInfo(
|
|||||||
fromAddress,
|
fromAddress,
|
||||||
exchangeList,
|
exchangeList,
|
||||||
},
|
},
|
||||||
{ chainId },
|
{ chainId, useNewSwapsApi },
|
||||||
) {
|
) {
|
||||||
const urlParams = {
|
const urlParams = {
|
||||||
destinationToken,
|
destinationToken,
|
||||||
@ -249,7 +281,11 @@ export async function fetchTradesInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const queryString = new URLSearchParams(urlParams).toString();
|
const queryString = new URLSearchParams(urlParams).toString();
|
||||||
const tradeURL = `${getBaseApi('trade', chainId)}${queryString}`;
|
const tradeURL = `${getBaseApi(
|
||||||
|
'trade',
|
||||||
|
chainId,
|
||||||
|
useNewSwapsApi,
|
||||||
|
)}${queryString}`;
|
||||||
const tradesResponse = await fetchWithCache(
|
const tradesResponse = await fetchWithCache(
|
||||||
tradeURL,
|
tradeURL,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -293,8 +329,8 @@ export async function fetchTradesInfo(
|
|||||||
return newQuotes;
|
return newQuotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchToken(contractAddress, chainId) {
|
export async function fetchToken(contractAddress, chainId, useNewSwapsApi) {
|
||||||
const tokenUrl = getBaseApi('token', chainId);
|
const tokenUrl = getBaseApi('token', chainId, useNewSwapsApi);
|
||||||
const token = await fetchWithCache(
|
const token = await fetchWithCache(
|
||||||
`${tokenUrl}?address=${contractAddress}`,
|
`${tokenUrl}?address=${contractAddress}`,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -303,8 +339,8 @@ export async function fetchToken(contractAddress, chainId) {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTokens(chainId) {
|
export async function fetchTokens(chainId, useNewSwapsApi) {
|
||||||
const tokensUrl = getBaseApi('tokens', chainId);
|
const tokensUrl = getBaseApi('tokens', chainId, useNewSwapsApi);
|
||||||
const tokens = await fetchWithCache(
|
const tokens = await fetchWithCache(
|
||||||
tokensUrl,
|
tokensUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -325,8 +361,12 @@ export async function fetchTokens(chainId) {
|
|||||||
return filteredTokens;
|
return filteredTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAggregatorMetadata(chainId) {
|
export async function fetchAggregatorMetadata(chainId, useNewSwapsApi) {
|
||||||
const aggregatorMetadataUrl = getBaseApi('aggregatorMetadata', chainId);
|
const aggregatorMetadataUrl = getBaseApi(
|
||||||
|
'aggregatorMetadata',
|
||||||
|
chainId,
|
||||||
|
useNewSwapsApi,
|
||||||
|
);
|
||||||
const aggregators = await fetchWithCache(
|
const aggregators = await fetchWithCache(
|
||||||
aggregatorMetadataUrl,
|
aggregatorMetadataUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -347,8 +387,8 @@ export async function fetchAggregatorMetadata(chainId) {
|
|||||||
return filteredAggregators;
|
return filteredAggregators;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTopAssets(chainId) {
|
export async function fetchTopAssets(chainId, useNewSwapsApi) {
|
||||||
const topAssetsUrl = getBaseApi('topAssets', chainId);
|
const topAssetsUrl = getBaseApi('topAssets', chainId, useNewSwapsApi);
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
topAssetsUrl,
|
topAssetsUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -363,18 +403,18 @@ export async function fetchTopAssets(chainId) {
|
|||||||
return topAssetsMap;
|
return topAssetsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchSwapsFeatureLiveness(chainId) {
|
export async function fetchSwapsFeatureFlags() {
|
||||||
const status = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
getBaseApi('featureFlag', chainId),
|
`${SWAPS_API_V2_BASE_URL}/featureFlags`,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
{ cacheRefreshTime: 600000 },
|
{ cacheRefreshTime: 600000 },
|
||||||
);
|
);
|
||||||
return status?.active;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchSwapsQuoteRefreshTime(chainId) {
|
export async function fetchSwapsQuoteRefreshTime(chainId, useNewSwapsApi) {
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
getBaseApi('refreshTime', chainId),
|
getBaseApi('refreshTime', chainId, useNewSwapsApi),
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
{ cacheRefreshTime: 600000 },
|
{ cacheRefreshTime: 600000 },
|
||||||
);
|
);
|
||||||
@ -409,8 +449,8 @@ export async function fetchTokenBalance(address, userAddress) {
|
|||||||
return usersToken;
|
return usersToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchSwapsGasPrices(chainId) {
|
export async function fetchSwapsGasPrices(chainId, useNewSwapsApi) {
|
||||||
const gasPricesUrl = getBaseApi('gasPrices', chainId);
|
const gasPricesUrl = getBaseApi('gasPrices', chainId, useNewSwapsApi);
|
||||||
const response = await fetchWithCache(
|
const response = await fetchWithCache(
|
||||||
gasPricesUrl,
|
gasPricesUrl,
|
||||||
{ method: 'GET' },
|
{ method: 'GET' },
|
||||||
@ -730,3 +770,56 @@ export const isContractAddressValid = (
|
|||||||
contractAddressForChainId.toUpperCase() === contractAddress.toUpperCase()
|
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,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
import { MOCKS } from '../../../test/jest';
|
||||||
import {
|
import {
|
||||||
ETH_SYMBOL,
|
ETH_SYMBOL,
|
||||||
WETH_SYMBOL,
|
WETH_SYMBOL,
|
||||||
MAINNET_CHAIN_ID,
|
MAINNET_CHAIN_ID,
|
||||||
BSC_CHAIN_ID,
|
BSC_CHAIN_ID,
|
||||||
|
POLYGON_CHAIN_ID,
|
||||||
LOCALHOST_CHAIN_ID,
|
LOCALHOST_CHAIN_ID,
|
||||||
|
RINKEBY_CHAIN_ID,
|
||||||
} from '../../../shared/constants/network';
|
} from '../../../shared/constants/network';
|
||||||
import {
|
import {
|
||||||
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
|
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
|
||||||
ETH_WETH_CONTRACT_ADDRESS,
|
ETH_WETH_CONTRACT_ADDRESS,
|
||||||
|
ETHEREUM,
|
||||||
|
POLYGON,
|
||||||
|
BSC,
|
||||||
} from '../../../shared/constants/swaps';
|
} from '../../../shared/constants/swaps';
|
||||||
import {
|
import {
|
||||||
TOKENS,
|
TOKENS,
|
||||||
@ -17,13 +23,14 @@ import {
|
|||||||
AGGREGATOR_METADATA,
|
AGGREGATOR_METADATA,
|
||||||
TOP_ASSETS,
|
TOP_ASSETS,
|
||||||
} from './swaps-util-test-constants';
|
} from './swaps-util-test-constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fetchTradesInfo,
|
fetchTradesInfo,
|
||||||
fetchTokens,
|
fetchTokens,
|
||||||
fetchAggregatorMetadata,
|
fetchAggregatorMetadata,
|
||||||
fetchTopAssets,
|
fetchTopAssets,
|
||||||
isContractAddressValid,
|
isContractAddressValid,
|
||||||
|
getNetworkNameByChainId,
|
||||||
|
getSwapsLivenessForNetwork,
|
||||||
} from './swaps.util';
|
} from './swaps.util';
|
||||||
|
|
||||||
jest.mock('../../helpers/utils/storage-helpers.js', () => ({
|
jest.mock('../../helpers/utils/storage-helpers.js', () => ({
|
||||||
@ -372,4 +379,75 @@ describe('Swaps Util', () => {
|
|||||||
).toBe(false);
|
).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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -2121,9 +2121,9 @@ export function setPendingTokens(pendingTokens) {
|
|||||||
|
|
||||||
// Swaps
|
// Swaps
|
||||||
|
|
||||||
export function setSwapsLiveness(swapsFeatureIsLive) {
|
export function setSwapsLiveness(swapsLiveness) {
|
||||||
return async (dispatch) => {
|
return async (dispatch) => {
|
||||||
await promisifiedBackground.setSwapsLiveness(swapsFeatureIsLive);
|
await promisifiedBackground.setSwapsLiveness(swapsLiveness);
|
||||||
await forceUpdateMetamaskState(dispatch);
|
await forceUpdateMetamaskState(dispatch);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user