@reown/appkit-wagmi-react-native 2.0.0 → 2.0.1

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.
@@ -1,62 +1,75 @@
1
- import type {
2
- Provider,
3
- RequestArguments,
4
- WalletConnector
5
- } from '@reown/appkit-common-react-native';
1
+ import type { Provider, WalletConnector } from '@reown/appkit-common-react-native';
2
+
6
3
  import {
7
4
  getAddress,
8
5
  numberToHex,
6
+ RpcError,
9
7
  SwitchChainError,
10
8
  UserRejectedRequestError,
11
9
  type Hex
12
10
  } from 'viem';
13
- import { ChainNotConfiguredError, createConnector, ProviderNotFoundError } from 'wagmi';
14
- import { formatNetwork } from '../utils/helpers';
11
+ import {
12
+ ChainNotConfiguredError,
13
+ createConnector,
14
+ ProviderNotFoundError,
15
+ type Connector
16
+ } from 'wagmi';
17
+
18
+ type UniversalConnector = Connector & {
19
+ onSessionDelete(data: { topic: string }): void;
20
+ };
21
+
22
+ type Properties = {
23
+ onSessionDelete(data: { topic: string }): void;
24
+ };
15
25
 
16
26
  export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
17
27
  let provider: Provider | undefined;
18
28
 
19
- let accountsChangedHandler: ((accounts: string[]) => void) | undefined;
20
- let chainChangedHandler: ((chainId: string | number) => void) | undefined;
21
- let disconnectHandler: ((error?: Error) => void) | undefined;
29
+ let accountsChanged: UniversalConnector['onAccountsChanged'] | undefined;
30
+ let chainChanged: UniversalConnector['onChainChanged'] | undefined;
31
+ let sessionDelete: UniversalConnector['onSessionDelete'] | undefined;
32
+ let disconnect: UniversalConnector['onDisconnect'] | undefined;
22
33
 
23
- type AppKitConnectorProperties = { ready: boolean };
34
+ function cleanupEventListeners(_provider?: Provider | null) {
35
+ if (accountsChanged) {
36
+ _provider?.off('accountsChanged', accountsChanged);
37
+ accountsChanged = undefined;
38
+ }
39
+ if (chainChanged) {
40
+ _provider?.off('chainChanged', chainChanged);
41
+ chainChanged = undefined;
42
+ }
43
+ if (disconnect) {
44
+ _provider?.off('disconnect', disconnect);
45
+ disconnect = undefined;
46
+ }
47
+ if (sessionDelete) {
48
+ _provider?.off('session_delete', sessionDelete);
49
+ sessionDelete = undefined;
50
+ }
51
+ }
24
52
 
25
- return createConnector<Provider, AppKitConnectorProperties>(config => ({
53
+ return createConnector<Provider, Properties>(config => ({
26
54
  id: 'walletconnect',
27
55
  name: 'WalletConnect',
28
56
  type: 'walletconnect' as const,
29
- ready: !!appKitProvidedConnector.getProvider(),
57
+ ready: !!appKitProvidedConnector.getProvider('eip155'),
30
58
 
31
59
  async setup() {
32
- provider = appKitProvidedConnector.getProvider();
33
- if (provider?.on) {
34
- accountsChangedHandler = (accounts: string[]) => {
35
- const hexAccounts = accounts.map(acc => getAddress(acc));
36
- config.emitter.emit('change', { accounts: hexAccounts });
37
- if (hexAccounts.length === 0) {
38
- config.emitter.emit('disconnect');
39
- }
40
- };
41
- chainChangedHandler = (chainId: string | number) => {
42
- const newChainId = typeof chainId === 'string' ? parseInt(chainId, 10) : chainId;
43
- config.emitter.emit('change', { chainId: newChainId });
44
- };
45
- disconnectHandler = (error?: Error) => {
46
- config.emitter.emit('disconnect');
47
- if (error) config.emitter.emit('error', { error });
48
- };
49
-
50
- if (accountsChangedHandler) provider.on('accountsChanged', accountsChangedHandler);
51
- if (chainChangedHandler) provider.on('chainChanged', chainChangedHandler);
52
- if (disconnectHandler) provider.on('disconnect', disconnectHandler);
53
- if (disconnectHandler) provider.on('session_delete', disconnectHandler);
60
+ const _provider = await this.getProvider().catch(() => null);
61
+ if (!_provider) {
62
+ return;
63
+ }
64
+ if (!sessionDelete) {
65
+ sessionDelete = this.onSessionDelete.bind(this);
66
+ _provider.on('session_delete', sessionDelete);
54
67
  }
55
68
  },
56
69
 
57
70
  async connect({ chainId } = {}) {
58
71
  try {
59
- const _provider = await this.getProvider();
72
+ const _provider = appKitProvidedConnector.getProvider('eip155');
60
73
  if (!_provider) throw new ProviderNotFoundError();
61
74
 
62
75
  // AppKit connector is already connected or handles its own connection.
@@ -75,8 +88,22 @@ export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
75
88
  await this.switchChain?.({ chainId });
76
89
  currentChainId = chainId;
77
90
  }
78
-
79
- this.ready = true;
91
+ if (!accountsChanged) {
92
+ accountsChanged = this.onAccountsChanged.bind(this);
93
+ _provider.on('accountsChanged', accountsChanged);
94
+ }
95
+ if (!chainChanged) {
96
+ chainChanged = this.onChainChanged.bind(this);
97
+ _provider.on('chainChanged', chainChanged);
98
+ }
99
+ if (!disconnect) {
100
+ disconnect = this.onDisconnect.bind(this);
101
+ _provider.on('disconnect', disconnect);
102
+ }
103
+ if (!sessionDelete) {
104
+ sessionDelete = this.onSessionDelete.bind(this);
105
+ _provider.on('session_delete', sessionDelete);
106
+ }
80
107
 
81
108
  return { accounts: accountAddresses, chainId: currentChainId };
82
109
  } catch (error) {
@@ -86,24 +113,22 @@ export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
86
113
  },
87
114
 
88
115
  async disconnect() {
89
- await appKitProvidedConnector.disconnect();
90
- config.emitter.emit('message', { type: 'externalDisconnect' });
91
- if (provider?.off && accountsChangedHandler && chainChangedHandler && disconnectHandler) {
92
- provider.off('accountsChanged', accountsChangedHandler);
93
- provider.off('chainChanged', chainChangedHandler);
94
- provider.off('disconnect', disconnectHandler);
95
- provider.off('session_delete', disconnectHandler);
96
- accountsChangedHandler = undefined;
97
- chainChangedHandler = undefined;
98
- disconnectHandler = undefined;
116
+ const _provider = await this.getProvider().catch(() => null);
117
+ try {
118
+ await appKitProvidedConnector.disconnect();
119
+ config.emitter.emit('message', { type: 'externalDisconnect' });
120
+ } catch (error) {
121
+ if (!/No matching key/i.test((error as Error).message)) {
122
+ throw error;
123
+ }
124
+ } finally {
125
+ cleanupEventListeners(_provider);
99
126
  }
100
- this.ready = false;
101
127
  },
102
128
 
103
129
  async getAccounts() {
104
130
  const namespaces = appKitProvidedConnector.getNamespaces();
105
- // @ts-ignore
106
- const eip155Accounts = namespaces?.eip155?.accounts;
131
+ const eip155Accounts = namespaces?.['eip155']?.accounts as string[] | undefined;
107
132
  if (!eip155Accounts) return [] as readonly Hex[];
108
133
 
109
134
  return eip155Accounts
@@ -123,8 +148,7 @@ export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
123
148
 
124
149
  // Fallback: Try to get from CAIP accounts if available
125
150
  const namespaces = appKitProvidedConnector.getNamespaces();
126
- // @ts-ignore
127
- const eip155Accounts = namespaces?.eip155?.accounts;
151
+ const eip155Accounts = namespaces?.['eip155']?.accounts as string[] | undefined;
128
152
  if (eip155Accounts && eip155Accounts.length > 0) {
129
153
  const parts = eip155Accounts[0]?.split(':');
130
154
  if (parts && parts.length > 1 && typeof parts[1] === 'string') {
@@ -140,20 +164,10 @@ export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
140
164
 
141
165
  async getProvider() {
142
166
  if (!provider) {
143
- provider = appKitProvidedConnector.getProvider();
167
+ provider = appKitProvidedConnector.getProvider('eip155');
144
168
  }
145
169
 
146
- const chainId = await this.getChainId();
147
-
148
- //TODO: Review this with gancho
149
- const _provider = {
150
- ...provider,
151
- request: (args: RequestArguments) => {
152
- return provider?.request(args, `eip155:${chainId}`);
153
- }
154
- };
155
-
156
- return Promise.resolve(_provider as Provider);
170
+ return provider;
157
171
  },
158
172
 
159
173
  async isAuthorized() {
@@ -167,7 +181,7 @@ export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
167
181
  },
168
182
 
169
183
  async switchChain({ chainId }) {
170
- const _provider = await this.getProvider();
184
+ const _provider = appKitProvidedConnector.getProvider('eip155');
171
185
  if (!_provider) throw new Error('Provider not available for switching chain.');
172
186
  const newChain = config.chains.find(c => c.id === chainId);
173
187
 
@@ -179,28 +193,27 @@ export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
179
193
  params: [{ chainId: numberToHex(chainId) }]
180
194
  });
181
195
 
182
- config.emitter.emit('change', { chainId });
183
-
184
196
  return newChain;
185
- } catch (error) {
186
- // Try to add chain if switch failed (common pattern)
187
- //4902 in MetaMask: Unrecognized chain ID
197
+ } catch (err) {
198
+ const error = err as RpcError;
199
+
200
+ if (/(user rejected)/i.test(error.message)) throw new UserRejectedRequestError(error);
201
+
188
202
  if ((error as any)?.code === 4902 || (error as any)?.data?.originalError?.code === 4902) {
203
+ // Indicates chain is not added to provider
189
204
  try {
205
+ const addEthereumChainParams = {
206
+ chainId: numberToHex(chainId),
207
+ chainName: newChain.name,
208
+ nativeCurrency: newChain.nativeCurrency,
209
+ rpcUrls: [newChain.rpcUrls.default?.http[0] ?? ''],
210
+ blockExplorerUrls: [newChain.blockExplorers?.default?.url]
211
+ };
212
+
190
213
  await _provider.request({
191
214
  method: 'wallet_addEthereumChain',
192
- params: [
193
- {
194
- chainId: numberToHex(chainId),
195
- chainName: newChain.name,
196
- nativeCurrency: newChain.nativeCurrency,
197
- rpcUrls: [newChain.rpcUrls.default?.http[0] ?? ''], // Take first default HTTP RPC URL
198
- blockExplorerUrls: [newChain.blockExplorers?.default?.url]
199
- }
200
- ]
215
+ params: [addEthereumChainParams]
201
216
  });
202
- await appKitProvidedConnector.switchNetwork(formatNetwork(newChain));
203
- config.emitter.emit('change', { chainId });
204
217
 
205
218
  return newChain;
206
219
  } catch (addError) {
@@ -212,17 +225,41 @@ export function UniversalConnector(appKitProvidedConnector: WalletConnector) {
212
225
  },
213
226
 
214
227
  onAccountsChanged(accounts: string[]) {
215
- if (accounts.length === 0) this.onDisconnect();
216
- else config.emitter.emit('change', { accounts: accounts.map(x => getAddress(x)) });
228
+ //Only emit if the account is an evm account
229
+ const shouldEmit = accounts.some(account => account.startsWith('0x'));
230
+
231
+ if (accounts.length === 0) {
232
+ this.onDisconnect();
233
+ } else if (shouldEmit) {
234
+ config.emitter.emit('change', { accounts: accounts.map(x => getAddress(x)) });
235
+ }
217
236
  },
218
237
 
219
238
  onChainChanged(chain: string) {
220
239
  const chainId = Number(chain);
221
- config.emitter.emit('change', { chainId });
240
+
241
+ //Only emit if the chain is in the config (evm)
242
+ const shouldEmit = config.chains.some(c => c.id === chainId);
243
+ if (shouldEmit) {
244
+ config.emitter.emit('change', { chainId });
245
+ }
222
246
  },
223
247
 
224
- onDisconnect: () => {
248
+ async onDisconnect() {
225
249
  config.emitter.emit('disconnect');
250
+
251
+ try {
252
+ const _provider = await this.getProvider();
253
+ cleanupEventListeners(_provider);
254
+ } catch (error) {
255
+ // If provider is not available, still clean up local references
256
+ // to prevent memory leaks
257
+ cleanupEventListeners(null);
258
+ }
259
+ },
260
+
261
+ onSessionDelete() {
262
+ this.onDisconnect();
226
263
  }
227
264
  }));
228
265
  }
@@ -1,14 +1,13 @@
1
- import { CoreHelperUtil } from '@reown/appkit-react-native';
1
+ import { http } from 'viem';
2
2
  import {
3
3
  PresetsUtil,
4
4
  ConstantsUtil,
5
5
  type AppKitNetwork,
6
6
  type Network
7
7
  } from '@reown/appkit-common-react-native';
8
- import { http } from 'viem';
9
8
 
10
9
  export function getTransport({ chainId, projectId }: { chainId: number; projectId: string }) {
11
- const RPC_URL = CoreHelperUtil.getBlockchainApiUrl();
10
+ const RPC_URL = ConstantsUtil.BLOCKCHAIN_API_RPC_URL;
12
11
 
13
12
  if (!PresetsUtil.RpcChainIds.includes(chainId)) {
14
13
  return http();