@etherplay/connect 0.0.9 → 0.0.11

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.
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type {AlchemyMechanism, OriginAccount} from '@etherplay/alchemy';
2
2
  import {writable} from 'svelte/store';
3
3
  import {createPopupLauncher, type PopupPromise} from './popup.js';
4
- import type {EIP1193WindowWalletProvider} from 'eip-1193';
4
+ import type {EIP1193ChainId, EIP1193WindowWalletProvider} from 'eip-1193';
5
5
  import {
6
6
  fromEntropyKeyToMnemonic,
7
7
  fromMnemonicToFirstAccount,
@@ -10,6 +10,7 @@ import {
10
10
  originPublicKeyPublicationMessage,
11
11
  } from '@etherplay/alchemy';
12
12
  import {hashMessage} from './utils.js';
13
+ import {createProvider} from './provider.js';
13
14
 
14
15
  export {fromEntropyKeyToMnemonic, originPublicKeyPublicationMessage, originKeyMessage};
15
16
  export type {OriginAccount};
@@ -40,50 +41,82 @@ export type Connection = {
40
41
  | {
41
42
  step: 'Idle';
42
43
  loading: boolean;
44
+ wallet: undefined;
43
45
  }
44
46
  // It can then end up in MechanismToChoose if no specific connection mechanism was chosen upon clicking "connect"
45
47
  | {
46
48
  step: 'MechanismToChoose';
49
+ wallet: undefined;
47
50
  }
48
- // if a social/email login mechanism was chose, a popup will be launched
51
+ // if a social/email login mechanism was chosen, a popup will be launched
49
52
  // popupClosed can be true and this means the popup has been closed and the user has to cancel the process to continue further
50
53
  | {
51
54
  step: 'PopupLaunched';
55
+ wallet: undefined;
52
56
  popupClosed: boolean;
53
57
  mechanism: AlchemyMechanism;
54
58
  }
55
59
  // If the user has chosen to use web3-wallet there might be multi-choice for it
56
60
  | {
57
61
  step: 'WalletToChoose';
62
+ wallet: undefined;
58
63
  mechanism: WalletMechanism<undefined, undefined>;
59
64
  }
60
65
  // Once a user has chosen a wallet, the system will try to connect to it
61
66
  | {
62
67
  step: 'WaitingForWalletConnection';
68
+ wallet: undefined;
63
69
  mechanism: WalletMechanism<string, undefined>;
64
70
  }
65
71
  // Once the wallet is connected, the system will need a signature
66
72
  // this state represent the fact and require another user interaction to request the signature
67
73
  | {
68
- step: 'NeedWalletSignature';
74
+ step: 'WalletConnected';
69
75
  mechanism: WalletMechanism<string, `0x${string}`>;
76
+ wallet: {
77
+ provider: EIP1193WindowWalletProvider;
78
+ locked: boolean;
79
+ accountChanged?: `0x${string}`;
80
+ chainId: string;
81
+ invalidChainId: boolean;
82
+ switchingChain: 'addingChain' | 'switchingChain' | false;
83
+ };
70
84
  }
71
85
  // This state is triggered once the signature is requested, the user will have to confirm with its wallet
72
86
  | {
73
87
  step: 'WaitingForSignature';
74
88
  mechanism: WalletMechanism<string, `0x${string}`>;
89
+ wallet: {
90
+ provider: EIP1193WindowWalletProvider;
91
+ locked: boolean;
92
+ accountChanged?: `0x${string}`;
93
+ chainId: string;
94
+ invalidChainId: boolean;
95
+ switchingChain: 'addingChain' | 'switchingChain' | false;
96
+ };
75
97
  }
76
98
  // Finally the user is fully signed in
77
- // walletAccountChanged if set, represent the fact that the user has changed its web3-wallet accounnt.
99
+ // wallet?.accountChanged if set, represent the fact that the user has changed its web3-wallet accounnt.
100
+ // wallet?.invalidChainId if set, represent the fact that the wallet is connected to a different chain.
101
+ // wallet?.switchingChain if set, represent the fact that the user is currently switching chain.
78
102
  // a notification could be shown to the user so that he can switch the app to use that other account.
79
103
  | {
80
104
  step: 'SignedIn';
81
- mechanism: FullfilledMechanism;
105
+ mechanism: AlchemyMechanism;
82
106
  account: OriginAccount;
83
- wallet?: {
107
+ wallet: undefined;
108
+ }
109
+ | {
110
+ step: 'SignedIn';
111
+ mechanism: WalletMechanism<string, `0x${string}`>;
112
+ account: OriginAccount;
113
+ wallet: {
84
114
  provider: EIP1193WindowWalletProvider;
115
+ locked: boolean;
85
116
  accountChanged?: `0x${string}`;
86
117
  chainId: string;
118
+ invalidChainId: boolean;
119
+ switchingChain: 'addingChain' | 'switchingChain' | false;
87
120
  };
88
121
  }
89
122
  );
@@ -106,13 +139,26 @@ export interface EIP6963AnnounceProviderEvent extends CustomEvent {
106
139
  }
107
140
 
108
141
  const storageAccountKey = '__origin_account';
109
- export function createConnection(settings: {walletHost: string; autoConnect?: boolean}) {
142
+ export function createConnection(settings: {
143
+ walletHost: string;
144
+ autoConnect?: boolean;
145
+ requestSignatureAutomaticallyIfPossible?: boolean;
146
+ node: {url: string; chainId: string; prioritizeWalletProvider?: boolean; requestsPerSecond?: number};
147
+ }) {
148
+ const alwaysOnChainId = settings.node.chainId;
149
+ const alwaysOnProvider = createProvider({
150
+ endpoint: settings.node.url,
151
+ chainId: settings.node.chainId,
152
+ prioritizeWalletProvider: settings.node.prioritizeWalletProvider,
153
+ requestsPerSecond: settings.node.requestsPerSecond,
154
+ });
110
155
  let autoConnect = true;
111
156
  if (typeof settings.autoConnect !== 'undefined') {
112
157
  autoConnect = settings.autoConnect;
113
158
  }
159
+ const requestSignatureAutomaticallyIfPossible = settings.requestSignatureAutomaticallyIfPossible || false;
114
160
 
115
- let $connection: Connection = {step: 'Idle', loading: true, wallets: []};
161
+ let $connection: Connection = {step: 'Idle', loading: true, wallet: undefined, wallets: []};
116
162
  const _store = writable<Connection>($connection);
117
163
  function set(connection: Connection) {
118
164
  $connection = connection;
@@ -182,53 +228,60 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
182
228
  const existingAccount = getOriginAccount();
183
229
  if (existingAccount) {
184
230
  if (existingAccount.signer) {
185
- if (existingAccount.mechanismUsed.type == 'wallet') {
186
- const walletMechanism = existingAccount.mechanismUsed as WalletMechanism<string, `0x${string}`>;
231
+ const mechanismUsed = existingAccount.mechanismUsed as
232
+ | AlchemyMechanism
233
+ | WalletMechanism<string, `0x${string}`>;
234
+ if (mechanismUsed.type == 'wallet') {
235
+ const walletMechanism = mechanismUsed as WalletMechanism<string, `0x${string}`>;
187
236
  waitForWallet(walletMechanism.name)
188
237
  .then(async (walletDetails: EIP6963ProviderDetail) => {
189
238
  const walletProvider = walletDetails.provider;
190
239
  const chainIdAsHex = await walletProvider.request({method: 'eth_chainId'});
191
240
  const chainId = Number(chainIdAsHex).toString();
192
241
  _wallet = {provider: walletProvider, chainId};
242
+ alwaysOnProvider.setWalletProvider(walletProvider);
193
243
  watchForChainIdChange(_wallet.provider);
194
244
  set({
195
245
  step: 'SignedIn',
196
246
  account: existingAccount,
197
- mechanism: existingAccount.mechanismUsed as FullfilledMechanism,
247
+ mechanism: walletMechanism,
198
248
  wallets: $connection.wallets,
199
249
  wallet: {
200
250
  provider: walletProvider,
251
+ locked: false, // TODO should fetch eth_account first
201
252
  accountChanged: undefined,
202
253
  chainId,
254
+ invalidChainId: alwaysOnChainId != chainId,
255
+ switchingChain: false,
203
256
  },
204
257
  });
205
258
  walletProvider.request({method: 'eth_accounts'}).then(onAccountChanged);
206
259
  watchForAccountChange(walletProvider);
207
260
  })
208
261
  .catch((err) => {
209
- set({step: 'Idle', loading: false, wallets: $connection.wallets});
262
+ set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
210
263
  });
211
264
  } else {
212
265
  set({
213
266
  step: 'SignedIn',
214
267
  account: existingAccount,
215
- mechanism: existingAccount.mechanismUsed as FullfilledMechanism,
268
+ mechanism: mechanismUsed,
216
269
  wallets: $connection.wallets,
217
270
  wallet: undefined,
218
271
  });
219
272
  }
220
273
  } else {
221
- set({step: 'Idle', loading: false, wallets: $connection.wallets});
274
+ set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
222
275
  }
223
276
  } else {
224
- set({step: 'Idle', loading: false, wallets: $connection.wallets});
277
+ set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
225
278
  }
226
279
  } catch {
227
- set({step: 'Idle', loading: false, wallets: $connection.wallets});
280
+ set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
228
281
  }
229
282
  }
230
283
  } else {
231
- set({step: 'Idle', loading: false, wallets: $connection.wallets});
284
+ set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
232
285
  }
233
286
  fetchWallets();
234
287
 
@@ -249,8 +302,8 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
249
302
  }
250
303
 
251
304
  async function requestSignature() {
252
- if ($connection.step !== 'NeedWalletSignature') {
253
- throw new Error(`invalid step: ${$connection.step}, needs to be NeedWalletSignature`);
305
+ if ($connection.step !== 'WalletConnected') {
306
+ throw new Error(`invalid step: ${$connection.step}, needs to be WalletConnected`);
254
307
  }
255
308
 
256
309
  if (!_wallet) {
@@ -278,7 +331,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
278
331
  // TODO handle rejection (code: 4001 ?)
279
332
  set({
280
333
  ...$connection,
281
- step: 'NeedWalletSignature',
334
+ step: 'WalletConnected',
282
335
  mechanism: {
283
336
  type: 'wallet',
284
337
  name: $connection.mechanism.name,
@@ -318,7 +371,10 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
318
371
  wallet: {
319
372
  chainId,
320
373
  provider: provider,
374
+ locked: false,
321
375
  accountChanged: undefined, // TODO check account list
376
+ invalidChainId: alwaysOnChainId != chainId,
377
+ switchingChain: false,
322
378
  },
323
379
  });
324
380
  if (remember) {
@@ -343,12 +399,18 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
343
399
  if (_wallet) {
344
400
  _wallet.chainId = chainId;
345
401
  }
346
- if ($connection.step === 'SignedIn' && $connection.wallet && $connection.wallet.chainId != chainId) {
402
+ if (
403
+ $connection.step === 'SignedIn' &&
404
+ $connection.mechanism.type === 'wallet' &&
405
+ $connection.wallet &&
406
+ $connection.wallet.chainId != chainId
407
+ ) {
347
408
  set({
348
409
  ...$connection,
349
410
  wallet: {
350
411
  ...$connection.wallet,
351
412
  chainId,
413
+ invalidChainId: alwaysOnChainId != chainId,
352
414
  },
353
415
  });
354
416
  }
@@ -356,6 +418,25 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
356
418
 
357
419
  function onAccountChanged(accounts: `0x${string}`[]) {
358
420
  const accountsFormated = accounts.map((a) => a.toLowerCase()) as `0x${string}`[];
421
+
422
+ if (accountsFormated.length === 0 && $connection.wallet) {
423
+ set({
424
+ ...$connection,
425
+ wallet: {
426
+ ...$connection.wallet,
427
+ locked: true,
428
+ },
429
+ });
430
+ } else if (accountsFormated.length > 0 && $connection.wallet?.locked) {
431
+ set({
432
+ ...$connection,
433
+ wallet: {
434
+ ...$connection.wallet,
435
+ locked: false,
436
+ },
437
+ });
438
+ }
439
+
359
440
  if ($connection.step === 'SignedIn' && $connection.mechanism.type === 'wallet') {
360
441
  // TODO if auto-connect and saved-signature ?
361
442
  // connect(
@@ -364,7 +445,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
364
445
  // address: accounts[0],
365
446
  // name: $connection.mechanism.name
366
447
  // },
367
- // { requireUserConfirmationBeforeSIgnatureRequest: true }
448
+ // { requireUserConfirmationBeforeSignatureRequest: true }
368
449
  // );
369
450
 
370
451
  if ($connection.wallet && accountsFormated.length > 0 && accountsFormated[0] != $connection.account.address) {
@@ -407,7 +488,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
407
488
  async function connect(
408
489
  mechanism?: Mechanism,
409
490
  options?: {
410
- requireUserConfirmationBeforeSIgnatureRequest?: boolean;
491
+ requireUserConfirmationBeforeSignatureRequest?: boolean;
411
492
  doNotStoreLocally?: boolean;
412
493
  requestSignatureRightAway?: boolean;
413
494
  },
@@ -420,6 +501,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
420
501
  const wallet = $connection.wallets.find((v) => v.info.name == walletName || v.info.uuid == walletName);
421
502
  if (wallet) {
422
503
  if (_wallet) {
504
+ alwaysOnProvider.setWalletProvider(undefined);
423
505
  stopatchingForAccountChange(_wallet.provider);
424
506
  stopatchingForChainIdChange(_wallet.provider);
425
507
  }
@@ -433,6 +515,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
433
515
  step: 'WaitingForWalletConnection', // TODO FetchingAccounts
434
516
  mechanism,
435
517
  wallets: $connection.wallets,
518
+ wallet: undefined,
436
519
  });
437
520
  const provider = wallet.provider;
438
521
  const chainIdAsHex = await provider.request({method: 'eth_chainId'});
@@ -441,14 +524,16 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
441
524
  chainId,
442
525
  provider,
443
526
  };
527
+ alwaysOnProvider.setWalletProvider(_wallet.provider);
444
528
  watchForChainIdChange(_wallet.provider);
445
529
  let accounts = await provider.request({method: 'eth_accounts'});
446
530
  accounts = accounts.map((v) => v.toLowerCase()) as `0x${string}`[];
447
531
  if (accounts.length === 0) {
448
532
  set({
449
- step: 'WaitingForWalletConnection',
533
+ step: 'WaitingForWalletConnection', // TODO add another step to unlock ?
450
534
  mechanism,
451
535
  wallets: $connection.wallets,
536
+ wallet: undefined,
452
537
  });
453
538
  accounts = await provider.request({method: 'eth_requestAccounts'});
454
539
  accounts = accounts.map((v) => v.toLowerCase()) as `0x${string}`[];
@@ -456,22 +541,38 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
456
541
  if (options?.requestSignatureRightAway) {
457
542
  watchForAccountChange(_wallet.provider);
458
543
  set({
459
- step: 'NeedWalletSignature',
544
+ step: 'WalletConnected',
460
545
  mechanism: {
461
546
  ...mechanism,
462
547
  address: accounts[0],
463
548
  },
464
549
  wallets: $connection.wallets,
550
+ wallet: {
551
+ provider: _wallet.provider,
552
+ locked: false,
553
+ accountChanged: undefined,
554
+ chainId,
555
+ invalidChainId: alwaysOnChainId != chainId,
556
+ switchingChain: false,
557
+ },
465
558
  });
466
559
  await requestSignature();
467
560
  } else {
468
561
  set({
469
- step: 'NeedWalletSignature',
562
+ step: 'WalletConnected',
470
563
  mechanism: {
471
564
  ...mechanism,
472
565
  address: accounts[0],
473
566
  },
474
567
  wallets: $connection.wallets,
568
+ wallet: {
569
+ provider: _wallet.provider,
570
+ locked: false,
571
+ accountChanged: undefined,
572
+ chainId,
573
+ invalidChainId: alwaysOnChainId != chainId,
574
+ switchingChain: false,
575
+ },
475
576
  });
476
577
  watchForAccountChange(_wallet.provider);
477
578
  }
@@ -479,29 +580,46 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
479
580
  set({
480
581
  step: 'MechanismToChoose',
481
582
  wallets: $connection.wallets,
583
+ wallet: undefined,
482
584
  error: {message: 'could not get any accounts'},
483
585
  });
484
586
  }
485
587
  } else {
486
- if (options?.requireUserConfirmationBeforeSIgnatureRequest) {
588
+ if (!requestSignatureAutomaticallyIfPossible || options?.requireUserConfirmationBeforeSignatureRequest) {
487
589
  set({
488
- step: 'NeedWalletSignature',
590
+ step: 'WalletConnected',
489
591
  mechanism: {
490
592
  ...mechanism,
491
593
  address: accounts[0],
492
594
  },
493
595
  wallets: $connection.wallets,
596
+ wallet: {
597
+ provider: _wallet.provider,
598
+ locked: false,
599
+ accountChanged: undefined,
600
+ chainId,
601
+ invalidChainId: alwaysOnChainId != chainId,
602
+ switchingChain: false,
603
+ },
494
604
  });
495
605
  watchForAccountChange(_wallet.provider);
496
606
  } else {
497
607
  watchForAccountChange(_wallet.provider);
498
608
  set({
499
- step: 'NeedWalletSignature',
609
+ step: 'WalletConnected',
500
610
  mechanism: {
501
611
  ...mechanism,
502
612
  address: accounts[0],
503
613
  },
504
614
  wallets: $connection.wallets,
615
+ wallet: {
616
+ provider: _wallet.provider,
617
+ locked: false,
618
+ accountChanged: undefined,
619
+ chainId,
620
+ invalidChainId: alwaysOnChainId != chainId,
621
+ switchingChain: false,
622
+ },
505
623
  });
506
624
  await requestSignature();
507
625
  }
@@ -511,6 +629,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
511
629
  set({
512
630
  step: 'MechanismToChoose',
513
631
  wallets: $connection.wallets,
632
+ wallet: undefined,
514
633
  error: {message: `failed to get wallet ${walletName}`},
515
634
  });
516
635
  }
@@ -524,6 +643,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
524
643
  set({
525
644
  step: 'WalletToChoose',
526
645
  mechanism: {type: 'wallet'},
646
+ wallet: undefined,
527
647
  wallets: $connection.wallets,
528
648
  });
529
649
  }
@@ -537,6 +657,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
537
657
  popupClosed: false,
538
658
  mechanism,
539
659
  wallets: $connection.wallets,
660
+ wallet: undefined,
540
661
  });
541
662
 
542
663
  const unsubscribe = popup.subscribe(($popup) => {
@@ -564,7 +685,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
564
685
  }
565
686
  } catch (err) {
566
687
  console.log({error: err});
567
- set({step: 'Idle', loading: false, wallets: $connection.wallets});
688
+ set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
568
689
  } finally {
569
690
  unsubscribe();
570
691
  }
@@ -573,6 +694,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
573
694
  set({
574
695
  step: 'MechanismToChoose',
575
696
  wallets: $connection.wallets,
697
+ wallet: undefined,
576
698
  });
577
699
  }
578
700
  }
@@ -580,6 +702,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
580
702
  function disconnect() {
581
703
  deleteOriginAccount();
582
704
  if (_wallet) {
705
+ alwaysOnProvider.setWalletProvider(undefined);
583
706
  stopatchingForAccountChange(_wallet.provider);
584
707
  stopatchingForChainIdChange(_wallet.provider);
585
708
  }
@@ -587,6 +710,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
587
710
  set({
588
711
  step: 'Idle',
589
712
  loading: false,
713
+ wallet: undefined,
590
714
  wallets: $connection.wallets,
591
715
  });
592
716
  }
@@ -594,11 +718,11 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
594
718
  function back(step: 'MechanismToChoose' | 'Idle' | 'WalletToChoose') {
595
719
  popup?.cancel();
596
720
  if (step === 'MechanismToChoose') {
597
- set({step, wallets: $connection.wallets});
721
+ set({step, wallets: $connection.wallets, wallet: undefined});
598
722
  } else if (step === 'Idle') {
599
- set({step, loading: false, wallets: $connection.wallets});
723
+ set({step, loading: false, wallet: undefined, wallets: $connection.wallets});
600
724
  } else if (step === 'WalletToChoose') {
601
- set({step, wallets: $connection.wallets, mechanism: {type: 'wallet'}});
725
+ set({step, wallet: undefined, wallets: $connection.wallets, mechanism: {type: 'wallet'}});
602
726
  }
603
727
  }
604
728
 
@@ -664,7 +788,7 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
664
788
 
665
789
  function cancel() {
666
790
  popup?.cancel();
667
- set({step: 'Idle', loading: false, wallets: $connection.wallets});
791
+ set({step: 'Idle', wallet: undefined, loading: false, wallets: $connection.wallets});
668
792
  }
669
793
 
670
794
  function getSignatureForPublicKeyPublication(): Promise<`0x${string}`> {
@@ -693,6 +817,182 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
693
817
  throw new Error(`no saved public key publication signature for ${account.address}`);
694
818
  }
695
819
 
820
+ async function unlock() {
821
+ if (!$connection.wallet) {
822
+ throw new Error(`invali state`);
823
+ }
824
+
825
+ const wallet = $connection.wallet;
826
+
827
+ // TODO unlocking state
828
+ await wallet.provider.request({method: 'eth_requestAccounts'}).then(onAccountChanged);
829
+ }
830
+
831
+ async function switchWalletChain(
832
+ chainId: string,
833
+ config?: {
834
+ readonly rpcUrls?: readonly string[];
835
+ readonly blockExplorerUrls?: readonly string[];
836
+ readonly chainName?: string;
837
+ readonly iconUrls?: readonly string[];
838
+ readonly nativeCurrency?: {
839
+ name: string;
840
+ symbol: string;
841
+ decimals: number;
842
+ };
843
+ },
844
+ ) {
845
+ if (!$connection.wallet) {
846
+ throw new Error(`invali state`);
847
+ }
848
+
849
+ const wallet = $connection.wallet;
850
+ // if (!wallet) {
851
+ // throw new Error(`no wallet`);
852
+ // }
853
+
854
+ try {
855
+ // attempt to switch...
856
+ set({
857
+ ...$connection,
858
+ wallet: {...$connection.wallet, switchingChain: 'switchingChain'},
859
+ });
860
+ const result = await wallet.provider.request({
861
+ method: 'wallet_switchEthereumChain',
862
+ params: [
863
+ {
864
+ chainId: ('0x' + parseInt(chainId).toString(16)) as EIP1193ChainId,
865
+ },
866
+ ],
867
+ });
868
+ if (!result) {
869
+ if ($connection.wallet) {
870
+ set({
871
+ ...$connection,
872
+ wallet: {...$connection.wallet, switchingChain: false},
873
+ });
874
+ }
875
+
876
+ // logger.info(`wallet_switchEthereumChain: complete`);
877
+ // this will be taken care with `chainChanged` (but maybe it should be done there ?)
878
+ // handleNetwork(chainId);
879
+ } else {
880
+ if ($connection.wallet) {
881
+ set({
882
+ ...$connection,
883
+ wallet: {...$connection.wallet, switchingChain: false},
884
+ error: {
885
+ message: `Failed to switch to ${config?.chainName || `chain with id = ${chainId}`}`,
886
+ cause: result,
887
+ },
888
+ });
889
+ }
890
+ throw result;
891
+ }
892
+ } catch (err) {
893
+ if ((err as any).code === 4001) {
894
+ // logger.info(`wallet_addEthereumChain: failed but error code === 4001, we ignore as user rejected it`, err);
895
+ if ($connection.wallet) {
896
+ set({
897
+ ...$connection,
898
+ wallet: {...$connection.wallet, switchingChain: false},
899
+ });
900
+ }
901
+ return;
902
+ }
903
+ // if ((err as any).code === 4902) {
904
+ else if (config && config.rpcUrls && config.rpcUrls.length > 0) {
905
+ if ($connection.wallet) {
906
+ set({
907
+ ...$connection,
908
+ wallet: {...$connection.wallet, switchingChain: 'addingChain'},
909
+ });
910
+ }
911
+ // logger.info(`wallet_switchEthereumChain: could not switch, try adding the chain via "wallet_addEthereumChain"`);
912
+ try {
913
+ const result = await wallet.provider.request({
914
+ method: 'wallet_addEthereumChain',
915
+ params: [
916
+ {
917
+ chainId: ('0x' + parseInt(chainId).toString(16)) as EIP1193ChainId,
918
+ rpcUrls: config.rpcUrls,
919
+ chainName: config.chainName,
920
+ blockExplorerUrls: config.blockExplorerUrls,
921
+ iconUrls: config.iconUrls,
922
+ nativeCurrency: config.nativeCurrency,
923
+ },
924
+ ],
925
+ });
926
+ if (!result) {
927
+ if ($connection.wallet) {
928
+ set({
929
+ ...$connection,
930
+ wallet: {...$connection.wallet, switchingChain: false},
931
+ });
932
+ }
933
+ // this will be taken care with `chainChanged` (but maybe it should be done there ?)
934
+ // handleNetwork(chainId);
935
+ } else {
936
+ if ($connection.wallet) {
937
+ set({
938
+ ...$connection,
939
+ wallet: {...$connection.wallet, switchingChain: false},
940
+ error: {
941
+ message: `Failed to add new chain: ${config?.chainName || `chain with id = ${chainId}`}`,
942
+ cause: result,
943
+ },
944
+ });
945
+ }
946
+ // logger.info(`wallet_addEthereumChain: a non-undefinded result means an error`, result);
947
+ throw result;
948
+ }
949
+ } catch (err) {
950
+ if ((err as any).code !== 4001) {
951
+ if ($connection.wallet) {
952
+ set({
953
+ ...$connection,
954
+ wallet: {...$connection.wallet, switchingChain: false},
955
+ error: {
956
+ message: `Failed to add new chain: ${config?.chainName || `chain with id = ${chainId}`}`,
957
+ cause: err,
958
+ },
959
+ });
960
+ }
961
+ // logger.info(`wallet_addEthereumChain: failed`, err);
962
+ // TODO ?
963
+ // set({
964
+ // error: {message: `Failed to add new chain`, cause: err},
965
+ // });
966
+ // for now:
967
+ throw err;
968
+ } else {
969
+ if ($connection.wallet) {
970
+ set({
971
+ ...$connection,
972
+ wallet: {...$connection.wallet, switchingChain: false},
973
+ });
974
+ }
975
+ // logger.info(`wallet_addEthereumChain: failed but error code === 4001, we ignore as user rejected it`, err);
976
+ return;
977
+ }
978
+ }
979
+ } else {
980
+ const errorMessage = `Chain "${config?.chainName || `with chainId = ${chainId}`} " is not available on your wallet`;
981
+ if ($connection.wallet) {
982
+ set({
983
+ ...$connection,
984
+ wallet: {...$connection.wallet, switchingChain: false},
985
+ error: {
986
+ message: errorMessage,
987
+ },
988
+ });
989
+ }
990
+
991
+ throw new Error(errorMessage);
992
+ }
993
+ }
994
+ }
995
+
696
996
  return {
697
997
  subscribe: _store.subscribe,
698
998
  connect,
@@ -702,5 +1002,10 @@ export function createConnection(settings: {walletHost: string; autoConnect?: bo
702
1002
  connectOnCurrentWalletAccount,
703
1003
  disconnect,
704
1004
  getSignatureForPublicKeyPublication,
1005
+ switchWalletChain,
1006
+ unlock,
1007
+ provider: alwaysOnProvider,
705
1008
  };
706
1009
  }
1010
+
1011
+ export type ConnectionStore = ReturnType<typeof createConnection>;