@wagmi/connectors 5.0.0 → 5.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.
package/src/metaMask.ts CHANGED
@@ -1,16 +1,12 @@
1
- import {
2
- EventType,
1
+ import type {
3
2
  MetaMaskSDK,
4
- type MetaMaskSDKOptions,
3
+ MetaMaskSDKOptions,
5
4
  SDKProvider,
6
5
  } from '@metamask/sdk'
6
+ import { ChainNotConfiguredError, createConnector } from '@wagmi/core'
7
+ import type { Evaluate, ExactPartial } from '@wagmi/core/internal'
7
8
  import {
8
- ChainNotConfiguredError,
9
- createConnector,
10
- normalizeChainId,
11
- } from '@wagmi/core'
12
- import type { Evaluate, ExactPartial, Omit } from '@wagmi/core/internal'
13
- import {
9
+ type AddEthereumChainParameter,
14
10
  type Address,
15
11
  type ProviderConnectInfo,
16
12
  type ProviderRpcError,
@@ -18,31 +14,15 @@ import {
18
14
  RpcError,
19
15
  SwitchChainError,
20
16
  UserRejectedRequestError,
21
- type WalletPermission,
22
17
  getAddress,
23
18
  numberToHex,
24
19
  } from 'viem'
25
20
 
26
21
  export type MetaMaskParameters = Evaluate<
27
- ExactPartial<
28
- Omit<
29
- MetaMaskSDKOptions,
30
- | 'checkInstallationImmediately'
31
- | 'checkInstallationOnAllCalls'
32
- | 'defaultReadOnlyChainId'
33
- | 'readonlyRPCMap'
34
- >
35
- >
22
+ ExactPartial<Omit<MetaMaskSDKOptions, '_source' | 'readonlyRPCMap'>>
36
23
  >
37
24
 
38
25
  metaMask.type = 'metaMask' as const
39
- /**
40
- * @deprecated
41
- *
42
- * __Warning__ This connector has a large file size due to the underlying `@metamask/sdk`. For mobile
43
- * support, it is recommended to use {@link walletConnect}. For desktop support, you should rely on Multi Injected
44
- * Provider Discovery (EIP-6963) via the Wagmi {@link Config}.
45
- */
46
26
  export function metaMask(parameters: MetaMaskParameters = {}) {
47
27
  type Provider = SDKProvider
48
28
  type Properties = {
@@ -52,7 +32,8 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
52
32
  type Listener = Parameters<Provider['on']>[1]
53
33
 
54
34
  let sdk: MetaMaskSDK
55
- let walletProvider: Provider | undefined
35
+ let provider: Provider | undefined
36
+ let providerPromise: Promise<typeof provider>
56
37
 
57
38
  return createConnector<Provider, Properties, StorageItem>((config) => ({
58
39
  id: 'metaMaskSDK',
@@ -66,33 +47,13 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
66
47
  async connect({ chainId, isReconnecting } = {}) {
67
48
  const provider = await this.getProvider()
68
49
 
69
- let accounts: readonly Address[] | null = null
70
- if (!isReconnecting) {
71
- accounts = await this.getAccounts().catch(() => null)
72
- const isAuthorized = !!accounts?.length
73
- if (isAuthorized)
74
- // Attempt to show another prompt for selecting account if already connected
75
- try {
76
- const permissions = (await provider.request({
77
- method: 'wallet_requestPermissions',
78
- params: [{ eth_accounts: {} }],
79
- })) as WalletPermission[]
80
- accounts = permissions[0]?.caveats?.[0]?.value?.map(getAddress)
81
- } catch (err) {
82
- const error = err as RpcError
83
- // Not all injected providers support `wallet_requestPermissions` (e.g. MetaMask iOS).
84
- // Only bubble up error if user rejects request
85
- if (error.code === UserRejectedRequestError.code)
86
- throw new UserRejectedRequestError(error)
87
- // Or prompt is already open
88
- if (error.code === ResourceUnavailableRpcError.code) throw error
89
- }
90
- }
50
+ let accounts: readonly Address[] = []
51
+ if (isReconnecting) accounts = await this.getAccounts().catch(() => [])
91
52
 
92
53
  try {
93
54
  if (!accounts?.length) {
94
55
  const requestedAccounts = (await sdk.connect()) as string[]
95
- accounts = requestedAccounts.map(getAddress)
56
+ accounts = requestedAccounts.map((x) => getAddress(x))
96
57
  }
97
58
 
98
59
  provider.removeListener(
@@ -106,20 +67,8 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
106
67
  provider.on('chainChanged', this.onChainChanged as Listener)
107
68
  provider.on('disconnect', this.onDisconnect.bind(this) as Listener)
108
69
 
109
- // Backward compatibility with older wallet (<7.3) version that return accounts before authorization
110
- if (!sdk.isExtensionActive() && !sdk._getConnection()?.isAuthorized()) {
111
- function waitForAuthorized() {
112
- return new Promise((resolve) => {
113
- const connection = sdk._getConnection()
114
- const connector = connection?.getConnector()
115
- connector?.once(EventType.AUTHORIZED, () => resolve(true))
116
- })
117
- }
118
- await waitForAuthorized()
119
- }
120
-
121
70
  // Switch to chain if provided
122
- let currentChainId = await this.getChainId()
71
+ let currentChainId = (await this.getChainId()) as number
123
72
  if (chainId && currentChainId !== chainId) {
124
73
  const chain = await this.switchChain!({ chainId }).catch((error) => {
125
74
  if (error.code === UserRejectedRequestError.code) throw error
@@ -161,48 +110,51 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
161
110
  const accounts = (await provider.request({
162
111
  method: 'eth_accounts',
163
112
  })) as string[]
164
- return accounts.map(getAddress)
113
+ return accounts.map((x) => getAddress(x))
165
114
  },
166
115
  async getChainId() {
167
116
  const provider = await this.getProvider()
168
117
  const chainId =
169
- provider.chainId ?? (await provider?.request({ method: 'eth_chainId' }))
170
- return normalizeChainId(chainId)
118
+ provider.getChainId() ||
119
+ (await provider?.request({ method: 'eth_chainId' }))
120
+ return Number(chainId)
171
121
  },
172
122
  async getProvider() {
173
- if (!walletProvider) {
174
- if (!sdk || !sdk?.isInitialized()) {
175
- sdk = new MetaMaskSDK({
176
- enableDebug: false,
177
- dappMetadata: { name: 'wagmi' },
178
- extensionOnly: true,
179
- modals: {
180
- // Disable by default since it pops up when mobile tries to reconnect
181
- otp() {
182
- const noop = () => {}
183
- return { mount: noop, unmount: noop }
184
- },
185
- },
186
- useDeeplink: true,
187
- _source: 'wagmi',
188
- ...parameters,
189
- checkInstallationImmediately: false,
190
- checkInstallationOnAllCalls: false,
191
- })
192
- await sdk.init()
193
- }
194
- try {
195
- walletProvider = sdk.getProvider()
196
- } catch (error) {
197
- // TODO: SDK sometimes throws errors when MM extension or mobile provider is not detected (don't throw for those errors)
198
- const regex = /^SDK state invalid -- undefined( mobile)? provider$/
199
- if (!regex.test((error as Error).message)) throw error
200
- }
123
+ async function initProvider() {
124
+ const { MetaMaskSDK } = await import('@metamask/sdk')
125
+ sdk = new MetaMaskSDK({
126
+ dappMetadata: {},
127
+ ...parameters,
128
+ _source: 'wagmi',
129
+ readonlyRPCMap: Object.fromEntries(
130
+ config.chains.map((chain) => [
131
+ chain.id,
132
+ chain.rpcUrls.default.http[0]!,
133
+ ]),
134
+ ),
135
+ })
136
+ await sdk.init()
137
+ return sdk.getProvider()!
138
+ }
139
+
140
+ if (!provider) {
141
+ if (!providerPromise) providerPromise = initProvider()
142
+ provider = await providerPromise
201
143
  }
202
- return walletProvider!
144
+ return provider!
203
145
  },
204
146
  async isAuthorized() {
205
147
  try {
148
+ const isMobileBrowser =
149
+ typeof navigator !== 'undefined'
150
+ ? /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
151
+ navigator.userAgent,
152
+ )
153
+ : false
154
+
155
+ // MetaMask Mobile doesn't support persisted sessions.
156
+ if (isMobileBrowser) return false
157
+
206
158
  const isDisconnected =
207
159
  // If shim exists in storage, connector is disconnected
208
160
  await config.storage?.getItem('metaMaskSDK.disconnected')
@@ -214,7 +166,7 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
214
166
  return false
215
167
  }
216
168
  },
217
- async switchChain({ chainId }) {
169
+ async switchChain({ addEthereumChainParameter, chainId }) {
218
170
  const provider = await this.getProvider()
219
171
 
220
172
  const chain = config.chains.find((x) => x.id === chainId)
@@ -247,24 +199,34 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
247
199
  try {
248
200
  const { default: blockExplorer, ...blockExplorers } =
249
201
  chain.blockExplorers ?? {}
250
- let blockExplorerUrls: string[] = []
251
- if (blockExplorer)
202
+ let blockExplorerUrls
203
+ if (addEthereumChainParameter?.blockExplorerUrls)
204
+ blockExplorerUrls = addEthereumChainParameter.blockExplorerUrls
205
+ else if (blockExplorer)
252
206
  blockExplorerUrls = [
253
207
  blockExplorer.url,
254
208
  ...Object.values(blockExplorers).map((x) => x.url),
255
209
  ]
256
210
 
211
+ let rpcUrls
212
+ if (addEthereumChainParameter?.rpcUrls?.length)
213
+ rpcUrls = addEthereumChainParameter.rpcUrls
214
+ else rpcUrls = [chain.rpcUrls.default?.http[0] ?? '']
215
+
216
+ const addEthereumChain = {
217
+ blockExplorerUrls,
218
+ chainId: numberToHex(chainId),
219
+ chainName: addEthereumChainParameter?.chainName ?? chain.name,
220
+ iconUrls: addEthereumChainParameter?.iconUrls,
221
+ nativeCurrency:
222
+ addEthereumChainParameter?.nativeCurrency ??
223
+ chain.nativeCurrency,
224
+ rpcUrls,
225
+ } satisfies AddEthereumChainParameter
226
+
257
227
  await provider.request({
258
228
  method: 'wallet_addEthereumChain',
259
- params: [
260
- {
261
- chainId: numberToHex(chainId),
262
- chainName: chain.name,
263
- nativeCurrency: chain.nativeCurrency,
264
- rpcUrls: [chain.rpcUrls.default?.http[0] ?? ''],
265
- blockExplorerUrls,
266
- },
267
- ],
229
+ params: [addEthereumChain],
268
230
  })
269
231
 
270
232
  const currentChainId = await this.getChainId()
@@ -294,17 +256,20 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
294
256
  await config.storage?.removeItem('metaMaskSDK.disconnected')
295
257
  }
296
258
  // Regular change event
297
- else config.emitter.emit('change', { accounts: accounts.map(getAddress) })
259
+ else
260
+ config.emitter.emit('change', {
261
+ accounts: accounts.map((x) => getAddress(x)),
262
+ })
298
263
  },
299
264
  onChainChanged(chain) {
300
- const chainId = normalizeChainId(chain)
265
+ const chainId = Number(chain)
301
266
  config.emitter.emit('change', { chainId })
302
267
  },
303
268
  async onConnect(connectInfo) {
304
269
  const accounts = await this.getAccounts()
305
270
  if (accounts.length === 0) return
306
271
 
307
- const chainId = normalizeChainId(connectInfo.chainId)
272
+ const chainId = Number(connectInfo.chainId)
308
273
  config.emitter.emit('connect', { accounts, chainId })
309
274
 
310
275
  const provider = await this.getProvider()
@@ -324,6 +289,12 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
324
289
  if (provider && !!(await this.getAccounts()).length) return
325
290
  }
326
291
 
292
+ // Remove cached SDK properties.
293
+ if (typeof localStorage !== 'undefined') {
294
+ localStorage.removeItem('MMSDK_cached_address')
295
+ localStorage.removeItem('MMSDK_cached_chainId')
296
+ }
297
+
327
298
  // No need to remove 'metaMaskSDK.disconnected' from storage because `onDisconnect` is typically
328
299
  // only called when the wallet is disconnected through the wallet's interface, meaning the wallet
329
300
  // actually disconnected and we don't need to simulate it.
package/src/safe.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { SafeAppProvider } from '@safe-global/safe-apps-provider'
2
- import { type Opts, default as SafeAppsSDK } from '@safe-global/safe-apps-sdk'
1
+ import type { SafeAppProvider } from '@safe-global/safe-apps-provider'
2
+ import type { Opts } from '@safe-global/safe-apps-sdk'
3
3
  import {
4
+ type Connector,
4
5
  ProviderNotFoundError,
5
6
  createConnector,
6
- normalizeChainId,
7
7
  } from '@wagmi/core'
8
8
  import type { Evaluate } from '@wagmi/core/internal'
9
9
  import { getAddress } from 'viem'
@@ -31,14 +31,8 @@ export function safe(parameters: SafeParameters = {}) {
31
31
  type StorageItem = { 'safe.disconnected': true }
32
32
 
33
33
  let provider_: Provider | undefined
34
- let SDK: typeof SafeAppsSDK.default
35
- if (
36
- typeof SafeAppsSDK !== 'function' &&
37
- typeof SafeAppsSDK.default === 'function'
38
- )
39
- SDK = SafeAppsSDK.default
40
- else SDK = SafeAppsSDK as unknown as typeof SafeAppsSDK.default
41
- const sdk = new SDK(parameters)
34
+
35
+ let disconnect: Connector['onDisconnect'] | undefined
42
36
 
43
37
  return createConnector<Provider, Properties, StorageItem>((config) => ({
44
38
  id: 'safe',
@@ -51,7 +45,10 @@ export function safe(parameters: SafeParameters = {}) {
51
45
  const accounts = await this.getAccounts()
52
46
  const chainId = await this.getChainId()
53
47
 
54
- provider.on('disconnect', this.onDisconnect.bind(this))
48
+ if (!disconnect) {
49
+ disconnect = this.onDisconnect.bind(this)
50
+ provider.on('disconnect', disconnect)
51
+ }
55
52
 
56
53
  // Remove disconnected shim if it exists
57
54
  if (shimDisconnect) await config.storage?.removeItem('safe.disconnected')
@@ -62,7 +59,10 @@ export function safe(parameters: SafeParameters = {}) {
62
59
  const provider = await this.getProvider()
63
60
  if (!provider) throw new ProviderNotFoundError()
64
61
 
65
- provider.removeListener('disconnect', this.onDisconnect.bind(this))
62
+ if (disconnect) {
63
+ provider.removeListener('disconnect', disconnect)
64
+ disconnect = undefined
65
+ }
66
66
 
67
67
  // Add shim signalling connector is disconnected
68
68
  if (shimDisconnect)
@@ -82,8 +82,23 @@ export function safe(parameters: SafeParameters = {}) {
82
82
  if (!isIframe) return
83
83
 
84
84
  if (!provider_) {
85
+ const { default: SafeAppsSDK } = await import(
86
+ '@safe-global/safe-apps-sdk'
87
+ )
88
+ let SDK: typeof SafeAppsSDK.default
89
+ if (
90
+ typeof SafeAppsSDK !== 'function' &&
91
+ typeof SafeAppsSDK.default === 'function'
92
+ )
93
+ SDK = SafeAppsSDK.default
94
+ else SDK = SafeAppsSDK as unknown as typeof SafeAppsSDK.default
95
+ const sdk = new SDK(parameters)
96
+
85
97
  const safe = await sdk.safe.getInfo()
86
98
  if (!safe) throw new Error('Could not load Safe information')
99
+ const { SafeAppProvider } = await import(
100
+ '@safe-global/safe-apps-provider'
101
+ )
87
102
  provider_ = new SafeAppProvider(safe, sdk)
88
103
  }
89
104
  return provider_
@@ -91,7 +106,7 @@ export function safe(parameters: SafeParameters = {}) {
91
106
  async getChainId() {
92
107
  const provider = await this.getProvider()
93
108
  if (!provider) throw new ProviderNotFoundError()
94
- return normalizeChainId(provider.chainId)
109
+ return Number(provider.chainId)
95
110
  },
96
111
  async isAuthorized() {
97
112
  try {
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '5.0.0'
1
+ export const version = '5.0.1'