Skip to content

Commit 373a8c5

Browse files
authored
Merge branch 'develop' into salim/multichain-token-detection-final
2 parents 1804f9e + 08cc205 commit 373a8c5

File tree

16 files changed

+597
-231
lines changed

16 files changed

+597
-231
lines changed

app/scripts/controllers/swaps/index.ts

+82-59
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import { Contract } from '@ethersproject/contracts';
2-
import {
3-
ExternalProvider,
4-
JsonRpcFetchFunc,
5-
Web3Provider,
6-
} from '@ethersproject/providers';
2+
import { Web3Provider } from '@ethersproject/providers';
73
import { BaseController, StateMetadata } from '@metamask/base-controller';
8-
import type { ChainId } from '@metamask/controller-utils';
94
import { GasFeeState } from '@metamask/gas-fee-controller';
105
import { TransactionParams } from '@metamask/transaction-controller';
116
import { captureException } from '@sentry/browser';
127
import { BigNumber } from 'bignumber.js';
138
import abi from 'human-standard-token-abi';
149
import { cloneDeep, mapValues } from 'lodash';
10+
import { NetworkClient, NetworkClientId } from '@metamask/network-controller';
11+
import { Hex } from '@metamask/utils';
1512
import { EtherDenomination } from '../../../../shared/constants/common';
1613
import { GasEstimateTypes } from '../../../../shared/constants/gas';
1714
import {
@@ -71,6 +68,13 @@ import type {
7168
Trade,
7269
} from './swaps.types';
7370

71+
type Network = {
72+
client: NetworkClient;
73+
clientId: NetworkClientId;
74+
chainId: Hex;
75+
ethersProvider: Web3Provider;
76+
};
77+
7478
const metadata: StateMetadata<SwapsControllerState> = {
7579
swapsState: {
7680
persist: false,
@@ -103,28 +107,27 @@ export default class SwapsController extends BaseController<
103107
properties: Record<string, string | boolean | number | null>;
104108
}) => void;
105109

106-
#ethersProvider: Web3Provider;
107-
108-
#ethersProviderChainId: ChainId;
109-
110110
#indexOfNewestCallInFlight: number;
111111

112112
#pollCount: number;
113113

114114
#pollingTimeout: ReturnType<typeof setTimeout> | null = null;
115115

116-
#provider: ExternalProvider | JsonRpcFetchFunc;
117-
118-
#getEIP1559GasFeeEstimates: () => Promise<GasFeeState>;
116+
#getEIP1559GasFeeEstimates: (options?: {
117+
networkClientId?: NetworkClientId;
118+
shouldUpdateState?: boolean;
119+
}) => Promise<GasFeeState>;
119120

120121
#getLayer1GasFee: (params: {
121122
transactionParams: TransactionParams;
122-
chainId: ChainId;
123+
networkClientId: NetworkClientId;
123124
}) => Promise<string>;
124125

126+
#network: Network | undefined;
127+
125128
private _fetchTradesInfo: (
126129
fetchParams: FetchTradesInfoParams,
127-
fetchMetadata: { chainId: ChainId },
130+
fetchMetadata: { chainId: Hex },
128131
) => Promise<{
129132
[aggId: string]: Quote;
130133
}> = defaultFetchTradesInfo;
@@ -267,11 +270,8 @@ export default class SwapsController extends BaseController<
267270

268271
this.#getEIP1559GasFeeEstimates = opts.getEIP1559GasFeeEstimates;
269272
this.#getLayer1GasFee = opts.getLayer1GasFee;
270-
this.#ethersProvider = new Web3Provider(opts.provider);
271-
this.#ethersProviderChainId = this._getCurrentChainId();
272273
this.#indexOfNewestCallInFlight = 0;
273274
this.#pollCount = 0;
274-
this.#provider = opts.provider;
275275

276276
// TODO: this should be private, but since a lot of tests depends on spying on it
277277
// we cannot enforce privacy 100%
@@ -295,11 +295,11 @@ export default class SwapsController extends BaseController<
295295
return null;
296296
}
297297

298-
const { chainId } = fetchParamsMetaData;
299-
300-
if (chainId !== this.#ethersProviderChainId) {
301-
this.#ethersProvider = new Web3Provider(this.#provider);
302-
this.#ethersProviderChainId = chainId;
298+
let network;
299+
if (this.#network?.clientId === fetchParamsMetaData.networkClientId) {
300+
network = this.#network;
301+
} else {
302+
network = this.#setNetwork(fetchParamsMetaData.networkClientId);
303303
}
304304

305305
const { quotesPollingLimitEnabled, saveFetchedQuotes } =
@@ -327,8 +327,8 @@ export default class SwapsController extends BaseController<
327327
}
328328

329329
let [newQuotes] = await Promise.all([
330-
this._fetchTradesInfo(fetchParams, { ...fetchParamsMetaData }),
331-
this._setSwapsNetworkConfig(),
330+
this._fetchTradesInfo(fetchParams, { chainId: network.chainId }),
331+
this._setSwapsNetworkConfig(network),
332332
]);
333333

334334
const { saveFetchedQuotes: saveFetchedQuotesAfterResponse } =
@@ -349,16 +349,16 @@ export default class SwapsController extends BaseController<
349349
destinationTokenInfo: fetchParamsMetaData?.destinationTokenInfo,
350350
}));
351351

352-
const isOptimism = chainId === CHAIN_IDS.OPTIMISM.toString();
353-
const isBase = chainId === CHAIN_IDS.BASE.toString();
352+
const isOptimism = network.chainId === CHAIN_IDS.OPTIMISM.toString();
353+
const isBase = network.chainId === CHAIN_IDS.BASE.toString();
354354

355355
if ((isOptimism || isBase) && Object.values(newQuotes).length > 0) {
356356
await Promise.all(
357357
Object.values(newQuotes).map(async (quote) => {
358358
if (quote.trade) {
359359
const multiLayerL1TradeFeeTotal = await this.#getLayer1GasFee({
360360
transactionParams: quote.trade,
361-
chainId,
361+
networkClientId: network.clientId,
362362
});
363363

364364
quote.multiLayerL1TradeFeeTotal = multiLayerL1TradeFeeTotal;
@@ -372,13 +372,13 @@ export default class SwapsController extends BaseController<
372372

373373
let approvalRequired = false;
374374
if (
375-
!isSwapsDefaultTokenAddress(fetchParams.sourceToken, chainId) &&
375+
!isSwapsDefaultTokenAddress(fetchParams.sourceToken, network.chainId) &&
376376
Object.values(newQuotes).length
377377
) {
378378
const allowance = await this._getERC20Allowance(
379379
fetchParams.sourceToken,
380380
fetchParams.fromAddress,
381-
chainId,
381+
network,
382382
);
383383
const [firstQuote] = Object.values(newQuotes);
384384

@@ -428,9 +428,9 @@ export default class SwapsController extends BaseController<
428428
if (Object.values(newQuotes).length === 0) {
429429
this.setSwapsErrorKey(QUOTES_NOT_AVAILABLE_ERROR);
430430
} else {
431-
const topQuoteAndSavings = await this.getTopQuoteWithCalculatedSavings(
432-
newQuotes,
433-
);
431+
const topQuoteAndSavings = await this.getTopQuoteWithCalculatedSavings({
432+
quotes: newQuotes,
433+
});
434434
if (Array.isArray(topQuoteAndSavings)) {
435435
topAggId = topQuoteAndSavings[0];
436436
newQuotes = topQuoteAndSavings[1];
@@ -476,11 +476,27 @@ export default class SwapsController extends BaseController<
476476
return [newQuotes, topAggId];
477477
}
478478

479-
public async getTopQuoteWithCalculatedSavings(
480-
quotes: Record<string, Quote> = {},
481-
): Promise<[string | null, Record<string, Quote>] | Record<string, never>> {
479+
public async getTopQuoteWithCalculatedSavings({
480+
quotes,
481+
networkClientId,
482+
}: {
483+
quotes: Record<string, Quote>;
484+
networkClientId?: NetworkClientId;
485+
}): Promise<[string | null, Record<string, Quote>] | Record<string, never>> {
486+
let chainId;
487+
if (networkClientId) {
488+
const networkClient = this.messagingSystem.call(
489+
'NetworkController:getNetworkClientById',
490+
networkClientId,
491+
);
492+
chainId = networkClient.configuration.chainId;
493+
} else if (this.#network === undefined) {
494+
throw new Error('There is no network set');
495+
} else {
496+
chainId = this.#network.chainId;
497+
}
498+
482499
const { marketData } = this._getTokenRatesState();
483-
const chainId = this._getCurrentChainId();
484500
const tokenConversionRates = marketData?.[chainId] ?? {};
485501

486502
const { customGasPrice, customMaxPriorityFeePerGas } =
@@ -494,7 +510,7 @@ export default class SwapsController extends BaseController<
494510
const newQuotes = cloneDeep(quotes);
495511

496512
const { gasFeeEstimates, gasEstimateType } =
497-
await this.#getEIP1559GasFeeEstimates();
513+
await this.#getEIP1559GasFeeEstimates({ networkClientId });
498514

499515
let usedGasPrice = '0x0';
500516

@@ -911,9 +927,9 @@ export default class SwapsController extends BaseController<
911927
};
912928

913929
// Private Methods
914-
private async _fetchSwapsNetworkConfig(chainId: ChainId) {
930+
private async _fetchSwapsNetworkConfig(network: Network) {
915931
const response = await fetchWithCache({
916-
url: getBaseApi('network', chainId),
932+
url: getBaseApi('network', network.chainId),
917933
fetchOptions: { method: 'GET' },
918934
cacheOptions: { cacheRefreshTime: 600000 },
919935
functionName: '_fetchSwapsNetworkConfig',
@@ -983,29 +999,16 @@ export default class SwapsController extends BaseController<
983999
return newQuotes;
9841000
}
9851001

986-
private _getCurrentChainId(): ChainId {
987-
const { selectedNetworkClientId } = this.messagingSystem.call(
988-
'NetworkController:getState',
989-
);
990-
const {
991-
configuration: { chainId },
992-
} = this.messagingSystem.call(
993-
'NetworkController:getNetworkClientById',
994-
selectedNetworkClientId,
995-
);
996-
return chainId as ChainId;
997-
}
998-
9991002
private async _getERC20Allowance(
10001003
contractAddress: string,
10011004
walletAddress: string,
1002-
chainId: ChainId,
1005+
network: Network,
10031006
) {
1004-
const contract = new Contract(contractAddress, abi, this.#ethersProvider);
1007+
const contract = new Contract(contractAddress, abi, network.ethersProvider);
10051008
return await contract.allowance(
10061009
walletAddress,
10071010
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP[
1008-
chainId as keyof typeof SWAPS_CHAINID_CONTRACT_ADDRESS_MAP
1011+
network.chainId as keyof typeof SWAPS_CHAINID_CONTRACT_ADDRESS_MAP
10091012
],
10101013
);
10111014
}
@@ -1053,8 +1056,7 @@ export default class SwapsController extends BaseController<
10531056
}
10541057

10551058
// Sets the network config from the MetaSwap API.
1056-
private async _setSwapsNetworkConfig() {
1057-
const chainId = this._getCurrentChainId();
1059+
private async _setSwapsNetworkConfig(network: Network) {
10581060
let swapsNetworkConfig: {
10591061
quotes: number;
10601062
quotesPrefetching: number;
@@ -1065,7 +1067,7 @@ export default class SwapsController extends BaseController<
10651067
} | null = null;
10661068

10671069
try {
1068-
swapsNetworkConfig = await this._fetchSwapsNetworkConfig(chainId);
1070+
swapsNetworkConfig = await this._fetchSwapsNetworkConfig(network);
10691071
} catch (e) {
10701072
console.error('Request for Swaps network config failed: ', e);
10711073
}
@@ -1142,4 +1144,25 @@ export default class SwapsController extends BaseController<
11421144
});
11431145
});
11441146
}
1147+
1148+
#setNetwork(networkClientId: NetworkClientId) {
1149+
const networkClient = this.messagingSystem.call(
1150+
'NetworkController:getNetworkClientById',
1151+
networkClientId,
1152+
);
1153+
const { chainId } = networkClient.configuration;
1154+
// Web3Provider (via JsonRpcProvider) creates two extra network requests, so
1155+
// we cache the object so that we can reuse it for subsequent contract
1156+
// interactions for the same network
1157+
const ethersProvider = new Web3Provider(networkClient.provider);
1158+
1159+
const network = {
1160+
client: networkClient,
1161+
clientId: networkClientId,
1162+
chainId,
1163+
ethersProvider,
1164+
};
1165+
this.#network = network;
1166+
return network;
1167+
}
11451168
}

0 commit comments

Comments
 (0)