@toruslabs/ethereum-controllers 4.5.2 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/ethereumControllers.cjs.js +390 -13
  2. package/dist/ethereumControllers.cjs.js.map +1 -1
  3. package/dist/ethereumControllers.esm.js +368 -15
  4. package/dist/ethereumControllers.esm.js.map +1 -1
  5. package/dist/ethereumControllers.umd.min.js +1 -1
  6. package/dist/ethereumControllers.umd.min.js.LICENSE.txt +0 -2
  7. package/dist/ethereumControllers.umd.min.js.map +1 -1
  8. package/dist/types/Message/AddChainController.d.ts +20 -0
  9. package/dist/types/Message/DecryptMessageController.d.ts +2 -2
  10. package/dist/types/Message/EncryptionPublicKeyController.d.ts +2 -2
  11. package/dist/types/Message/MessageController.d.ts +2 -2
  12. package/dist/types/Message/PersonalMessageController.d.ts +2 -2
  13. package/dist/types/Message/SwitchChainController.d.ts +20 -0
  14. package/dist/types/Message/TypedMessageController.d.ts +2 -2
  15. package/dist/types/Message/utils.d.ts +3 -1
  16. package/dist/types/Network/createEthereumMiddleware.d.ts +23 -15
  17. package/dist/types/Preferences/PreferencesController.d.ts +9 -2
  18. package/dist/types/index.d.ts +2 -0
  19. package/dist/types/utils/constants.d.ts +2 -0
  20. package/dist/types/utils/interfaces.d.ts +22 -1
  21. package/package.json +10 -10
  22. package/src/Message/AddChainController.ts +73 -0
  23. package/src/Message/DecryptMessageController.ts +2 -5
  24. package/src/Message/EncryptionPublicKeyController.ts +2 -8
  25. package/src/Message/MessageController.ts +2 -2
  26. package/src/Message/PersonalMessageController.ts +3 -3
  27. package/src/Message/SwitchChainController.ts +74 -0
  28. package/src/Message/TypedMessageController.ts +2 -2
  29. package/src/Message/utils.ts +49 -1
  30. package/src/Network/createEthereumMiddleware.ts +190 -26
  31. package/src/Preferences/PreferencesController.ts +66 -1
  32. package/src/index.ts +2 -0
  33. package/src/utils/constants.ts +2 -0
  34. package/src/utils/interfaces.ts +26 -1
@@ -1,8 +1,16 @@
1
1
  import { addHexPrefix, bytesToHex, isValidAddress, stripHexPrefix } from "@ethereumjs/util";
2
2
  import { EthEncryptedData, TYPED_MESSAGE_SCHEMA, TypedDataV1Field, typedSignatureHash } from "@metamask/eth-sig-util";
3
+ import { isHexString, JsonRpcProvider, toQuantity } from "ethers";
3
4
  import { validate } from "jsonschema";
4
5
 
5
- import { DecryptMessageParams, EncryptionPublicKeyParams, MessageParams, TypedMessageParams } from "../utils/interfaces";
6
+ import {
7
+ AddChainMessageParams,
8
+ DecryptMessageParams,
9
+ EncryptionPublicKeyParams,
10
+ MessageParams,
11
+ SwitchChainMessageParams,
12
+ TypedMessageParams,
13
+ } from "../utils/interfaces";
6
14
 
7
15
  const hexRe = /^[0-9A-Fa-f]+$/gu;
8
16
 
@@ -105,3 +113,43 @@ export function parseDecryptMessageData(data: string): EthEncryptedData {
105
113
  const buffer = Buffer.from(stripped, "hex");
106
114
  return JSON.parse(buffer.toString("utf8")) as EthEncryptedData;
107
115
  }
116
+
117
+ export async function validateAddChainData(data: AddChainMessageParams) {
118
+ const { chainId, rpcUrls, nativeCurrency } = data || {};
119
+
120
+ if (!chainId) {
121
+ throw new Error("Invalid add chain params: please pass chainId in params");
122
+ }
123
+
124
+ if (!isHexString(chainId)) {
125
+ throw new Error("Invalid add chain params: please pass a valid hex chainId in params, for: ex: 0x1");
126
+ }
127
+
128
+ if (!rpcUrls || rpcUrls.length === 0) throw new Error("params.rpcUrls not provided");
129
+ if (!nativeCurrency) throw new Error("params.nativeCurrency not provided");
130
+ const { name, symbol, decimals } = nativeCurrency;
131
+
132
+ if (!name) throw new Error("params.nativeCurrency.name not provided");
133
+ if (!symbol) throw new Error("params.nativeCurrency.symbol not provided");
134
+ if (decimals === undefined) throw new Error("params.nativeCurrency.decimals not provided");
135
+
136
+ const _web3 = new JsonRpcProvider(rpcUrls[0], "any");
137
+ const { chainId: networkChainID } = await _web3.getNetwork();
138
+ if (Number.parseInt(networkChainID.toString()) !== Number.parseInt(chainId, 16)) {
139
+ throw new Error(
140
+ `Provided rpc url's chainId version is not matching with provided chainId, expected: ${toQuantity(networkChainID)}, received: ${chainId}`
141
+ );
142
+ }
143
+ }
144
+
145
+ export function validateSwitchChainData(data: SwitchChainMessageParams) {
146
+ const { chainId } = data || {};
147
+
148
+ if (!chainId) {
149
+ throw new Error("Invalid switch chain params: please pass chainId in params");
150
+ }
151
+
152
+ if (!isHexString(chainId)) {
153
+ throw new Error("Invalid switch chain params: please pass a valid hex chainId in params, for: ex: 0x1");
154
+ }
155
+ }
@@ -1,3 +1,4 @@
1
+ import { SignTypedDataVersion } from "@metamask/eth-sig-util";
1
2
  import { InPageWalletProviderState, PROVIDER_JRPC_METHODS } from "@toruslabs/base-controllers";
2
3
  import {
3
4
  createAsyncMiddleware,
@@ -12,10 +13,12 @@ import {
12
13
 
13
14
  import { METHOD_TYPES, TRANSACTION_ENVELOPE_TYPES } from "../utils/constants";
14
15
  import {
16
+ AddChainMessageParams,
15
17
  BlockParams,
16
18
  EncryptionPublicKeyParams,
17
19
  EthereumTransactionMeta,
18
20
  MessageParams,
21
+ SwitchChainMessageParams,
19
22
  TransactionParams,
20
23
  TransactionRPCMeta,
21
24
  TypedMessageParams,
@@ -30,17 +33,18 @@ export interface IProviderHandlers {
30
33
  // All operations which open popup window or modal should operate with a windowId parameter
31
34
  processTransaction?: (txParams: TransactionParams, req: JRPCRequest<TransactionParams> & UserRequestApprovalParams) => Promise<string>;
32
35
 
33
- processEthSignMessage?: (msgParams: MessageParams, req: JRPCRequest<MessageParams> & UserRequestApprovalParams) => Promise<string>;
34
- processTypedMessage?: (msgParams: TypedMessageParams, req: JRPCRequest<TypedMessageParams> & UserRequestApprovalParams) => Promise<string>;
35
- processTypedMessageV3?: (msgParams: TypedMessageParams, req: JRPCRequest<TypedMessageParams> & UserRequestApprovalParams) => Promise<string>;
36
- processTypedMessageV4?: (msgParams: TypedMessageParams, req: JRPCRequest<TypedMessageParams> & UserRequestApprovalParams) => Promise<string>;
37
- processPersonalMessage?: (msgParams: MessageParams, req: JRPCRequest<MessageParams> & UserRequestApprovalParams) => Promise<string>;
36
+ processEthSignMessage?: (msgParams: MessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
37
+ processTypedMessage?: (msgParams: TypedMessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
38
+ processTypedMessageV3?: (msgParams: TypedMessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
39
+ processTypedMessageV4?: (msgParams: TypedMessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
40
+ processPersonalMessage?: (msgParams: MessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
38
41
 
39
- processEncryptionPublicKey?: (
40
- msgParams: EncryptionPublicKeyParams,
41
- req: JRPCRequest<EncryptionPublicKeyParams> & UserRequestApprovalParams
42
- ) => Promise<string>;
43
- processDecryptMessage?: (msgParams: MessageParams, req: JRPCRequest<MessageParams> & UserRequestApprovalParams) => Promise<string>;
42
+ processEncryptionPublicKey?: (msgParams: EncryptionPublicKeyParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
43
+ processDecryptMessage?: (msgParams: MessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
44
+
45
+ processSwitchEthereumChain?: (msgParams: SwitchChainMessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
46
+
47
+ processAddEthereumChain?: (msgParams: AddChainMessageParams, req: JRPCRequest<unknown> & UserRequestApprovalParams) => Promise<string>;
44
48
 
45
49
  getPendingNonce?: (
46
50
  nonceParams: { address: string; blockReference: string },
@@ -87,14 +91,30 @@ export function createProcessEthSignMessage({
87
91
  processEthSignMessage,
88
92
  }: {
89
93
  processEthSignMessage: IProviderHandlers["processEthSignMessage"];
90
- }): JRPCMiddleware<MessageParams, string> {
94
+ }): JRPCMiddleware<unknown, string> {
91
95
  return createAsyncMiddleware(async (request, response, next) => {
92
96
  const { method } = request;
93
97
  if (method !== METHOD_TYPES.ETH_SIGN) return next();
94
98
 
95
99
  if (!processEthSignMessage) throw new Error("WalletMiddleware - opts.processEthSignMessage not provided");
100
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
101
+
102
+ let msgParams: MessageParams = request.params as MessageParams;
103
+
104
+ if (Array.isArray(request.params)) {
105
+ if (!(request.params.length === 2)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [address, message]`);
96
106
 
97
- response.result = await processEthSignMessage(request.params, request);
107
+ const params = request.params as [string, string];
108
+ const address = params[0];
109
+ const message = params[1];
110
+
111
+ msgParams = {
112
+ from: address,
113
+ data: message,
114
+ };
115
+ }
116
+
117
+ response.result = await processEthSignMessage(msgParams, request);
98
118
  });
99
119
  }
100
120
 
@@ -102,13 +122,31 @@ export function createProcessTypedMessage({
102
122
  processTypedMessage,
103
123
  }: {
104
124
  processTypedMessage: IProviderHandlers["processTypedMessage"];
105
- }): JRPCMiddleware<TypedMessageParams, string> {
125
+ }): JRPCMiddleware<unknown, string> {
106
126
  return createAsyncMiddleware(async (request, response, next) => {
107
127
  const { method } = request;
108
128
  if (method !== METHOD_TYPES.ETH_SIGN_TYPED_DATA) return next();
109
129
 
110
130
  if (!processTypedMessage) throw new Error("WalletMiddleware - opts.processTypedMessage not provided");
111
- response.result = await processTypedMessage(request.params, request);
131
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
132
+
133
+ let msgParams: TypedMessageParams = request.params as TypedMessageParams;
134
+
135
+ if (Array.isArray(request.params)) {
136
+ if (!(request.params.length === 2)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [typedData, address]`);
137
+
138
+ const params = request.params as [Record<string, unknown>[], string];
139
+ const message = params[0];
140
+ const address = params[1];
141
+
142
+ msgParams = {
143
+ from: address,
144
+ data: message,
145
+ version: SignTypedDataVersion.V1,
146
+ };
147
+ }
148
+
149
+ response.result = await processTypedMessage(msgParams, request);
112
150
  });
113
151
  }
114
152
 
@@ -116,14 +154,31 @@ export function createProcessTypedMessageV3({
116
154
  processTypedMessageV3,
117
155
  }: {
118
156
  processTypedMessageV3: IProviderHandlers["processTypedMessageV3"];
119
- }): JRPCMiddleware<TypedMessageParams, string> {
157
+ }): JRPCMiddleware<unknown, string> {
120
158
  return createAsyncMiddleware(async (request, response, next) => {
121
159
  const { method } = request;
122
160
  if (method !== METHOD_TYPES.ETH_SIGN_TYPED_DATA_V3) return next();
123
161
 
124
162
  if (!processTypedMessageV3) throw new Error("WalletMiddleware - opts.processTypedMessageV3 is not provided");
163
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
164
+
165
+ let msgParams: TypedMessageParams = request.params as TypedMessageParams;
166
+
167
+ if (Array.isArray(request.params)) {
168
+ if (!(request.params.length === 2)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [address, typedData]`);
169
+
170
+ const params = request.params as [string, string];
171
+ const address = params[0];
172
+ const message = JSON.parse(params[1]);
125
173
 
126
- response.result = await processTypedMessageV3(request.params, request);
174
+ msgParams = {
175
+ from: address,
176
+ data: message,
177
+ version: SignTypedDataVersion.V3,
178
+ };
179
+ }
180
+
181
+ response.result = await processTypedMessageV3(msgParams, request);
127
182
  });
128
183
  }
129
184
 
@@ -131,14 +186,31 @@ export function createProcessTypedMessageV4({
131
186
  processTypedMessageV4,
132
187
  }: {
133
188
  processTypedMessageV4: IProviderHandlers["processTypedMessageV4"];
134
- }): JRPCMiddleware<TypedMessageParams, string> {
189
+ }): JRPCMiddleware<unknown, string> {
135
190
  return createAsyncMiddleware(async (request, response, next) => {
136
191
  const { method } = request;
137
192
  if (method !== METHOD_TYPES.ETH_SIGN_TYPED_DATA_V4) return next();
138
193
 
139
194
  if (!processTypedMessageV4) throw new Error("WalletMiddleware - opts.processTypedMessageV4 is not provided");
195
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
196
+
197
+ let msgParams: TypedMessageParams = request.params as TypedMessageParams;
198
+
199
+ if (Array.isArray(request.params)) {
200
+ if (!(request.params.length === 2)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [address, typedData]`);
201
+
202
+ const params = request.params as [string, string];
203
+ const address = params[0];
204
+ const message = JSON.parse(params[1]);
140
205
 
141
- response.result = await processTypedMessageV4(request.params, request);
206
+ msgParams = {
207
+ from: address,
208
+ data: message,
209
+ version: SignTypedDataVersion.V4,
210
+ };
211
+ }
212
+
213
+ response.result = await processTypedMessageV4(msgParams, request);
142
214
  });
143
215
  }
144
216
 
@@ -146,14 +218,30 @@ export function createProcessPersonalMessage({
146
218
  processPersonalMessage,
147
219
  }: {
148
220
  processPersonalMessage: IProviderHandlers["processPersonalMessage"];
149
- }): JRPCMiddleware<MessageParams, string> {
221
+ }): JRPCMiddleware<unknown, string> {
150
222
  return createAsyncMiddleware(async (request, response, next) => {
151
223
  const { method } = request;
152
224
  if (method !== METHOD_TYPES.PERSONAL_SIGN) return next();
153
225
 
154
226
  if (!processPersonalMessage) throw new Error("WalletMiddleware - opts.processPersonalMessage is not provided");
227
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
228
+
229
+ let msgParams: MessageParams = request.params as MessageParams;
230
+
231
+ if (Array.isArray(request.params)) {
232
+ if (!(request.params.length >= 2)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [message, address]`);
155
233
 
156
- response.result = await processPersonalMessage(request.params, request);
234
+ const params = request.params as [string, string];
235
+ const message = params[0];
236
+ const address = params[1];
237
+
238
+ msgParams = {
239
+ from: address,
240
+ data: message,
241
+ };
242
+ }
243
+
244
+ response.result = await processPersonalMessage(msgParams, request);
157
245
  });
158
246
  }
159
247
 
@@ -230,14 +318,25 @@ export function createProcessEncryptionPublicKeyMiddleware({
230
318
  processEncryptionPublicKey,
231
319
  }: {
232
320
  processEncryptionPublicKey: IProviderHandlers["processEncryptionPublicKey"];
233
- }): JRPCMiddleware<EncryptionPublicKeyParams, string> {
321
+ }): JRPCMiddleware<unknown, string> {
234
322
  return createAsyncMiddleware(async (request, response, next) => {
235
- const { params, method } = request;
323
+ const { method } = request;
236
324
  if (method !== METHOD_TYPES.ETH_GET_ENCRYPTION_PUBLIC_KEY) return next();
237
325
 
238
326
  if (!processEncryptionPublicKey) throw new Error("WalletMiddleware - opts.processEncryptionPublicKey not provided");
239
327
 
240
- response.result = await processEncryptionPublicKey(params, request);
328
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
329
+
330
+ let msgParams: EncryptionPublicKeyParams = request.params as EncryptionPublicKeyParams;
331
+
332
+ if (Array.isArray(request.params)) {
333
+ if (!(request.params.length === 1)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [address]`);
334
+
335
+ const [address] = request.params as [string];
336
+ msgParams = { data: address, from: address };
337
+ }
338
+
339
+ response.result = await processEncryptionPublicKey(msgParams, request);
241
340
  });
242
341
  }
243
342
 
@@ -245,14 +344,75 @@ export function createProcessDecryptMessageMiddleware({
245
344
  processDecryptMessage,
246
345
  }: {
247
346
  processDecryptMessage: IProviderHandlers["processDecryptMessage"];
248
- }): JRPCMiddleware<MessageParams, string> {
347
+ }): JRPCMiddleware<unknown, string> {
249
348
  return createAsyncMiddleware(async (request, response, next) => {
250
- const { params, method } = request;
349
+ const { method } = request;
251
350
  if (method !== METHOD_TYPES.ETH_DECRYPT) return next();
252
351
 
253
352
  if (!processDecryptMessage) throw new Error("WalletMiddleware - opts.processDecryptMessage not provided");
254
353
 
255
- response.result = await processDecryptMessage(params, request);
354
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
355
+
356
+ let msgParams: MessageParams = request.params as MessageParams;
357
+
358
+ if (Array.isArray(request.params)) {
359
+ if (!(request.params.length === 2)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [message, address]`);
360
+
361
+ const [message, address] = request.params as [string, string];
362
+ msgParams = { data: message, from: address };
363
+ }
364
+
365
+ response.result = await processDecryptMessage(msgParams, request);
366
+ });
367
+ }
368
+
369
+ export function createProcessSwitchEthereumChain({
370
+ processSwitchEthereumChain,
371
+ }: {
372
+ processSwitchEthereumChain: IProviderHandlers["processSwitchEthereumChain"];
373
+ }): JRPCMiddleware<unknown, string> {
374
+ return createAsyncMiddleware(async (request, response, next) => {
375
+ const { method } = request;
376
+ if (method !== METHOD_TYPES.SWITCH_CHAIN) return next();
377
+
378
+ if (!processSwitchEthereumChain) throw new Error("WalletMiddleware - opts.processSwitchEthereumChain not provided");
379
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
380
+
381
+ let msgParams: SwitchChainMessageParams = request.params as SwitchChainMessageParams;
382
+
383
+ if (Array.isArray(request.params)) {
384
+ if (!(request.params.length === 1)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [data]`);
385
+
386
+ const [message] = request.params as [SwitchChainMessageParams];
387
+ msgParams = message;
388
+ }
389
+
390
+ response.result = await processSwitchEthereumChain(msgParams, request);
391
+ });
392
+ }
393
+
394
+ export function createProcessAddEthereumChain({
395
+ processAddEthereumChain,
396
+ }: {
397
+ processAddEthereumChain: IProviderHandlers["processAddEthereumChain"];
398
+ }): JRPCMiddleware<unknown, string> {
399
+ return createAsyncMiddleware(async (request, response, next) => {
400
+ const { method } = request;
401
+ if (method !== METHOD_TYPES.ADD_CHAIN) return next();
402
+
403
+ if (!processAddEthereumChain) throw new Error("WalletMiddleware - opts.processAddEthereumChain not provided");
404
+ if (!request?.params) throw new Error("WalletMiddleware - missing params");
405
+
406
+ let msgParams: AddChainMessageParams = request.params as AddChainMessageParams;
407
+
408
+ if (Array.isArray(request.params)) {
409
+ if (!(request.params.length === 1)) throw new Error(`WalletMiddleware - incorrect params for ${method} method. expected [data]`);
410
+
411
+ const [message] = request.params as [AddChainMessageParams];
412
+ msgParams = message;
413
+ }
414
+
415
+ response.result = await processAddEthereumChain(msgParams, request);
256
416
  });
257
417
  }
258
418
 
@@ -287,6 +447,8 @@ export function createEthereumMiddleware(providerHandlers: IProviderHandlers): J
287
447
  getPendingTransactionByHash,
288
448
  processEncryptionPublicKey,
289
449
  processDecryptMessage,
450
+ processSwitchEthereumChain,
451
+ processAddEthereumChain,
290
452
  getProviderState,
291
453
  version,
292
454
  } = providerHandlers;
@@ -307,5 +469,7 @@ export function createEthereumMiddleware(providerHandlers: IProviderHandlers): J
307
469
  createPendingTxMiddleware({ getPendingTransactionByHash }) as JRPCMiddleware<unknown, unknown>,
308
470
  createProcessEncryptionPublicKeyMiddleware({ processEncryptionPublicKey }) as JRPCMiddleware<unknown, unknown>,
309
471
  createProcessDecryptMessageMiddleware({ processDecryptMessage }) as JRPCMiddleware<unknown, unknown>,
472
+ createProcessSwitchEthereumChain({ processSwitchEthereumChain }) as JRPCMiddleware<unknown, unknown>,
473
+ createProcessAddEthereumChain({ processAddEthereumChain }) as JRPCMiddleware<unknown, unknown>,
310
474
  ]);
311
475
  }
@@ -22,10 +22,12 @@ import NetworkController from "../Network/NetworkController";
22
22
  import { SUPPORTED_NETWORKS } from "../utils/constants";
23
23
  import { formatDate, formatPastTx, formatTime, getEthTxStatus } from "../utils/helpers";
24
24
  import type {
25
+ AddChainMessageParams,
25
26
  CustomNetworkPayload,
26
27
  CustomNetworks,
27
28
  CustomNftInfo,
28
29
  CustomTokenInfo,
30
+ EthereumProviderConfig,
29
31
  EthereumUser,
30
32
  ExtendedAddressPreferences,
31
33
  FetchCommonTransaction,
@@ -42,6 +44,7 @@ interface IPreferencesControllerOptions {
42
44
  blockTracker?: PollingBlockTracker;
43
45
  signAuthMessage?: KeyringController["signAuthMessage"];
44
46
  getProviderConfig?: NetworkController["getProviderConfig"];
47
+ setProviderConfig?: NetworkController["setProviderConfig"];
45
48
  }
46
49
 
47
50
  export default class PreferencesController
@@ -54,14 +57,17 @@ export default class PreferencesController
54
57
 
55
58
  private getProviderConfig: NetworkController["getProviderConfig"];
56
59
 
60
+ private setProviderConfig: NetworkController["setProviderConfig"];
61
+
57
62
  private provider: SafeEventEmitterProvider;
58
63
 
59
64
  private blockTracker: PollingBlockTracker;
60
65
 
61
- constructor({ config, state, provider, blockTracker, signAuthMessage, getProviderConfig }: IPreferencesControllerOptions) {
66
+ constructor({ config, state, provider, blockTracker, signAuthMessage, getProviderConfig, setProviderConfig }: IPreferencesControllerOptions) {
62
67
  super({ config, state, defaultPreferences: { formattedPastTransactions: [], fetchedPastTx: [], paymentTx: [] }, signAuthMessage });
63
68
  this.provider = provider;
64
69
  this.getProviderConfig = getProviderConfig;
70
+ this.setProviderConfig = setProviderConfig;
65
71
  this.blockTracker = blockTracker;
66
72
  log.info(this.blockTracker);
67
73
  }
@@ -261,6 +267,36 @@ export default class PreferencesController
261
267
  return this.getAddressState(address)?.customNfts ?? [];
262
268
  }
263
269
 
270
+ public async addChain(network: AddChainMessageParams): Promise<void> {
271
+ const approveChainOptions = this.getChainOptions();
272
+ const providerConfig = approveChainOptions.find((x) => x.chainId === network.chainId);
273
+
274
+ if (providerConfig) {
275
+ throw new Error(`chainId ${network.chainId} already exists`);
276
+ }
277
+
278
+ const newNetwork: CustomNetworkPayload = {
279
+ displayName: network.chainName,
280
+ rpcTarget: network.rpcUrls[0],
281
+ ticker: network.nativeCurrency.symbol,
282
+ chainId: network.chainId,
283
+ blockExplorerUrl: network.blockExplorerUrls[0],
284
+ };
285
+
286
+ const isSuccess = await this.addCustomNetwork({ type: "rpc", network: newNetwork });
287
+ if (!isSuccess) throw new Error("unable to add custom network");
288
+ }
289
+
290
+ switchChain(data: { chainId: string }) {
291
+ const chainOptions = this.getChainOptions();
292
+ const providerConfig = chainOptions.find((x) => x.chainId === data.chainId);
293
+ if (providerConfig) {
294
+ this.setProviderConfig(providerConfig);
295
+ } else {
296
+ throw new Error(`chainId ${data.chainId} is not supported`);
297
+ }
298
+ }
299
+
264
300
  // Custom Network methods
265
301
  public async addCustomNetwork({ type, network }: { type: string; network: CustomNetworkPayload }): Promise<number> {
266
302
  try {
@@ -354,6 +390,35 @@ export default class PreferencesController
354
390
  this.updateState({ paymentTx: accumulator }, address);
355
391
  }
356
392
 
393
+ private getChainOptions(): EthereumProviderConfig[] {
394
+ const { selectedAddress, identities } = this.state;
395
+ const customNetworks = identities[selectedAddress]?.customNetworks ?? [];
396
+
397
+ const custom: EthereumProviderConfig[] = Object.values(customNetworks).reduce((chains, network) => {
398
+ const networkItem = {
399
+ blockExplorerUrl: network.block_explorer_url,
400
+ chainId: network.chain_id,
401
+ displayName: network.network_name,
402
+ logo: "eth.svg",
403
+ rpcTarget: network.rpc_url,
404
+ ticker: network.symbol,
405
+ tickerName: network.symbol.toUpperCase(),
406
+ isCustom: true,
407
+ id: network.id,
408
+ };
409
+ if (Object.keys(SUPPORTED_NETWORKS).includes(networkItem.chainId)) return chains;
410
+ chains.push(networkItem);
411
+ return chains;
412
+ }, []);
413
+
414
+ const supported = Object.values(SUPPORTED_NETWORKS).reduce((chains, network) => {
415
+ chains.push(network);
416
+ return chains;
417
+ }, []);
418
+
419
+ return [...supported, ...custom];
420
+ }
421
+
357
422
  private async calculatePastTx(txs: FetchedTransaction[], address: string) {
358
423
  const pastTx = [];
359
424
  const pendingTx = [];
package/src/index.ts CHANGED
@@ -5,10 +5,12 @@ export { default as GasFeeController } from "./Gas/GasFeeController";
5
5
  export * from "./Gas/IGasFeeController";
6
6
  export { default as KeyringController } from "./Keyring/KeyringController";
7
7
  export * from "./Message/AbstractMessageController";
8
+ export * from "./Message/AddChainController";
8
9
  export * from "./Message/DecryptMessageController";
9
10
  export * from "./Message/EncryptionPublicKeyController";
10
11
  export * from "./Message/MessageController";
11
12
  export * from "./Message/PersonalMessageController";
13
+ export * from "./Message/SwitchChainController";
12
14
  export * from "./Message/TypedMessageController";
13
15
  export * from "./Message/utils";
14
16
  export * from "./Network/createEthereumMiddleware";
@@ -193,6 +193,8 @@ export const METHOD_TYPES = {
193
193
  ETH_GET_BLOCK_BY_HASH: "eth_getBlockByHash",
194
194
  ETH_GET_CODE: "eth_getCode",
195
195
  ETH_GET_GAS_PRICE: "eth_gasPrice",
196
+ SWITCH_CHAIN: "wallet_switchEthereumChain",
197
+ ADD_CHAIN: "wallet_addEthereumChain",
196
198
  } as const;
197
199
 
198
200
  export const TRANSACTION_ENVELOPE_TYPES = {
@@ -73,7 +73,7 @@ export interface BaseRequestParams {
73
73
  /**
74
74
  * Unique id for each request
75
75
  */
76
- id: string;
76
+ id?: string;
77
77
  /**
78
78
  * Address to send this transaction from.
79
79
  */
@@ -115,6 +115,31 @@ export type SignTypedDataMessageV3V4 = {
115
115
  message: unknown;
116
116
  };
117
117
 
118
+ export interface SwitchChainMessageParams extends BaseRequestParams {
119
+ chainId: string;
120
+ displayName?: string;
121
+ }
122
+
123
+ export interface SwitchChainMessage extends AbstractMessage {
124
+ messageParams: SwitchChainMessageParams;
125
+ }
126
+
127
+ export interface AddChainMessageParams extends BaseRequestParams {
128
+ chainId: string;
129
+ chainName: string;
130
+ nativeCurrency: {
131
+ name: string;
132
+ symbol: string;
133
+ decimals: number;
134
+ };
135
+ rpcUrls: string[];
136
+ blockExplorerUrls?: string[];
137
+ }
138
+
139
+ export interface AddChainMessage extends AbstractMessage {
140
+ messageParams: AddChainMessageParams;
141
+ }
142
+
118
143
  export interface TypedMessageParams extends BaseRequestParams {
119
144
  data: Record<string, unknown>[] | string | SignTypedDataMessageV3V4;
120
145
  version?: SignTypedDataVersion;