1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 09:23:21 +01:00

feat(mme-17214): migrate L33 files to typescript (#17372)

This commit is contained in:
Danica Shen 2023-01-31 12:01:10 +00:00 committed by GitHub
parent 8a7c897a1c
commit 2958d68af8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 330 additions and 170 deletions

View File

@ -4,6 +4,9 @@ import { SWAPS_API_V2_BASE_URL } from '../../../shared/constants/swaps';
import {
BUYABLE_CHAINS_MAP,
CHAIN_IDS,
WyreChainSettings,
CurrencySymbol,
ChainId,
} from '../../../shared/constants/network';
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout';
import {
@ -18,13 +21,17 @@ const fetchWithTimeout = getFetchWithTimeout();
/**
* Create a Wyre purchase URL.
*
* @param {string} walletAddress - Ethereum destination address
* @param {string} chainId - Current chain ID
* @param {string|undefined} symbol - Token symbol to buy
* @param walletAddress - Ethereum destination address
* @param chainId - Current chain ID
* @param symbol - Token symbol to buy
* @returns String
*/
const createWyrePurchaseUrl = async (walletAddress, chainId, symbol) => {
const { wyre = {} } = BUYABLE_CHAINS_MAP[chainId];
const createWyrePurchaseUrl = async (
walletAddress: string,
chainId: keyof typeof BUYABLE_CHAINS_MAP,
symbol: CurrencySymbol,
): Promise<any> => {
const { wyre = {} as WyreChainSettings } = BUYABLE_CHAINS_MAP[chainId];
const { srn, currencyCode } = wyre;
const networkId = parseInt(chainId, 16);
@ -57,12 +64,16 @@ const createWyrePurchaseUrl = async (walletAddress, chainId, symbol) => {
* Create a Transak Checkout URL.
* API docs here: https://www.notion.so/Query-Parameters-9ec523df3b874ec58cef4fa3a906f238
*
* @param {string} walletAddress - Ethereum destination address
* @param {string} chainId - Current chain ID
* @param {string|undefined} symbol - Token symbol to buy
* @param walletAddress - Ethereum destination address
* @param chainId - Current chain ID
* @param symbol - Token symbol to buy
* @returns String
*/
const createTransakUrl = (walletAddress, chainId, symbol) => {
const createTransakUrl = (
walletAddress: string,
chainId: keyof typeof BUYABLE_CHAINS_MAP,
symbol: CurrencySymbol,
): string => {
const { nativeCurrency, network } = BUYABLE_CHAINS_MAP[chainId];
const queryParams = new URLSearchParams({
@ -79,13 +90,17 @@ const createTransakUrl = (walletAddress, chainId, symbol) => {
/**
* Create a MoonPay Checkout URL.
*
* @param {string} walletAddress - Destination address
* @param {string} chainId - Current chain ID
* @param {string|undefined} symbol - Token symbol to buy
* @param walletAddress - Destination address
* @param chainId - Current chain ID
* @param symbol - Token symbol to buy
* @returns String
*/
const createMoonPayUrl = async (walletAddress, chainId, symbol) => {
const { moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {} } =
const createMoonPayUrl = async (
walletAddress: string,
chainId: keyof typeof BUYABLE_CHAINS_MAP,
symbol: CurrencySymbol,
): Promise<string> => {
const { moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {} as any } =
BUYABLE_CHAINS_MAP[chainId];
const moonPayQueryParams = new URLSearchParams({
apiKey: MOONPAY_API_KEY,
@ -121,12 +136,16 @@ const createMoonPayUrl = async (walletAddress, chainId, symbol) => {
/**
* Create a Coinbase Pay Checkout URL.
*
* @param {string} walletAddress - Ethereum destination address
* @param {string} chainId - Current chain ID
* @param {string|undefined} symbol - Token symbol to buy
* @param walletAddress - Ethereum destination address
* @param chainId - Current chain ID
* @param symbol - Token symbol to buy
* @returns String
*/
const createCoinbasePayUrl = (walletAddress, chainId, symbol) => {
const createCoinbasePayUrl = (
walletAddress: string,
chainId: keyof typeof BUYABLE_CHAINS_MAP,
symbol: CurrencySymbol,
): string => {
// since coinbasePayCurrencies is going to be extended to include all tokens supported
// we now default to nativeCurrency instead of the 2 previous tokens + eth that we had before
const { nativeCurrency } = BUYABLE_CHAINS_MAP[chainId];
@ -146,15 +165,25 @@ const createCoinbasePayUrl = (walletAddress, chainId, symbol) => {
/**
* Gives the caller a url at which the user can acquire eth, depending on the network they are in
*
* @param {object} opts - Options required to determine the correct url
* @param {string} opts.chainId - The chainId for which to return a url
* @param {string} opts.address - The address the bought ETH should be sent to. Only relevant if chainId === '0x1'.
* @param opts - Options required to determine the correct url
* @param opts.chainId - The chainId for which to return a url
* @param opts.address - The address the bought ETH should be sent to. Only relevant if chainId === '0x1'.
* @param opts.service
* @param {string|undefined} opts.symbol - The symbol of the token to buy. Only relevant if buying a token.
* @returns {string|undefined} The url at which the user can access ETH, while in the given chain. If the passed
* @param opts.symbol - The symbol of the token to buy. Only relevant if buying a token.
* @returns The url at which the user can access ETH, while in the given chain. If the passed
* chainId does not match any of the specified cases, or if no chainId is given, returns undefined.
*/
export default async function getBuyUrl({ chainId, address, service, symbol }) {
export default async function getBuyUrl({
chainId,
address,
service,
symbol,
}: {
chainId: keyof typeof BUYABLE_CHAINS_MAP;
address: string;
service: string;
symbol: CurrencySymbol;
}): Promise<string> {
// default service by network if not specified
if (!service) {
// eslint-disable-next-line no-param-reassign
@ -183,7 +212,7 @@ export default async function getBuyUrl({ chainId, address, service, symbol }) {
}
}
function getDefaultServiceForChain(chainId) {
function getDefaultServiceForChain(chainId: ChainId): string {
switch (chainId) {
case CHAIN_IDS.MAINNET:
return 'wyre';

View File

@ -174,15 +174,19 @@ async function verifyEnglishLocale() {
// and gradually phase out the key based search
const globsToStrictSearch = [
'ui/components/app/metamask-translation/*.js',
'ui/components/app/metamask-translation/*.ts',
'ui/pages/confirmation/templates/*.js',
'ui/pages/confirmation/templates/*.ts',
];
const testGlob = '**/*.test.js';
const javascriptFiles = await glob(
[
'ui/**/*.js',
'ui/**/*.ts',
'shared/**/*.js',
'shared/**/*.ts',
'app/scripts/constants/**/*.js',
'app/scripts/constants/**/*.ts',
],
{
ignore: [...globsToStrictSearch, testGlob],

View File

@ -306,7 +306,7 @@
"labeled-stream-splicer": "^2.0.2",
"localforage": "^1.9.0",
"lodash": "^4.17.21",
"loglevel": "^1.4.1",
"loglevel": "^1.8.1",
"luxon": "^3.2.1",
"nanoid": "^2.1.6",
"nonce-tracker": "^1.0.0",

View File

@ -17,7 +17,8 @@ export type ChainId = typeof CHAIN_IDS[keyof typeof CHAIN_IDS];
* This type is non-exhaustive, and cannot be used for areas where the user
* or dapp may supply their own symbol.
*/
type CurrencySymbol = typeof CURRENCY_SYMBOLS[keyof typeof CURRENCY_SYMBOLS];
export type CurrencySymbol =
typeof CURRENCY_SYMBOLS[keyof typeof CURRENCY_SYMBOLS];
/**
* A type that is a union type for the supported symbols on different onramp providers.
*/
@ -41,7 +42,7 @@ type MoonPayNetworkAbbreviation = 'BSC' | 'CCHAIN' | 'POLYGON';
* MoonPay requires some settings that are configured per network that it is
* enabled on. This type describes those settings.
*/
type MoonPayChainSettings = {
export type MoonPayChainSettings = {
/**
* What should the default onramp currency be, for example 'eth' on 'mainnet'
* This type matches a single SupportedCurrencySymbol or a
@ -77,7 +78,7 @@ type RPCPreferences = {
/**
* An object that describes a network to be used inside of MetaMask
*/
type RPCDefinition = {
export type RPCDefinition = {
/**
* The hex encoded ChainId for the network
*/
@ -104,7 +105,7 @@ type RPCDefinition = {
* Wyre is a fiat onramp provider. We must provide some settings for networks
* that support Wyre.
*/
type WyreChainSettings = {
export type WyreChainSettings = {
/**
* The network name
*/

View File

@ -20,7 +20,7 @@ export const SWAPS_FETCH_ORDER_CONFLICT = 'swaps-fetch-order-conflict';
// in place of the token address that ERC-20 tokens have
const DEFAULT_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000';
interface SwapsTokenObject {
export interface SwapsTokenObject {
/**
* The symbol of token object
*/

View File

@ -143,12 +143,13 @@ const getBaseUrlForNewSwapsApi = (type, chainId) => {
return `${v2ApiBaseUrl}/networks/${chainIdDecimal}`;
};
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);
export const getBaseApi = function (type, chainId) {
const _chainId = TEST_CHAIN_IDS.includes(chainId)
? CHAIN_IDS.MAINNET
: chainId;
const baseUrl = getBaseUrlForNewSwapsApi(type, _chainId);
if (!baseUrl) {
throw new Error(`Swaps API calls are disabled for chainId: ${chainId}`);
throw new Error(`Swaps API calls are disabled for chainId: ${_chainId}`);
}
switch (type) {
case 'trade':

1
shared/modules/declare-modules.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module 'human-standard-token-abi';

View File

@ -49,7 +49,7 @@ import {
isContractAddressValid,
getSwapsLivenessForNetwork,
parseSmartTransactionsError,
stxErrorTypes,
StxErrorTypes,
} from '../../pages/swaps/swaps.util';
import {
addHexes,
@ -206,9 +206,9 @@ const slice = createSlice({
state.customGas.fallBackPrice = action.payload;
},
setCurrentSmartTransactionsError: (state, action) => {
const errorType = Object.values(stxErrorTypes).includes(action.payload)
const errorType = Object.values(StxErrorTypes).includes(action.payload)
? action.payload
: stxErrorTypes.UNAVAILABLE;
: StxErrorTypes.UNAVAILABLE;
state.currentSmartTransactionsError = errorType;
},
setSwapsSTXSubmitLoading: (state, action) => {
@ -554,7 +554,7 @@ const disableStxIfRegularTxInProgress = (dispatch, transactions) => {
for (const transaction of transactions) {
if (IN_PROGRESS_TRANSACTION_STATUSES.includes(transaction.status)) {
dispatch(
setCurrentSmartTransactionsError(stxErrorTypes.REGULAR_TX_IN_PROGRESS),
setCurrentSmartTransactionsError(StxErrorTypes.REGULAR_TX_IN_PROGRESS),
);
break;
}
@ -581,8 +581,8 @@ export const fetchSwapsLivenessAndFeatureFlags = () => {
disableStxIfRegularTxInProgress(dispatch, transactions);
}
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
swapsFeatureFlags,
chainId,
swapsFeatureFlags,
);
} catch (error) {
log.error(
@ -621,8 +621,8 @@ export const fetchQuotesAndSetQuoteState = (
try {
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
swapsFeatureFlags,
chainId,
swapsFeatureFlags,
);
} catch (error) {
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
@ -939,7 +939,7 @@ export const signAndSendSwapsSmartTransaction = ({
if (!fees) {
log.error('"fetchSwapsSmartTransactionFees" failed');
dispatch(setSwapsSTXSubmitLoading(false));
dispatch(setCurrentSmartTransactionsError(stxErrorTypes.UNAVAILABLE));
dispatch(setCurrentSmartTransactionsError(StxErrorTypes.UNAVAILABLE));
return;
}
if (approveTxParams) {
@ -1020,8 +1020,8 @@ export const signAndSendTransactions = (
try {
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
swapsFeatureFlags,
chainId,
swapsFeatureFlags,
);
} catch (error) {
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
@ -1329,7 +1329,7 @@ export function fetchSwapsSmartTransactionFees({
const errorObj = parseSmartTransactionsError(e.message);
if (
fallbackOnNotEnoughFunds ||
errorObj?.error !== stxErrorTypes.NOT_ENOUGH_FUNDS
errorObj?.error !== StxErrorTypes.NOT_ENOUGH_FUNDS
) {
dispatch(setCurrentSmartTransactionsError(errorObj?.error));
}

View File

@ -129,7 +129,7 @@ describe('i18n helper', () => {
);
});
it('should correctly render falsey substitutions', () => {
it('should correctly render falsy substitutions', () => {
const result = t(TEST_KEY_4, [0, -0, '', false, NaN]);
expect(result).toStrictEqual('0 - 0 - - false - NaN');
});

View File

@ -1,25 +1,60 @@
// cross-browser connection to extension i18n API
import React from 'react';
import log from 'loglevel';
import { Json } from '@metamask/utils';
import * as Sentry from '@sentry/browser';
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout';
const fetchWithTimeout = getFetchWithTimeout();
const warned = {};
const missingMessageErrors = {};
const missingSubstitutionErrors = {};
// From app/_locales folders there is a messages.json file such as app/_locales/en, comes with key and translated results
// and we use as t('reject') to get the translated message in the codebase
// and in i18n lib, the translated message is an object (I18NMessage) with message & description -
// message is the string that will replace the translationKey, and that message may contain replacement variables such as $1, $2, etc.
// Description is key describing the usage of the message.
interface I18NMessage {
message: string;
description?: string;
}
// The overall translation file is made of same entries
// translationKey (string) and the I18NMessage as the value.
interface I18NMessageDict {
[translationKey: string]: I18NMessage;
}
// A parameterized type (or generic type) of maps that use the same structure (translationKey) key
interface I18NMessageDictMap<R> {
[translationKey: string]: R;
}
const warned: { [localeCode: string]: I18NMessageDictMap<boolean> } = {};
const missingMessageErrors: I18NMessageDictMap<Error> = {};
const missingSubstitutionErrors: {
[localeCode: string]: I18NMessageDictMap<boolean>;
} = {};
function getHasSubstitutions(
substitutions?: string[],
): substitutions is string[] {
return (substitutions?.length ?? 0) > 0;
}
/**
* Returns a localized message for the given key
*
* @param {string} localeCode - The code for the current locale
* @param {object} localeMessages - The map of messages for the current locale
* @param {string} key - The message key
* @param {string[]} substitutions - A list of message substitution replacements
* @returns {null|string} The localized message
* @param localeCode - The code for the current locale
* @param localeMessages - The map of messages for the current locale
* @param key - The message key
* @param substitutions - A list of message substitution replacements can replace $n in given message
* @returns The localized message
*/
export const getMessage = (localeCode, localeMessages, key, substitutions) => {
export const getMessage = (
localeCode: string,
localeMessages: I18NMessageDict,
key: string,
substitutions?: string[],
): JSX.Element | string | null => {
if (!localeMessages) {
return null;
}
@ -46,23 +81,22 @@ export const getMessage = (localeCode, localeMessages, key, substitutions) => {
}
return null;
}
const entry = localeMessages[key];
let phrase = entry.message;
const hasSubstitutions = Boolean(substitutions && substitutions.length);
const hasSubstitutions = getHasSubstitutions(substitutions);
const hasReactSubstitutions =
hasSubstitutions &&
substitutions.some(
substitutions?.some(
(element) =>
element !== null &&
(typeof element === 'function' || typeof element === 'object'),
);
const entry = localeMessages[key];
const phrase = entry.message;
// perform substitutions
if (hasSubstitutions) {
const parts = phrase.split(/(\$\d)/gu);
const substitutedParts = parts.map((part) => {
const substitutedParts = parts.map((part: string) => {
const subMatch = part.match(/\$(\d)/u);
if (!subMatch) {
return part;
@ -83,20 +117,21 @@ export const getMessage = (localeCode, localeMessages, key, substitutions) => {
log.error(error);
Sentry.captureException(error);
}
return substitutions[substituteIndex];
return substitutions?.[substituteIndex];
});
phrase = hasReactSubstitutions ? (
return hasReactSubstitutions ? (
<span> {substitutedParts} </span>
) : (
substitutedParts.join('')
);
}
return phrase;
};
export async function fetchLocale(localeCode) {
export async function fetchLocale(
localeCode: string,
): Promise<I18NMessageDict> {
try {
const response = await fetchWithTimeout(
`./_locales/${localeCode}/messages.json`,
@ -110,19 +145,21 @@ export async function fetchLocale(localeCode) {
const relativeTimeFormatLocaleData = new Set();
export async function loadRelativeTimeFormatLocaleData(localeCode) {
export async function loadRelativeTimeFormatLocaleData(
localeCode: string,
): Promise<void> {
const languageTag = localeCode.split('_')[0];
if (
Intl.RelativeTimeFormat &&
typeof Intl.RelativeTimeFormat.__addLocaleData === 'function' &&
typeof (Intl.RelativeTimeFormat as any).__addLocaleData === 'function' &&
!relativeTimeFormatLocaleData.has(languageTag)
) {
const localeData = await fetchRelativeTimeFormatData(languageTag);
Intl.RelativeTimeFormat.__addLocaleData(localeData);
(Intl.RelativeTimeFormat as any).__addLocaleData(localeData);
}
}
async function fetchRelativeTimeFormatData(languageTag) {
async function fetchRelativeTimeFormatData(languageTag: string): Promise<Json> {
const response = await fetchWithTimeout(
`./intl/${languageTag}/relative-time-format-data.json`,
);

View File

@ -3,7 +3,10 @@ import {
CHAIN_IDS,
} from '../../../shared/constants/network';
export const formatMoonpaySymbol = (symbol, chainId = CHAIN_IDS.MAINNET) => {
export const formatMoonpaySymbol = (
symbol: string | null,
chainId: keyof typeof BUYABLE_CHAINS_MAP,
): string | null => {
if (!symbol) {
return symbol;
}

View File

@ -11,7 +11,16 @@ describe('txHelper', () => {
c: { metamaskNetworkId, time: 2 },
};
const sorted = txHelper(txs, null, null, metamaskNetworkId, chainId);
const sorted = txHelper(
txs,
null,
null,
null,
null,
null,
metamaskNetworkId,
chainId,
);
expect(sorted[0].time).toStrictEqual(1);
expect(sorted[2].time).toStrictEqual(3);
});

View File

@ -3,15 +3,15 @@ import { transactionMatchesNetwork } from '../../../shared/modules/transaction.u
import { valuesFor } from './util';
export default function txHelper(
unapprovedTxs,
unapprovedMsgs,
personalMsgs,
decryptMsgs,
encryptionPublicKeyMsgs,
typedMessages,
network,
chainId,
) {
unapprovedTxs: Record<string, any> | null,
unapprovedMsgs: Record<string, any> | null,
personalMsgs: Record<string, any> | null,
decryptMsgs: Record<string, any> | null,
encryptionPublicKeyMsgs: Record<string, any> | null,
typedMessages: Record<string, any> | null,
network: string,
chainId: string,
): Record<string, any> {
log.debug('tx-helper called with params:');
log.debug({
unapprovedTxs,

View File

@ -173,6 +173,7 @@ const mapDispatchToProps = (dispatch) => ({
setRecoveryPhraseReminderLastShown: (lastShown) =>
dispatch(setRecoveryPhraseReminderLastShown(lastShown)),
setNewNetworkAdded: (newNetwork) => {
console.log({ newNetwork });
dispatch(setNewNetworkAdded(newNetwork));
},
setNewCollectibleAddedMessage: (message) => {

View File

@ -267,8 +267,8 @@ describe('Swaps Util', () => {
};
expect(
getSwapsLivenessForNetwork(
MOCKS.createFeatureFlagsResponse(),
CHAIN_IDS.LOCALHOST,
MOCKS.createFeatureFlagsResponse(),
),
).toMatchObject(expectedSwapsLiveness);
});
@ -279,8 +279,8 @@ describe('Swaps Util', () => {
};
expect(
getSwapsLivenessForNetwork(
MOCKS.createFeatureFlagsResponse(),
CHAIN_IDS.GOERLI,
MOCKS.createFeatureFlagsResponse(),
),
).toMatchObject(expectedSwapsLiveness);
});
@ -291,8 +291,8 @@ describe('Swaps Util', () => {
};
expect(
getSwapsLivenessForNetwork(
MOCKS.createFeatureFlagsResponse(),
CHAIN_IDS.SEPOLIA,
MOCKS.createFeatureFlagsResponse(),
),
).toMatchObject(expectedSwapsLiveness);
});
@ -303,8 +303,8 @@ describe('Swaps Util', () => {
};
expect(
getSwapsLivenessForNetwork(
MOCKS.createFeatureFlagsResponse(),
CHAIN_IDS.MAINNET,
MOCKS.createFeatureFlagsResponse(),
),
).toMatchObject(expectedSwapsLiveness);
});
@ -316,7 +316,7 @@ describe('Swaps Util', () => {
const swapsFeatureFlags = MOCKS.createFeatureFlagsResponse();
swapsFeatureFlags[ETHEREUM].extensionActive = false;
expect(
getSwapsLivenessForNetwork(swapsFeatureFlags, CHAIN_IDS.MAINNET),
getSwapsLivenessForNetwork(CHAIN_IDS.MAINNET, swapsFeatureFlags),
).toMatchObject(expectedSwapsLiveness);
});
});

View File

@ -1,24 +1,27 @@
import BigNumber from 'bignumber.js';
import { BigNumber } from 'bignumber.js';
import abi from 'human-standard-token-abi';
import { Json } from '@metamask/controller-utils';
import { IndividualTxFees } from '@metamask/smart-transactions-controller/dist/types';
import {
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
ALLOWED_CONTRACT_ADDRESSES,
ETHEREUM,
POLYGON,
BSC,
GOERLI,
AVALANCHE,
OPTIMISM,
ARBITRUM,
AVALANCHE,
BSC,
ETHEREUM,
GOERLI,
OPTIMISM,
POLYGON,
SWAPS_API_V2_BASE_URL,
SWAPS_DEV_API_V2_BASE_URL,
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
SWAPS_CLIENT_ID,
SWAPS_DEV_API_V2_BASE_URL,
SwapsTokenObject,
} from '../../../shared/constants/swaps';
import {
isSwapsDefaultTokenAddress,
isSwapsDefaultTokenSymbol,
} from '../../../shared/modules/swaps.utils';
import { CHAIN_IDS, CURRENCY_SYMBOLS } from '../../../shared/constants/network';
import { CHAIN_IDS } from '../../../shared/constants/network';
import { formatCurrency } from '../../helpers/utils/confirm-tx.util';
import fetchWithCache from '../../../shared/lib/fetch-with-cache';
@ -38,13 +41,20 @@ import {
getValueFromWeiHex,
sumHexes,
} from '../../../shared/modules/conversion.utils';
import { EtherDenomination } from '../../../shared/constants/common';
const CACHE_REFRESH_FIVE_MINUTES = 300000;
const USD_CURRENCY_CODE = 'usd';
const clientIdHeader = { 'X-Client-Id': SWAPS_CLIENT_ID };
const TOKEN_VALIDATORS = [
interface Validator {
property: string;
type: string;
validator: (a: string) => boolean;
}
const TOKEN_VALIDATORS: Validator[] = [
{
property: 'address',
type: 'string',
@ -64,7 +74,7 @@ const TOKEN_VALIDATORS = [
const TOP_ASSET_VALIDATORS = TOKEN_VALIDATORS.slice(0, 2);
const AGGREGATOR_METADATA_VALIDATORS = [
const AGGREGATOR_METADATA_VALIDATORS: Validator[] = [
{
property: 'color',
type: 'string',
@ -82,10 +92,10 @@ const AGGREGATOR_METADATA_VALIDATORS = [
},
];
const isValidDecimalNumber = (string) =>
const isValidDecimalNumber = (string: any): boolean =>
!isNaN(string) && string.match(/^[.0-9]+$/u) && !isNaN(parseFloat(string));
const SWAP_GAS_PRICE_VALIDATOR = [
const SWAP_GAS_PRICE_VALIDATOR: Validator[] = [
{
property: 'SafeGasPrice',
type: 'string',
@ -103,17 +113,22 @@ const SWAP_GAS_PRICE_VALIDATOR = [
},
];
export async function fetchToken(contractAddress, chainId) {
export async function fetchToken(
contractAddress: string,
chainId: any,
): Promise<Json> {
const tokenUrl = getBaseApi('token', chainId);
const token = await fetchWithCache(
return await fetchWithCache(
`${tokenUrl}?address=${contractAddress}`,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
);
return token;
}
export async function fetchTokens(chainId) {
type Token = { symbol: string; address: string };
export async function fetchTokens(
chainId: keyof typeof SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
): Promise<SwapsTokenObject[]> {
const tokensUrl = getBaseApi('tokens', chainId);
const tokens = await fetchWithCache(
tokensUrl,
@ -121,9 +136,10 @@ export async function fetchTokens(chainId) {
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
);
const logError = false;
const filteredTokens = [
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId],
...tokens.filter((token) => {
const tokenObject = SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId] || null;
return [
tokenObject,
...tokens.filter((token: Token) => {
return (
validateData(TOKEN_VALIDATORS, token, tokensUrl, logError) &&
!(
@ -133,17 +149,16 @@ export async function fetchTokens(chainId) {
);
}),
];
return filteredTokens;
}
export async function fetchAggregatorMetadata(chainId) {
export async function fetchAggregatorMetadata(chainId: any): Promise<object> {
const aggregatorMetadataUrl = getBaseApi('aggregatorMetadata', chainId);
const aggregators = await fetchWithCache(
aggregatorMetadataUrl,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
);
const filteredAggregators = {};
const filteredAggregators = {} as any;
for (const aggKey in aggregators) {
if (
validateData(
@ -158,7 +173,7 @@ export async function fetchAggregatorMetadata(chainId) {
return filteredAggregators;
}
export async function fetchTopAssets(chainId) {
export async function fetchTopAssets(chainId: any): Promise<object> {
const topAssetsUrl = getBaseApi('topAssets', chainId);
const response =
(await fetchWithCache(
@ -166,28 +181,30 @@ export async function fetchTopAssets(chainId) {
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: CACHE_REFRESH_FIVE_MINUTES },
)) || [];
const topAssetsMap = response.reduce((_topAssetsMap, asset, index) => {
if (validateData(TOP_ASSET_VALIDATORS, asset, topAssetsUrl)) {
return { ..._topAssetsMap, [asset.address]: { index: String(index) } };
}
return _topAssetsMap;
}, {});
const topAssetsMap = response.reduce(
(_topAssetsMap: any, asset: { address: string }, index: number) => {
if (validateData(TOP_ASSET_VALIDATORS, asset, topAssetsUrl)) {
return { ..._topAssetsMap, [asset.address]: { index: String(index) } };
}
return _topAssetsMap;
},
{},
);
return topAssetsMap;
}
export async function fetchSwapsFeatureFlags() {
export async function fetchSwapsFeatureFlags(): Promise<any> {
const v2ApiBaseUrl = process.env.SWAPS_USE_DEV_APIS
? SWAPS_DEV_API_V2_BASE_URL
: SWAPS_API_V2_BASE_URL;
const response = await fetchWithCache(
return await fetchWithCache(
`${v2ApiBaseUrl}/featureFlags`,
{ method: 'GET', headers: clientIdHeader },
{ cacheRefreshTime: 600000 },
);
return response;
}
export async function fetchTokenPrice(address) {
export async function fetchTokenPrice(address: string): Promise<any> {
const query = `contract_addresses=${address}&vs_currencies=eth`;
const prices = await fetchWithCache(
@ -195,19 +212,28 @@ export async function fetchTokenPrice(address) {
{ method: 'GET' },
{ cacheRefreshTime: 60000 },
);
return prices && prices[address]?.eth;
return prices?.[address]?.eth;
}
export async function fetchTokenBalance(address, userAddress) {
const tokenContract = global.eth.contract(abi).at(address);
export async function fetchTokenBalance(
address: string,
userAddress: string,
): Promise<any> {
const tokenContract = (global as any).eth.contract(abi).at(address);
const tokenBalancePromise = tokenContract
? tokenContract.balanceOf(userAddress)
: Promise.resolve();
const usersToken = await tokenBalancePromise;
return usersToken;
return await tokenBalancePromise;
}
export async function fetchSwapsGasPrices(chainId) {
export async function fetchSwapsGasPrices(chainId: any): Promise<
| any
| {
safeLow: string;
average: string;
fast: string;
}
> {
const gasPricesUrl = getBaseApi('gasPrices', chainId);
const response = await fetchWithCache(
gasPricesUrl,
@ -244,11 +270,18 @@ export const getFeeForSmartTransaction = ({
USDConversionRate,
nativeCurrencySymbol,
feeInWeiDec,
}: {
chainId: keyof typeof SWAPS_CHAINID_DEFAULT_TOKEN_MAP;
currentCurrency: string;
conversionRate: number;
USDConversionRate?: number;
nativeCurrencySymbol: string;
feeInWeiDec: number;
}) => {
const feeInWeiHex = decimalToHex(feeInWeiDec);
const ethFee = getValueFromWeiHex({
value: feeInWeiHex,
toDenomination: CURRENCY_SYMBOLS.ETH,
toDenomination: EtherDenomination.ETH,
numberOfDecimals: 5,
});
const rawNetworkFees = getValueFromWeiHex({
@ -270,7 +303,7 @@ export const getFeeForSmartTransaction = ({
}
const formattedNetworkFee = formatCurrency(rawNetworkFees, currentCurrency);
const chainCurrencySymbolToUse =
nativeCurrencySymbol || SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId].symbol;
nativeCurrencySymbol || SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.symbol;
return {
feeInUsd,
feeInFiat: formattedNetworkFee,
@ -292,7 +325,27 @@ export function getRenderableNetworkFeesForQuote({
chainId,
nativeCurrencySymbol,
multiLayerL1FeeTotal,
}) {
}: {
tradeGas: string;
approveGas: string;
gasPrice: string;
currentCurrency: string;
conversionRate: number;
USDConversionRate?: number;
tradeValue: number;
sourceSymbol: string;
sourceAmount: number;
chainId: keyof typeof SWAPS_CHAINID_DEFAULT_TOKEN_MAP;
nativeCurrencySymbol?: string;
multiLayerL1FeeTotal: string | null;
}): {
rawNetworkFees: string | number | BigNumber;
feeInUsd: string | number | BigNumber;
rawEthFee: string | number | BigNumber;
feeInFiat: string;
feeInEth: string;
nonGasFee: string;
} {
const totalGasLimitForCalculation = new BigNumber(tradeGas || '0x0', 16)
.plus(approveGas || '0x0', 16)
.toString(16);
@ -314,10 +367,9 @@ export function getRenderableNetworkFeesForQuote({
const totalWeiCost = new BigNumber(gasTotalInWeiHex, 16)
.plus(nonGasFee, 16)
.toString(16);
const ethFee = getValueFromWeiHex({
value: totalWeiCost,
toDenomination: 'ETH',
toDenomination: EtherDenomination.ETH,
numberOfDecimals: 5,
});
const rawNetworkFees = getValueFromWeiHex({
@ -353,7 +405,7 @@ export function getRenderableNetworkFeesForQuote({
};
}
export function quotesToRenderableData(
export function quotesToRenderableData({
quotes,
gasPrice,
conversionRate,
@ -364,7 +416,18 @@ export function quotesToRenderableData(
smartTransactionEstimatedGas,
nativeCurrencySymbol,
multiLayerL1ApprovalFeeTotal,
) {
}: {
quotes: object;
gasPrice: string;
conversionRate: number;
currentCurrency: string;
approveGas: string;
tokenConversionRates: Record<string, any>;
chainId: keyof typeof SWAPS_CHAINID_DEFAULT_TOKEN_MAP;
smartTransactionEstimatedGas: IndividualTxFees;
nativeCurrencySymbol: string;
multiLayerL1ApprovalFeeTotal: string | null;
}): Record<string, any> {
return Object.values(quotes).map((quote) => {
const {
destinationAmount = 0,
@ -426,7 +489,7 @@ export function quotesToRenderableData(
currentCurrency,
conversionRate,
nativeCurrencySymbol,
estimatedFeeInWeiDec: smartTransactionEstimatedGas.feeEstimate,
feeInWeiDec: smartTransactionEstimatedGas.feeEstimate,
}));
}
@ -494,7 +557,7 @@ export function quotesToRenderableData(
});
}
export function formatSwapsValueForDisplay(destinationAmount) {
export function formatSwapsValueForDisplay(destinationAmount: string): string {
let amountToDisplay = toPrecisionWithoutTrailingZeros(destinationAmount, 12);
if (amountToDisplay.match(/e[+-]/u)) {
amountToDisplay = new BigNumber(amountToDisplay).toFixed();
@ -505,30 +568,30 @@ export function formatSwapsValueForDisplay(destinationAmount) {
/**
* Checks whether a contract address is valid before swapping tokens.
*
* @param {string} contractAddress - E.g. "0x881d40237659c251811cec9c364ef91dc08d300c" for mainnet
* @param {string} chainId - The hex encoded chain ID to check
* @returns {boolean} Whether a contract address is valid or not
* @param contractAddress - E.g. "0x881d40237659c251811cec9c364ef91dc08d300c" for mainnet
* @param chainId - The hex encoded chain ID to check
* @returns Whether a contract address is valid or not
*/
export const isContractAddressValid = (
contractAddress,
chainId = CHAIN_IDS.MAINNET,
) => {
contractAddress: string,
chainId: keyof typeof ALLOWED_CONTRACT_ADDRESSES,
): boolean => {
if (!contractAddress || !ALLOWED_CONTRACT_ADDRESSES[chainId]) {
return false;
}
return ALLOWED_CONTRACT_ADDRESSES[chainId].some(
// Sometimes we get a contract address with a few upper-case chars and since addresses are
// case-insensitive, we compare lowercase versions for validity.
(allowedContractAddress) =>
(allowedContractAddress: string) =>
contractAddress.toLowerCase() === allowedContractAddress.toLowerCase(),
);
};
/**
* @param {string} chainId
* @param chainId
* @returns string e.g. ethereum, bsc or polygon
*/
export const getNetworkNameByChainId = (chainId) => {
export const getNetworkNameByChainId = (chainId: string): string => {
switch (chainId) {
case CHAIN_IDS.MAINNET:
return ETHEREUM;
@ -552,11 +615,14 @@ export const getNetworkNameByChainId = (chainId) => {
/**
* It returns info about if Swaps are enabled and if we should use our new APIs for it.
*
* @param {object} swapsFeatureFlags
* @param {string} chainId
* @param chainId
* @param swapsFeatureFlags
* @returns object with 2 items: "swapsFeatureIsLive"
*/
export const getSwapsLivenessForNetwork = (swapsFeatureFlags = {}, chainId) => {
export const getSwapsLivenessForNetwork = (
chainId: any,
swapsFeatureFlags: any = {},
) => {
const networkName = getNetworkNameByChainId(chainId);
// Use old APIs for testnet and Goerli.
if ([CHAIN_IDS.LOCALHOST, CHAIN_IDS.GOERLI].includes(chainId)) {
@ -583,17 +649,19 @@ export const getSwapsLivenessForNetwork = (swapsFeatureFlags = {}, chainId) => {
};
/**
* @param {number} value
* @param value
* @returns number
*/
export const countDecimals = (value) => {
export const countDecimals = (value: any): number => {
if (!value || Math.floor(value) === value) {
return 0;
}
return value.toString().split('.')[1]?.length || 0;
};
export const showRemainingTimeInMinAndSec = (remainingTimeInSec) => {
export const showRemainingTimeInMinAndSec = (
remainingTimeInSec: any,
): string => {
if (!Number.isInteger(remainingTimeInSec)) {
return '0:00';
}
@ -602,25 +670,28 @@ export const showRemainingTimeInMinAndSec = (remainingTimeInSec) => {
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
export const stxErrorTypes = {
UNAVAILABLE: 'unavailable',
NOT_ENOUGH_FUNDS: 'not_enough_funds',
REGULAR_TX_IN_PROGRESS: 'regular_tx_pending',
};
export enum StxErrorTypes {
unavailable = 'unavailable',
notEnoughFunds = 'not_enough_funds',
regularTxPending = 'regular_tx_pending',
}
export const getTranslatedStxErrorMessage = (errorType, t) => {
export const getTranslatedStxErrorMessage = (
errorType: StxErrorTypes,
t: (...args: any[]) => string,
): string => {
switch (errorType) {
case stxErrorTypes.UNAVAILABLE:
case stxErrorTypes.REGULAR_TX_IN_PROGRESS:
case StxErrorTypes.unavailable:
case StxErrorTypes.regularTxPending:
return t('stxErrorUnavailable');
case stxErrorTypes.NOT_ENOUGH_FUNDS:
case StxErrorTypes.notEnoughFunds:
return t('stxErrorNotEnoughFunds');
default:
return t('stxErrorUnavailable');
}
};
export const parseSmartTransactionsError = (errorMessage) => {
export const parseSmartTransactionsError = (errorMessage: string): string => {
const errorJson = errorMessage.slice(12);
return JSON.parse(errorJson.trim());
};

View File

@ -297,20 +297,23 @@ export default function ViewQuote() {
const approveGas = approveTxParams?.gas;
const renderablePopoverData = useMemo(() => {
return quotesToRenderableData(
return quotesToRenderableData({
quotes,
networkAndAccountSupports1559 ? baseAndPriorityFeePerGas : gasPrice,
gasPrice: networkAndAccountSupports1559
? baseAndPriorityFeePerGas
: gasPrice,
conversionRate,
currentCurrency,
approveGas,
memoizedTokenConversionRates,
tokenConversionRates: memoizedTokenConversionRates,
chainId,
smartTransactionsEnabled &&
smartTransactionEstimatedGas:
smartTransactionsEnabled &&
smartTransactionsOptInStatus &&
smartTransactionFees?.tradeTxFees,
nativeCurrencySymbol,
multiLayerL1ApprovalFeeTotal,
);
});
}, [
quotes,
gasPrice,

View File

@ -23360,10 +23360,10 @@ __metadata:
languageName: node
linkType: hard
"loglevel@npm:^1.4.1":
version: 1.6.0
resolution: "loglevel@npm:1.6.0"
checksum: 85e59bde900d8c7ddb6bd8f4ac466ca59fab54239e9bbf99193181c316fe49b067e3ed5945524894404c77db1a3a4d17f5fe272a808f05a8a8914a527628dea4
"loglevel@npm:^1.8.1":
version: 1.8.1
resolution: "loglevel@npm:1.8.1"
checksum: a1a62db40291aaeaef2f612334c49e531bff71cc1d01a2acab689ab80d59e092f852ab164a5aedc1a752fdc46b7b162cb097d8a9eb2cf0b299511106c29af61d
languageName: node
linkType: hard
@ -24305,7 +24305,7 @@ __metadata:
localforage: ^1.9.0
lockfile-lint: ^4.9.6
lodash: ^4.17.21
loglevel: ^1.4.1
loglevel: ^1.8.1
loose-envify: ^1.4.0
luxon: ^3.2.1
madge: ^5.0.1