1
1
import { Contract } from '@ethersproject/contracts' ;
2
- import {
3
- ExternalProvider ,
4
- JsonRpcFetchFunc ,
5
- Web3Provider ,
6
- } from '@ethersproject/providers' ;
2
+ import { Web3Provider } from '@ethersproject/providers' ;
7
3
import { BaseController , StateMetadata } from '@metamask/base-controller' ;
8
- import type { ChainId } from '@metamask/controller-utils' ;
9
4
import { GasFeeState } from '@metamask/gas-fee-controller' ;
10
5
import { TransactionParams } from '@metamask/transaction-controller' ;
11
6
import { captureException } from '@sentry/browser' ;
12
7
import { BigNumber } from 'bignumber.js' ;
13
8
import abi from 'human-standard-token-abi' ;
14
9
import { cloneDeep , mapValues } from 'lodash' ;
10
+ import { NetworkClient , NetworkClientId } from '@metamask/network-controller' ;
11
+ import { Hex } from '@metamask/utils' ;
15
12
import { EtherDenomination } from '../../../../shared/constants/common' ;
16
13
import { GasEstimateTypes } from '../../../../shared/constants/gas' ;
17
14
import {
@@ -71,6 +68,13 @@ import type {
71
68
Trade ,
72
69
} from './swaps.types' ;
73
70
71
+ type Network = {
72
+ client : NetworkClient ;
73
+ clientId : NetworkClientId ;
74
+ chainId : Hex ;
75
+ ethersProvider : Web3Provider ;
76
+ } ;
77
+
74
78
const metadata : StateMetadata < SwapsControllerState > = {
75
79
swapsState : {
76
80
persist : false ,
@@ -103,28 +107,27 @@ export default class SwapsController extends BaseController<
103
107
properties : Record < string , string | boolean | number | null > ;
104
108
} ) => void ;
105
109
106
- #ethersProvider: Web3Provider ;
107
-
108
- #ethersProviderChainId: ChainId ;
109
-
110
110
#indexOfNewestCallInFlight: number ;
111
111
112
112
#pollCount: number ;
113
113
114
114
#pollingTimeout: ReturnType < typeof setTimeout > | null = null ;
115
115
116
- #provider: ExternalProvider | JsonRpcFetchFunc ;
117
-
118
- #getEIP1559GasFeeEstimates: ( ) => Promise < GasFeeState > ;
116
+ #getEIP1559GasFeeEstimates: ( options ?: {
117
+ networkClientId ?: NetworkClientId ;
118
+ shouldUpdateState ?: boolean ;
119
+ } ) => Promise < GasFeeState > ;
119
120
120
121
#getLayer1GasFee: ( params : {
121
122
transactionParams : TransactionParams ;
122
- chainId : ChainId ;
123
+ networkClientId : NetworkClientId ;
123
124
} ) => Promise < string > ;
124
125
126
+ #network: Network | undefined ;
127
+
125
128
private _fetchTradesInfo : (
126
129
fetchParams : FetchTradesInfoParams ,
127
- fetchMetadata : { chainId : ChainId } ,
130
+ fetchMetadata : { chainId : Hex } ,
128
131
) => Promise < {
129
132
[ aggId : string ] : Quote ;
130
133
} > = defaultFetchTradesInfo ;
@@ -267,11 +270,8 @@ export default class SwapsController extends BaseController<
267
270
268
271
this . #getEIP1559GasFeeEstimates = opts . getEIP1559GasFeeEstimates ;
269
272
this . #getLayer1GasFee = opts . getLayer1GasFee ;
270
- this . #ethersProvider = new Web3Provider ( opts . provider ) ;
271
- this . #ethersProviderChainId = this . _getCurrentChainId ( ) ;
272
273
this . #indexOfNewestCallInFlight = 0 ;
273
274
this . #pollCount = 0 ;
274
- this . #provider = opts . provider ;
275
275
276
276
// TODO: this should be private, but since a lot of tests depends on spying on it
277
277
// we cannot enforce privacy 100%
@@ -295,11 +295,11 @@ export default class SwapsController extends BaseController<
295
295
return null ;
296
296
}
297
297
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 ) ;
303
303
}
304
304
305
305
const { quotesPollingLimitEnabled, saveFetchedQuotes } =
@@ -327,8 +327,8 @@ export default class SwapsController extends BaseController<
327
327
}
328
328
329
329
let [ newQuotes ] = await Promise . all ( [
330
- this . _fetchTradesInfo ( fetchParams , { ... fetchParamsMetaData } ) ,
331
- this . _setSwapsNetworkConfig ( ) ,
330
+ this . _fetchTradesInfo ( fetchParams , { chainId : network . chainId } ) ,
331
+ this . _setSwapsNetworkConfig ( network ) ,
332
332
] ) ;
333
333
334
334
const { saveFetchedQuotes : saveFetchedQuotesAfterResponse } =
@@ -349,16 +349,16 @@ export default class SwapsController extends BaseController<
349
349
destinationTokenInfo : fetchParamsMetaData ?. destinationTokenInfo ,
350
350
} ) ) ;
351
351
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 ( ) ;
354
354
355
355
if ( ( isOptimism || isBase ) && Object . values ( newQuotes ) . length > 0 ) {
356
356
await Promise . all (
357
357
Object . values ( newQuotes ) . map ( async ( quote ) => {
358
358
if ( quote . trade ) {
359
359
const multiLayerL1TradeFeeTotal = await this . #getLayer1GasFee( {
360
360
transactionParams : quote . trade ,
361
- chainId ,
361
+ networkClientId : network . clientId ,
362
362
} ) ;
363
363
364
364
quote . multiLayerL1TradeFeeTotal = multiLayerL1TradeFeeTotal ;
@@ -372,13 +372,13 @@ export default class SwapsController extends BaseController<
372
372
373
373
let approvalRequired = false ;
374
374
if (
375
- ! isSwapsDefaultTokenAddress ( fetchParams . sourceToken , chainId ) &&
375
+ ! isSwapsDefaultTokenAddress ( fetchParams . sourceToken , network . chainId ) &&
376
376
Object . values ( newQuotes ) . length
377
377
) {
378
378
const allowance = await this . _getERC20Allowance (
379
379
fetchParams . sourceToken ,
380
380
fetchParams . fromAddress ,
381
- chainId ,
381
+ network ,
382
382
) ;
383
383
const [ firstQuote ] = Object . values ( newQuotes ) ;
384
384
@@ -428,9 +428,9 @@ export default class SwapsController extends BaseController<
428
428
if ( Object . values ( newQuotes ) . length === 0 ) {
429
429
this . setSwapsErrorKey ( QUOTES_NOT_AVAILABLE_ERROR ) ;
430
430
} else {
431
- const topQuoteAndSavings = await this . getTopQuoteWithCalculatedSavings (
432
- newQuotes ,
433
- ) ;
431
+ const topQuoteAndSavings = await this . getTopQuoteWithCalculatedSavings ( {
432
+ quotes : newQuotes ,
433
+ } ) ;
434
434
if ( Array . isArray ( topQuoteAndSavings ) ) {
435
435
topAggId = topQuoteAndSavings [ 0 ] ;
436
436
newQuotes = topQuoteAndSavings [ 1 ] ;
@@ -476,11 +476,27 @@ export default class SwapsController extends BaseController<
476
476
return [ newQuotes , topAggId ] ;
477
477
}
478
478
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
+
482
499
const { marketData } = this . _getTokenRatesState ( ) ;
483
- const chainId = this . _getCurrentChainId ( ) ;
484
500
const tokenConversionRates = marketData ?. [ chainId ] ?? { } ;
485
501
486
502
const { customGasPrice, customMaxPriorityFeePerGas } =
@@ -494,7 +510,7 @@ export default class SwapsController extends BaseController<
494
510
const newQuotes = cloneDeep ( quotes ) ;
495
511
496
512
const { gasFeeEstimates, gasEstimateType } =
497
- await this . #getEIP1559GasFeeEstimates( ) ;
513
+ await this . #getEIP1559GasFeeEstimates( { networkClientId } ) ;
498
514
499
515
let usedGasPrice = '0x0' ;
500
516
@@ -911,9 +927,9 @@ export default class SwapsController extends BaseController<
911
927
} ;
912
928
913
929
// Private Methods
914
- private async _fetchSwapsNetworkConfig ( chainId : ChainId ) {
930
+ private async _fetchSwapsNetworkConfig ( network : Network ) {
915
931
const response = await fetchWithCache ( {
916
- url : getBaseApi ( 'network' , chainId ) ,
932
+ url : getBaseApi ( 'network' , network . chainId ) ,
917
933
fetchOptions : { method : 'GET' } ,
918
934
cacheOptions : { cacheRefreshTime : 600000 } ,
919
935
functionName : '_fetchSwapsNetworkConfig' ,
@@ -983,29 +999,16 @@ export default class SwapsController extends BaseController<
983
999
return newQuotes ;
984
1000
}
985
1001
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
-
999
1002
private async _getERC20Allowance (
1000
1003
contractAddress : string ,
1001
1004
walletAddress : string ,
1002
- chainId : ChainId ,
1005
+ network : Network ,
1003
1006
) {
1004
- const contract = new Contract ( contractAddress , abi , this . # ethersProvider) ;
1007
+ const contract = new Contract ( contractAddress , abi , network . ethersProvider ) ;
1005
1008
return await contract . allowance (
1006
1009
walletAddress ,
1007
1010
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
1009
1012
] ,
1010
1013
) ;
1011
1014
}
@@ -1053,8 +1056,7 @@ export default class SwapsController extends BaseController<
1053
1056
}
1054
1057
1055
1058
// Sets the network config from the MetaSwap API.
1056
- private async _setSwapsNetworkConfig ( ) {
1057
- const chainId = this . _getCurrentChainId ( ) ;
1059
+ private async _setSwapsNetworkConfig ( network : Network ) {
1058
1060
let swapsNetworkConfig : {
1059
1061
quotes : number ;
1060
1062
quotesPrefetching : number ;
@@ -1065,7 +1067,7 @@ export default class SwapsController extends BaseController<
1065
1067
} | null = null ;
1066
1068
1067
1069
try {
1068
- swapsNetworkConfig = await this . _fetchSwapsNetworkConfig ( chainId ) ;
1070
+ swapsNetworkConfig = await this . _fetchSwapsNetworkConfig ( network ) ;
1069
1071
} catch ( e ) {
1070
1072
console . error ( 'Request for Swaps network config failed: ' , e ) ;
1071
1073
}
@@ -1142,4 +1144,25 @@ export default class SwapsController extends BaseController<
1142
1144
} ) ;
1143
1145
} ) ;
1144
1146
}
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
+ }
1145
1168
}
0 commit comments