mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Enable new networks for Swaps (#16396)
This commit is contained in:
parent
6c0930899d
commit
1a9ebab828
6
app/_locales/en/messages.json
generated
6
app/_locales/en/messages.json
generated
@ -2143,6 +2143,9 @@
|
||||
"networkName": {
|
||||
"message": "Network name"
|
||||
},
|
||||
"networkNameArbitrum": {
|
||||
"message": "Arbitrum"
|
||||
},
|
||||
"networkNameAvalanche": {
|
||||
"message": "Avalanche"
|
||||
},
|
||||
@ -2158,6 +2161,9 @@
|
||||
"networkNameGoerli": {
|
||||
"message": "Goerli"
|
||||
},
|
||||
"networkNameOptimism": {
|
||||
"message": "Optimism"
|
||||
},
|
||||
"networkNamePolygon": {
|
||||
"message": "Polygon"
|
||||
},
|
||||
|
@ -274,6 +274,7 @@ export const CURRENCY_SYMBOLS = {
|
||||
USDC: 'USDC',
|
||||
USDT: 'USDT',
|
||||
WETH: 'WETH',
|
||||
OPTIMISM: 'OP',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
@ -531,6 +532,7 @@ export const NATIVE_CURRENCY_TOKEN_IMAGE_MAP = {
|
||||
[CURRENCY_SYMBOLS.BNB]: BNB_TOKEN_IMAGE_URL,
|
||||
[CURRENCY_SYMBOLS.MATIC]: MATIC_TOKEN_IMAGE_URL,
|
||||
[CURRENCY_SYMBOLS.AVALANCHE]: AVAX_TOKEN_IMAGE_URL,
|
||||
[CURRENCY_SYMBOLS.OPTIMISM]: OPTIMISM_TOKEN_IMAGE_URL,
|
||||
} as const;
|
||||
|
||||
export const INFURA_BLOCKED_KEY = 'countryBlocked';
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
ETH_TOKEN_IMAGE_URL,
|
||||
TEST_ETH_TOKEN_IMAGE_URL,
|
||||
BNB_TOKEN_IMAGE_URL,
|
||||
MATIC_TOKEN_IMAGE_URL,
|
||||
@ -23,7 +24,7 @@ export const ETH_SWAPS_TOKEN_OBJECT = {
|
||||
name: 'Ether',
|
||||
address: DEFAULT_TOKEN_ADDRESS,
|
||||
decimals: 18,
|
||||
iconUrl: './images/black-eth-logo.svg',
|
||||
iconUrl: ETH_TOKEN_IMAGE_URL,
|
||||
};
|
||||
|
||||
export const BNB_SWAPS_TOKEN_OBJECT = {
|
||||
@ -66,6 +67,10 @@ export const GOERLI_SWAPS_TOKEN_OBJECT = {
|
||||
iconUrl: TEST_ETH_TOKEN_IMAGE_URL,
|
||||
};
|
||||
|
||||
export const ARBITRUM_SWAPS_TOKEN_OBJECT = { ...ETH_SWAPS_TOKEN_OBJECT };
|
||||
|
||||
export const OPTIMISM_SWAPS_TOKEN_OBJECT = { ...ETH_SWAPS_TOKEN_OBJECT };
|
||||
|
||||
// A gas value for ERC20 approve calls that should be sufficient for all ERC20 approve implementations
|
||||
export const DEFAULT_ERC20_APPROVE_GAS = '0x1d4c0';
|
||||
|
||||
@ -77,8 +82,9 @@ const BSC_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
||||
|
||||
// It's the same as we use for BSC.
|
||||
const POLYGON_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
||||
|
||||
const AVALANCHE_CONTRACT_ADDRESS = '0x1a1ec25dc08e98e5e93f1104b5e5cdd298707d31';
|
||||
const OPTIMISM_CONTRACT_ADDRESS = '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6';
|
||||
const ARBITRUM_CONTRACT_ADDRESS = '0x9dDA6Ef3D919c9bC8885D5560999A3640431e8e6';
|
||||
|
||||
export const WETH_CONTRACT_ADDRESS =
|
||||
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||
@ -91,6 +97,11 @@ export const WMATIC_CONTRACT_ADDRESS =
|
||||
export const WAVAX_CONTRACT_ADDRESS =
|
||||
'0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7';
|
||||
|
||||
export const WETH_OPTIMISM_CONTRACT_ADDRESS =
|
||||
'0x4200000000000000000000000000000000000006';
|
||||
export const WETH_ARBITRUM_CONTRACT_ADDRESS =
|
||||
'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1';
|
||||
|
||||
const SWAPS_TESTNET_CHAIN_ID = '0x539';
|
||||
|
||||
export const SWAPS_API_V2_BASE_URL = 'https://swap.metaswap.codefi.network';
|
||||
@ -105,6 +116,8 @@ const MAINNET_DEFAULT_BLOCK_EXPLORER_URL = 'https://etherscan.io/';
|
||||
const GOERLI_DEFAULT_BLOCK_EXPLORER_URL = 'https://goerli.etherscan.io/';
|
||||
const POLYGON_DEFAULT_BLOCK_EXPLORER_URL = 'https://polygonscan.com/';
|
||||
const AVALANCHE_DEFAULT_BLOCK_EXPLORER_URL = 'https://snowtrace.io/';
|
||||
const OPTIMISM_DEFAULT_BLOCK_EXPLORER_URL = 'https://optimistic.etherscan.io/';
|
||||
const ARBITRUM_DEFAULT_BLOCK_EXPLORER_URL = 'https://arbiscan.io/';
|
||||
|
||||
export const ALLOWED_PROD_SWAPS_CHAIN_IDS = [
|
||||
CHAIN_IDS.MAINNET,
|
||||
@ -112,6 +125,8 @@ export const ALLOWED_PROD_SWAPS_CHAIN_IDS = [
|
||||
CHAIN_IDS.BSC,
|
||||
CHAIN_IDS.POLYGON,
|
||||
CHAIN_IDS.AVALANCHE,
|
||||
CHAIN_IDS.OPTIMISM,
|
||||
CHAIN_IDS.ARBITRUM,
|
||||
];
|
||||
|
||||
export const ALLOWED_DEV_SWAPS_CHAIN_IDS = [
|
||||
@ -131,6 +146,8 @@ export const SWAPS_CHAINID_CONTRACT_ADDRESS_MAP = {
|
||||
[CHAIN_IDS.POLYGON]: POLYGON_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.GOERLI]: TESTNET_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.AVALANCHE]: AVALANCHE_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.OPTIMISM]: OPTIMISM_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.ARBITRUM]: ARBITRUM_CONTRACT_ADDRESS,
|
||||
};
|
||||
|
||||
export const SWAPS_WRAPPED_TOKENS_ADDRESSES = {
|
||||
@ -140,6 +157,8 @@ export const SWAPS_WRAPPED_TOKENS_ADDRESSES = {
|
||||
[CHAIN_IDS.POLYGON]: WMATIC_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.GOERLI]: WETH_GOERLI_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.AVALANCHE]: WAVAX_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.OPTIMISM]: WETH_OPTIMISM_CONTRACT_ADDRESS,
|
||||
[CHAIN_IDS.ARBITRUM]: WETH_ARBITRUM_CONTRACT_ADDRESS,
|
||||
};
|
||||
|
||||
export const ALLOWED_CONTRACT_ADDRESSES = {
|
||||
@ -167,6 +186,14 @@ export const ALLOWED_CONTRACT_ADDRESSES = {
|
||||
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP[CHAIN_IDS.AVALANCHE],
|
||||
SWAPS_WRAPPED_TOKENS_ADDRESSES[CHAIN_IDS.AVALANCHE],
|
||||
],
|
||||
[CHAIN_IDS.OPTIMISM]: [
|
||||
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP[CHAIN_IDS.OPTIMISM],
|
||||
SWAPS_WRAPPED_TOKENS_ADDRESSES[CHAIN_IDS.OPTIMISM],
|
||||
],
|
||||
[CHAIN_IDS.ARBITRUM]: [
|
||||
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP[CHAIN_IDS.ARBITRUM],
|
||||
SWAPS_WRAPPED_TOKENS_ADDRESSES[CHAIN_IDS.ARBITRUM],
|
||||
],
|
||||
};
|
||||
|
||||
export const SWAPS_CHAINID_DEFAULT_TOKEN_MAP = {
|
||||
@ -176,6 +203,8 @@ export const SWAPS_CHAINID_DEFAULT_TOKEN_MAP = {
|
||||
[CHAIN_IDS.POLYGON]: MATIC_SWAPS_TOKEN_OBJECT,
|
||||
[CHAIN_IDS.GOERLI]: GOERLI_SWAPS_TOKEN_OBJECT,
|
||||
[CHAIN_IDS.AVALANCHE]: AVAX_SWAPS_TOKEN_OBJECT,
|
||||
[CHAIN_IDS.OPTIMISM]: OPTIMISM_SWAPS_TOKEN_OBJECT,
|
||||
[CHAIN_IDS.ARBITRUM]: ARBITRUM_SWAPS_TOKEN_OBJECT,
|
||||
};
|
||||
|
||||
export const SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP = {
|
||||
@ -184,6 +213,8 @@ export const SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP = {
|
||||
[CHAIN_IDS.POLYGON]: POLYGON_DEFAULT_BLOCK_EXPLORER_URL,
|
||||
[CHAIN_IDS.GOERLI]: GOERLI_DEFAULT_BLOCK_EXPLORER_URL,
|
||||
[CHAIN_IDS.AVALANCHE]: AVALANCHE_DEFAULT_BLOCK_EXPLORER_URL,
|
||||
[CHAIN_IDS.OPTIMISM]: OPTIMISM_DEFAULT_BLOCK_EXPLORER_URL,
|
||||
[CHAIN_IDS.ARBITRUM]: ARBITRUM_DEFAULT_BLOCK_EXPLORER_URL,
|
||||
};
|
||||
|
||||
export const ETHEREUM = 'ethereum';
|
||||
@ -191,6 +222,8 @@ export const POLYGON = 'polygon';
|
||||
export const BSC = 'bsc';
|
||||
export const GOERLI = 'goerli';
|
||||
export const AVALANCHE = 'avalanche';
|
||||
export const OPTIMISM = 'optimism';
|
||||
export const ARBITRUM = 'arbitrum';
|
||||
|
||||
export const SWAPS_CLIENT_ID = 'extension';
|
||||
|
||||
|
@ -146,7 +146,6 @@ export const getBaseApi = function (type, chainId = CHAIN_IDS.MAINNET) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
chainId = TEST_CHAIN_IDS.includes(chainId) ? CHAIN_IDS.MAINNET : chainId;
|
||||
const baseUrl = getBaseUrlForNewSwapsApi(type, chainId);
|
||||
const chainIdDecimal = chainId && parseInt(chainId, 16);
|
||||
if (!baseUrl) {
|
||||
throw new Error(`Swaps API calls are disabled for chainId: ${chainId}`);
|
||||
}
|
||||
@ -164,8 +163,7 @@ export const getBaseApi = function (type, chainId = CHAIN_IDS.MAINNET) {
|
||||
case 'gasPrices':
|
||||
return `${baseUrl}/gasPrices`;
|
||||
case 'network':
|
||||
// Only use v2 for this endpoint.
|
||||
return `${SWAPS_API_V2_BASE_URL}/networks/${chainIdDecimal}`;
|
||||
return baseUrl;
|
||||
default:
|
||||
throw new Error('getBaseApi requires an api call type');
|
||||
}
|
||||
|
@ -65,7 +65,10 @@ export function getRenderableTokenData(
|
||||
(symbol === CURRENCY_SYMBOLS.ETH && chainId === CHAIN_IDS.GOERLI) ||
|
||||
(symbol === CURRENCY_SYMBOLS.BNB && chainId === CHAIN_IDS.BSC) ||
|
||||
(symbol === CURRENCY_SYMBOLS.MATIC && chainId === CHAIN_IDS.POLYGON) ||
|
||||
(symbol === CURRENCY_SYMBOLS.AVALANCHE && chainId === CHAIN_IDS.AVALANCHE)
|
||||
(symbol === CURRENCY_SYMBOLS.AVALANCHE &&
|
||||
chainId === CHAIN_IDS.AVALANCHE) ||
|
||||
(symbol === CURRENCY_SYMBOLS.ETH && chainId === CHAIN_IDS.OPTIMISM) ||
|
||||
(symbol === CURRENCY_SYMBOLS.ETH && chainId === CHAIN_IDS.ARBITRUM)
|
||||
? iconUrl
|
||||
: formatIconUrlWithProxy({
|
||||
chainId: chainIdForTokenIcons,
|
||||
|
@ -46,6 +46,10 @@ export default function FeeCard({
|
||||
return t('networkNameGoerli');
|
||||
case CHAIN_IDS.AVALANCHE:
|
||||
return t('networkNameAvalanche');
|
||||
case CHAIN_IDS.OPTIMISM:
|
||||
return t('networkNameOptimism');
|
||||
case CHAIN_IDS.ARBITRUM:
|
||||
return t('networkNameArbitrum');
|
||||
default:
|
||||
throw new Error('This network is not supported for token swaps');
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import {
|
||||
BSC,
|
||||
GOERLI,
|
||||
AVALANCHE,
|
||||
OPTIMISM,
|
||||
ARBITRUM,
|
||||
SWAPS_API_V2_BASE_URL,
|
||||
SWAPS_DEV_API_V2_BASE_URL,
|
||||
SWAPS_CLIENT_ID,
|
||||
@ -38,6 +40,7 @@ import {
|
||||
validateData,
|
||||
} from '../../../shared/lib/swaps-utils';
|
||||
import { SECOND } from '../../../shared/constants/time';
|
||||
import { sumHexes } from '../../helpers/utils/transactions.util';
|
||||
|
||||
const CACHE_REFRESH_FIVE_MINUTES = 300000;
|
||||
const USD_CURRENCY_CODE = 'usd';
|
||||
@ -384,11 +387,18 @@ export function getRenderableNetworkFeesForQuote({
|
||||
sourceAmount,
|
||||
chainId,
|
||||
nativeCurrencySymbol,
|
||||
multiLayerL1FeeTotal,
|
||||
}) {
|
||||
const totalGasLimitForCalculation = new BigNumber(tradeGas || '0x0', 16)
|
||||
.plus(approveGas || '0x0', 16)
|
||||
.toString(16);
|
||||
const gasTotalInWeiHex = calcGasTotal(totalGasLimitForCalculation, gasPrice);
|
||||
let gasTotalInWeiHex = calcGasTotal(totalGasLimitForCalculation, gasPrice);
|
||||
if (multiLayerL1FeeTotal !== null) {
|
||||
gasTotalInWeiHex = sumHexes(
|
||||
gasTotalInWeiHex || '0x0',
|
||||
multiLayerL1FeeTotal || '0x0',
|
||||
);
|
||||
}
|
||||
|
||||
const nonGasFee = new BigNumber(tradeValue, 16)
|
||||
.minus(
|
||||
@ -449,6 +459,7 @@ export function quotesToRenderableData(
|
||||
chainId,
|
||||
smartTransactionEstimatedGas,
|
||||
nativeCurrencySymbol,
|
||||
multiLayerL1FeeTotal,
|
||||
) {
|
||||
return Object.values(quotes).map((quote) => {
|
||||
const {
|
||||
@ -489,6 +500,7 @@ export function quotesToRenderableData(
|
||||
sourceSymbol: sourceTokenInfo.symbol,
|
||||
sourceAmount,
|
||||
chainId,
|
||||
multiLayerL1FeeTotal,
|
||||
}));
|
||||
|
||||
if (smartTransactionEstimatedGas) {
|
||||
@ -611,6 +623,10 @@ export const getNetworkNameByChainId = (chainId) => {
|
||||
return GOERLI;
|
||||
case CHAIN_IDS.AVALANCHE:
|
||||
return AVALANCHE;
|
||||
case CHAIN_IDS.OPTIMISM:
|
||||
return OPTIMISM;
|
||||
case CHAIN_IDS.ARBITRUM:
|
||||
return ARBITRUM;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import {
|
||||
BSC,
|
||||
GOERLI,
|
||||
AVALANCHE,
|
||||
OPTIMISM,
|
||||
ARBITRUM,
|
||||
} from '../../../shared/constants/swaps';
|
||||
import {
|
||||
fetchTradesInfo,
|
||||
@ -319,6 +321,14 @@ describe('Swaps Util', () => {
|
||||
it('returns "avalanche" for Avalanche chain ID', () => {
|
||||
expect(getNetworkNameByChainId(CHAIN_IDS.AVALANCHE)).toBe(AVALANCHE);
|
||||
});
|
||||
|
||||
it('returns "optimism" for Optimism chain ID', () => {
|
||||
expect(getNetworkNameByChainId(CHAIN_IDS.OPTIMISM)).toBe(OPTIMISM);
|
||||
});
|
||||
|
||||
it('returns "arbitrum" for Arbitrum chain ID', () => {
|
||||
expect(getNetworkNameByChainId(CHAIN_IDS.ARBITRUM)).toBe(ARBITRUM);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSwapsLivenessForNetwork', () => {
|
||||
|
@ -11,6 +11,7 @@ import { useHistory } from 'react-router-dom';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { isEqual } from 'lodash';
|
||||
import classnames from 'classnames';
|
||||
import { captureException } from '@sentry/browser';
|
||||
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import SelectQuotePopover from '../select-quote-popover';
|
||||
@ -63,9 +64,9 @@ import {
|
||||
getHardwareWalletType,
|
||||
checkNetworkAndAccountSupports1559,
|
||||
getUSDConversionRate,
|
||||
getIsMultiLayerFeeNetwork,
|
||||
} from '../../../selectors';
|
||||
import { getNativeCurrency, getTokens } from '../../../ducks/metamask/metamask';
|
||||
|
||||
import {
|
||||
safeRefetchQuotes,
|
||||
setCustomApproveTxData,
|
||||
@ -113,6 +114,8 @@ import {
|
||||
toPrecisionWithoutTrailingZeros,
|
||||
} from '../../../../shared/lib/transactions-controller-utils';
|
||||
import { calcTokenValue } from '../../../../shared/lib/swaps-utils';
|
||||
import fetchEstimatedL1Fee from '../../../helpers/utils/optimism/fetchEstimatedL1Fee';
|
||||
import { sumHexes } from '../../../helpers/utils/transactions.util';
|
||||
import ViewQuotePriceDifference from './view-quote-price-difference';
|
||||
|
||||
let intervalId;
|
||||
@ -128,6 +131,7 @@ export default function ViewQuote() {
|
||||
const [selectQuotePopoverShown, setSelectQuotePopoverShown] = useState(false);
|
||||
const [warningHidden, setWarningHidden] = useState(false);
|
||||
const [originalApproveAmount, setOriginalApproveAmount] = useState(null);
|
||||
const [multiLayerL1FeeTotal, setMultiLayerL1FeeTotal] = useState(null);
|
||||
// We need to have currentTimestamp in state, otherwise it would change with each rerender.
|
||||
const [currentTimestamp] = useState(Date.now());
|
||||
|
||||
@ -161,6 +165,7 @@ export default function ViewQuote() {
|
||||
const { balance: ethBalance } = useSelector(getSelectedAccount, shallowEqual);
|
||||
const conversionRate = useSelector(conversionRateSelector);
|
||||
const USDConversionRate = useSelector(getUSDConversionRate);
|
||||
const isMultiLayerFeeNetwork = useSelector(getIsMultiLayerFeeNetwork);
|
||||
const currentCurrency = useSelector(getCurrentCurrency);
|
||||
const swapsTokens = useSelector(getTokens, isEqual);
|
||||
const networkAndAccountSupports1559 = useSelector(
|
||||
@ -261,8 +266,13 @@ export default function ViewQuote() {
|
||||
maxPriorityFeePerGas,
|
||||
);
|
||||
}
|
||||
|
||||
const gasTotalInWeiHex = calcGasTotal(maxGasLimit, maxFeePerGas || gasPrice);
|
||||
let gasTotalInWeiHex = calcGasTotal(maxGasLimit, maxFeePerGas || gasPrice);
|
||||
if (multiLayerL1FeeTotal !== null) {
|
||||
gasTotalInWeiHex = sumHexes(
|
||||
gasTotalInWeiHex || '0x0',
|
||||
multiLayerL1FeeTotal || '0x0',
|
||||
);
|
||||
}
|
||||
|
||||
const { tokensWithBalances } = useTokenTracker(swapsTokens, true);
|
||||
const balanceToken =
|
||||
@ -303,6 +313,7 @@ export default function ViewQuote() {
|
||||
smartTransactionsOptInStatus &&
|
||||
smartTransactionFees?.tradeTxFees,
|
||||
nativeCurrencySymbol,
|
||||
multiLayerL1FeeTotal,
|
||||
);
|
||||
}, [
|
||||
quotes,
|
||||
@ -318,6 +329,7 @@ export default function ViewQuote() {
|
||||
nativeCurrencySymbol,
|
||||
smartTransactionsEnabled,
|
||||
smartTransactionsOptInStatus,
|
||||
multiLayerL1FeeTotal,
|
||||
]);
|
||||
|
||||
const renderableDataForUsedQuote = renderablePopoverData.find(
|
||||
@ -351,6 +363,7 @@ export default function ViewQuote() {
|
||||
sourceAmount: usedQuote.sourceAmount,
|
||||
chainId,
|
||||
nativeCurrencySymbol,
|
||||
multiLayerL1FeeTotal,
|
||||
});
|
||||
additionalTrackingParams.reg_tx_fee_in_usd = Number(feeInUsd);
|
||||
additionalTrackingParams.reg_tx_fee_in_eth = Number(rawEthFee);
|
||||
@ -367,6 +380,7 @@ export default function ViewQuote() {
|
||||
sourceAmount: usedQuote.sourceAmount,
|
||||
chainId,
|
||||
nativeCurrencySymbol,
|
||||
multiLayerL1FeeTotal,
|
||||
});
|
||||
let {
|
||||
feeInFiat: maxFeeInFiat,
|
||||
@ -875,6 +889,25 @@ export default function ViewQuote() {
|
||||
submitClicked,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMultiLayerFeeNetwork) {
|
||||
return;
|
||||
}
|
||||
const getEstimatedL1Fee = async () => {
|
||||
try {
|
||||
const result = await fetchEstimatedL1Fee(global.eth, {
|
||||
txParams: unsignedTransaction,
|
||||
chainId,
|
||||
});
|
||||
setMultiLayerL1FeeTotal(result);
|
||||
} catch (e) {
|
||||
captureException(e);
|
||||
setMultiLayerL1FeeTotal(null);
|
||||
}
|
||||
};
|
||||
getEstimatedL1Fee();
|
||||
}, [unsignedTransaction, isMultiLayerFeeNetwork, chainId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSmartTransactionsEnabled && smartTransactionsOptInStatus) {
|
||||
// Removes a smart transactions error when the component loads.
|
||||
|
Loading…
Reference in New Issue
Block a user