Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update metamask sdk #3848

Merged
merged 5 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading