Skip to content

Commit

Permalink
feat: update metamask sdk (#3848)
Browse files Browse the repository at this point in the history
* feat: update metamask sdk

* chore: changeset

* tweak

* tweaks

* tweaks
  • Loading branch information
jxom committed Apr 25, 2024
1 parent f2b1d3b commit dd40a41
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 487 deletions.
7 changes: 7 additions & 0 deletions .changeset/tame-bats-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wagmi/connectors": patch
"@wagmi/core": patch
"wagmi": patch
---

Updated MetaMask SDK.
9 changes: 3 additions & 6 deletions packages/connectors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"dependencies": {
"@coinbase/wallet-sdk": "3.9.1",
"@metamask/sdk": "0.14.3",
"@metamask/sdk": "0.18.6",
"@safe-global/safe-apps-provider": "0.18.1",
"@safe-global/safe-apps-sdk": "8.1.0",
"@walletconnect/ethereum-provider": "2.11.2",
Expand All @@ -56,10 +56,7 @@
"@wagmi/core": "workspace:*",
"msw": "^2.0.6"
},
"contributors": [
"awkweb.eth <t@wevm.dev>",
"jxom.eth <j@wevm.dev>"
],
"contributors": ["awkweb.eth <t@wevm.dev>", "jxom.eth <j@wevm.dev>"],
"funding": "https://github.com/sponsors/wevm",
"keywords": [
"react",
Expand All @@ -71,4 +68,4 @@
"web3",
"abi"
]
}
}
132 changes: 44 additions & 88 deletions packages/connectors/src/metaMask.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {
EventType,
type MetaMaskSDK,
type MetaMaskSDKOptions,
type SDKProvider,
} from '@metamask/sdk'
import { ChainNotConfiguredError, createConnector } from '@wagmi/core'
import type { Evaluate, ExactPartial, Omit } from '@wagmi/core/internal'
import type { Evaluate, ExactPartial } from '@wagmi/core/internal'
import {
type Address,
type ProviderConnectInfo,
Expand All @@ -14,31 +13,20 @@ import {
RpcError,
SwitchChainError,
UserRejectedRequestError,
type WalletPermission,
getAddress,
numberToHex,
} from 'viem'

export type MetaMaskParameters = Evaluate<
ExactPartial<
Omit<
MetaMaskSDKOptions,
| 'checkInstallationImmediately'
| 'checkInstallationOnAllCalls'
| 'defaultReadOnlyChainId'
| 'readonlyRPCMap'
>
>
ExactPartial<Omit<MetaMaskSDKOptions, '_source' | 'readonlyRPCMap'>>
>

const isMobileBrowser =
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent,
)

metaMask.type = 'metaMask' as const
/**
* @deprecated
*
* __Warning__ This connector has a large file size due to the underlying `@metamask/sdk`. For mobile
* support, it is recommended to use {@link walletConnect}. For desktop support, you should rely on Multi Injected
* Provider Discovery (EIP-6963) via the Wagmi {@link Config}.
*/
export function metaMask(parameters: MetaMaskParameters = {}) {
type Provider = SDKProvider
type Properties = {
Expand All @@ -48,7 +36,8 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
type Listener = Parameters<Provider['on']>[1]

let sdk: MetaMaskSDK
let walletProvider: Provider | undefined
let provider: Provider | undefined
let providerPromise: Promise<typeof provider>

return createConnector<Provider, Properties, StorageItem>((config) => ({
id: 'metaMaskSDK',
Expand All @@ -62,30 +51,8 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
async connect({ chainId, isReconnecting } = {}) {
const provider = await this.getProvider()

let accounts: readonly Address[] | null = null
if (!isReconnecting) {
accounts = await this.getAccounts().catch(() => null)
const isAuthorized = !!accounts?.length
if (isAuthorized)
// Attempt to show another prompt for selecting account if already connected
try {
const permissions = (await provider.request({
method: 'wallet_requestPermissions',
params: [{ eth_accounts: {} }],
})) as WalletPermission[]
accounts = (permissions[0]?.caveats?.[0]?.value as string[])?.map(
(x) => getAddress(x),
)
} catch (err) {
const error = err as RpcError
// Not all injected providers support `wallet_requestPermissions` (e.g. MetaMask iOS).
// Only bubble up error if user rejects request
if (error.code === UserRejectedRequestError.code)
throw new UserRejectedRequestError(error)
// Or prompt is already open
if (error.code === ResourceUnavailableRpcError.code) throw error
}
}
let accounts: readonly Address[] = []
if (isReconnecting) accounts = await this.getAccounts().catch(() => [])

try {
if (!accounts?.length) {
Expand All @@ -104,20 +71,8 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
provider.on('chainChanged', this.onChainChanged as Listener)
provider.on('disconnect', this.onDisconnect.bind(this) as Listener)

// Backward compatibility with older wallet (<7.3) version that return accounts before authorization
if (!sdk.isExtensionActive() && !sdk._getConnection()?.isAuthorized()) {
function waitForAuthorized() {
return new Promise((resolve) => {
const connection = sdk._getConnection()
const connector = connection?.getConnector()
connector?.once(EventType.AUTHORIZED, () => resolve(true))
})
}
await waitForAuthorized()
}

// Switch to chain if provided
let currentChainId = await this.getChainId()
let currentChainId = (await this.getChainId()) as number
if (chainId && currentChainId !== chainId) {
const chain = await this.switchChain!({ chainId }).catch((error) => {
if (error.code === UserRejectedRequestError.code) throw error
Expand Down Expand Up @@ -164,44 +119,39 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
async getChainId() {
const provider = await this.getProvider()
const chainId =
provider.chainId ?? (await provider?.request({ method: 'eth_chainId' }))
provider.getChainId() ||
(await provider?.request({ method: 'eth_chainId' }))
return Number(chainId)
},
async getProvider() {
if (!walletProvider) {
if (!sdk || !sdk?.isInitialized()) {
const { MetaMaskSDK } = await import('@metamask/sdk')
sdk = new MetaMaskSDK({
dappMetadata: { name: 'wagmi' },
enableAnalytics: false,
extensionOnly: true,
modals: {
// Disable by default since it pops up when mobile tries to reconnect
otp() {
const noop = () => {}
return { mount: noop, unmount: noop }
},
},
useDeeplink: true,
_source: 'wagmi',
...parameters,
checkInstallationImmediately: false,
checkInstallationOnAllCalls: false,
})
await sdk.init()
}
try {
walletProvider = sdk.getProvider()
} catch (error) {
// TODO: SDK sometimes throws errors when MM extension or mobile provider is not detected (don't throw for those errors)
const regex = /^SDK state invalid -- undefined( mobile)? provider$/
if (!regex.test((error as Error).message)) throw error
}
async function initProvider() {
const { MetaMaskSDK } = await import('@metamask/sdk')
sdk = new MetaMaskSDK({
dappMetadata: {},
...parameters,
_source: 'wagmi',
readonlyRPCMap: Object.fromEntries(
config.chains.map((chain) => [
chain.id,
chain.rpcUrls.default.http[0]!,
]),
),
})
await sdk.init()
return sdk.getProvider()!
}
return walletProvider!

if (!provider) {
if (!providerPromise) providerPromise = initProvider()
provider = await providerPromise
}
return provider!
},
async isAuthorized() {
try {
// MetaMask Mobile doesn't support persisted sessions.
if (isMobileBrowser) return false

const isDisconnected =
// If shim exists in storage, connector is disconnected
await config.storage?.getItem('metaMaskSDK.disconnected')
Expand Down Expand Up @@ -326,6 +276,12 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
if (provider && !!(await this.getAccounts()).length) return
}

// Remove cached SDK properties.
if (typeof localStorage !== 'undefined') {
localStorage.removeItem('MMSDK_cached_address')
localStorage.removeItem('MMSDK_cached_chainId')
}

// No need to remove 'metaMaskSDK.disconnected' from storage because `onDisconnect` is typically
// only called when the wallet is disconnected through the wallet's interface, meaning the wallet
// actually disconnected and we don't need to simulate it.
Expand Down
3 changes: 2 additions & 1 deletion playgrounds/next/src/wagmi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { http, cookieStorage, createConfig, createStorage } from 'wagmi'
import { mainnet, optimism, sepolia } from 'wagmi/chains'
import { injected, walletConnect } from 'wagmi/connectors'
import { injected, metaMask, walletConnect } from 'wagmi/connectors'

export const config = createConfig({
chains: [mainnet, sepolia, optimism],
Expand All @@ -10,6 +10,7 @@ export const config = createConfig({
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!,
showQrModal: false,
}),
metaMask(),
],
storage: createStorage({
storage: cookieStorage,
Expand Down
3 changes: 2 additions & 1 deletion playgrounds/vite-core/src/wagmi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { coinbaseWallet, walletConnect } from '@wagmi/connectors'
import { coinbaseWallet, metaMask, walletConnect } from '@wagmi/connectors'
import { http, createConfig, createStorage } from '@wagmi/core'
import { mainnet, optimism, sepolia } from '@wagmi/core/chains'

Expand All @@ -7,6 +7,7 @@ export const config = createConfig({
connectors: [
walletConnect({ projectId: import.meta.env.VITE_WC_PROJECT_ID }),
coinbaseWallet({ appName: 'Vite React Playground' }),
metaMask(),
],
storage: createStorage({ storage: localStorage, key: 'vite-core' }),
transports: {
Expand Down
3 changes: 2 additions & 1 deletion playgrounds/vite-react/src/wagmi.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { del, get, set } from 'idb-keyval'
import { http, createConfig } from 'wagmi'
import { celo, mainnet, optimism, sepolia } from 'wagmi/chains'
import { coinbaseWallet, walletConnect } from 'wagmi/connectors'
import { coinbaseWallet, metaMask, walletConnect } from 'wagmi/connectors'

// biome-ignore lint/correctness/noUnusedVariables: <explanation>
const indexedDBStorage = {
Expand All @@ -23,6 +23,7 @@ export const config = createConfig({
projectId: import.meta.env.VITE_WC_PROJECT_ID,
}),
coinbaseWallet({ appName: 'Vite React Playground', darkMode: true }),
metaMask(),
],
transports: {
[mainnet.id]: http(),
Expand Down
9 changes: 8 additions & 1 deletion playgrounds/vite-ssr-react/lib/wagmi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { http, cookieStorage, createConfig, createStorage } from 'wagmi'
import { celo, mainnet, optimism, sepolia } from 'wagmi/chains'
import { coinbaseWallet, injected, safe, walletConnect } from 'wagmi/connectors'
import {
coinbaseWallet,
injected,
metaMask,
safe,
walletConnect,
} from 'wagmi/connectors'

export const config = createConfig({
chains: [mainnet, sepolia, optimism, celo],
Expand All @@ -9,6 +15,7 @@ export const config = createConfig({
walletConnect({ projectId: import.meta.env.VITE_WC_PROJECT_ID }),
coinbaseWallet({ appName: 'Vite React Playground', darkMode: true }),
safe({ debug: true, shimDisconnect: true }),
metaMask(),
],
ssr: true,
storage: createStorage({
Expand Down
Loading

0 comments on commit dd40a41

Please sign in to comment.