Migrate from v1 to v2
Wagmi Core v2 redesigns the core APIs to mesh better with Viem. This major version transforms Wagmi into a light wrapper around Viem, sprinkling in multichain support and account management. As such, there are some breaking changes and deprecations to be aware of outlined in this guide.
To get started, install the latest version of Wagmi and it's required peer dependencies.
pnpm add @wagmi/core viem@2.x @wagmi/connectorsnpm install @wagmi/core viem@2.x @wagmi/connectorsyarn add @wagmi/core viem@2.x @wagmi/connectorsbun add @wagmi/core viem@2.x @wagmi/connectorsWagmi Core v2 should be the last major version that will have this many actionable breaking changes.
Moving forward, new functionality will be opt-in with old functionality being deprecated alongside the new features. This means upgrading to the latest major versions will not require immediate changes.
Not ready to migrate yet?
The Wagmi Core v1 docs are still available at 1.x.wagmi.sh/core.
Dependencies
Dropped CommonJS support
Wagmi v2 no longer publishes a separate cjs tag since very few people use this tag and ESM is the future. See Sindre Sorhus' guide for more info about switching to ESM.
Actions
Removed config singleton
Before v2, when you called createConfig, it set a global config singleton that was used internally by actions. For v2, config is now a required first parameter for actions.
import { getAccount, readContract } from '@wagmi/core'
import { parseAbi } from 'viem'
import { config } from './config'
const account = getAccount()
const account = getAccount(config)
const balanceOf = readContract({
const balanceOf = readContract(config, {
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
abi: parseAbi(['function balanceOf(address) view returns (uint256)']),
functionName: 'balanceOf',
args: ['0xd2135CfB216b74109775236E36d4b433F1DF507B'],
})import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})The previous global config singleton made it so you couldn't use multiple Config objects in the same project. In addition, we think passing config is more explicit and makes it easier to understand what's going on. Finally, types can be inferred directly from the config, like chain properties and more.
Removed getContract
Removed getContract export. Use Viem's getContract instead.
import { getContract } from '@wagmi/core'
import { getContract } from 'viem'
const contract = getContract()
const contract = getContract()Removed getNetwork and watchNetwork
The getNetwork and watchNetwork actions were removed since the connected chain is typically based on the connected account.
Use
config.chainsinstead to getchains.tsimport { getNetwork } from '@wagmi/core' const { chains } = getNetwork() const chains = config.chainstsimport { http, createConfig } from '@wagmi/core' import { mainnet, sepolia } from '@wagmi/core/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })Use
getAccountandconfig.chainsinstead to getchain.tsimport { getNetwork } from '@wagmi/core' import { getAccount } from '@wagmi/core' import { config } from './config' const { chain } = getNetwork() const { chainId } = getAccount(config) const chain = chains.find(chain => chain.id === chainId)tsimport { http, createConfig } from '@wagmi/core' import { mainnet, sepolia } from '@wagmi/core/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })Before v2,
getNetwork().chaincould result in an invalid chain if the active connector'schainIdwas not configured in the list ofconfig.chains. UsinggetAccountandconfig.chainsis more work, but ensures that chain is either valid or not defined. You can also usegetAccount(config).chainif you don't care about the chain beingundefinedwhen not configured.Use
watchAccountinstead ofwatchNetwork.tsimport { watchNetwork } from '@wagmi/core' import { watchAccount } from '@wagmi/core' import { config } from './config' const unwatch = watchNetwork((data) => console.log('Changed!', data)) const unwatch = watchAccount(config, { onChange(data) { const chains = config.chains const chain = chains.find(chain => chain.id === data.chainId) }, })tsimport { http, createConfig } from '@wagmi/core' import { mainnet, sepolia } from '@wagmi/core/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })
Removed getWebSocketPublicClient and watchWebSocketPublicClient
Viem Transports now determine the type of client that is returned. You can use getPublicClient and watchPublicClient to retrieve Viem PublicClient instances.
Alternatively, you can use getClient and watchClient to retrieve plain Viem Client instances. This is a better option for users that care about optimizing bundle size to be as small as possible.
Removed watchReadContract, watchReadContracts, and watchReadMulticall
Use watchBlockNumber along with readContract, readContracts, and multicall actions instead. Before v2, watchReadContract, watchReadContracts, and watchReadMulticall were all wrappers around watchBlockNumber and this simplifies the API.
import { watchReadContract } from '@wagmi/core'
import { watchBlockNumber, readContract } from '@wagmi/core'
import { config } from './config'
const unwatch = watchReadContract(
{
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
abi: parseAbi(['function balanceOf(address) view returns (uint256)']),
functionName: 'balanceOf',
args: ['0xd2135CfB216b74109775236E36d4b433F1DF507B'],
},
(result) => console.log('Changed!', result),
)
const unwatch = watchBlockNumber(config, {
onBlockNumber() {
const balanceOf = readContract(config, {
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
abi: parseAbi(['function balanceOf(address) view returns (uint256)']),
functionName: 'balanceOf',
args: ['0xd2135CfB216b74109775236E36d4b433F1DF507B'],
})
console.log('Changed!', balanceOf)
},
})import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})Removed fetchFeeData
Removed fetchFeeData. Use estimateFeesPerGas instead.
import { fetchFeeData } from '@wagmi/core'
import { estimateFeesPerGas } from '@wagmi/core'
import { config } from './config'
const result = await fetchFeeData()
const result = await estimateFeesPerGas(config)import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})Removed prepareWriteContract
Removed prepareWriteContract. Use simulateContract instead.
import { prepareWriteContract } from '@wagmi/core'
import { simulateContract } from '@wagmi/core'
import { config } from './config'
const result = await prepareWriteContract({ ... })
const result = await simulateContract(config, { ... })import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})Removed prepareSendTransaction
Removed prepareSendTransaction. Use estimateGas instead.
import { prepareSendTransaction } from '@wagmi/core'
import { estimateGas } from '@wagmi/core'
import { config } from './config'
const result = await prepareSendTransaction({ ... })
const result = await estimateGas(config, { ... })import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})Updated sendTransaction and writeContract return type
Updated sendTransaction and writeContract return type from { hash: `0x${string}` } to `0x${string}`.
const result = await sendTransaction({ hash: '0x...' })
result.hash
resultUpdated connect return type
Updated connect return type from { account: Address; chain: { id: number; unsupported?: boolean }; connector: Connector } to { accounts: readonly Address[]; chainId: number }. This better reflects the ability to have multiple accounts per connector.
Renamed parameters and return types
All hook parameters and return types follow the naming pattern of [PascalCaseActionName]Parameters and [PascalCaseActionName]ReturnType. For example, GetAccountParameters and GetAccountReturnType.
import { GetAccountConfig, GetAccountResult } from '@wagmi/core'
import { GetAccountParameters, GetAccountReturnType } from '@wagmi/core'Connectors
Moved Wagmi Connectors to peer dependencies
Wagmi Core v2 no longer exports connectors via the '@wagmi/core/connectors/*' entrypoints. Instead, you should install the @wagmi/connectors package.
pnpm add @wagmi/connectorsnpm install @wagmi/connectorsyarn add @wagmi/connectorsbun add @wagmi/connectorsAnd import connectors from there.
import { injected } from '@wagmi/connectors'See the connectors documentation for more information.
Updated connector API
In order to maximize type-safety and ease of creating connectors, the connector API changed. Follow the Creating Connectors guide for more info on creating new connectors and converting Wagmi v1 connectors.
Removed individual entrypoints
Previously, each connector had it's own entrypoint to optimize tree-shaking. Since all connectors now have package.json#sideEffects enabled, this is no longer necessary and the entrypoint is unified. Use the '@wagmi/connectors' package instead.
import { InjectedConnector } from '@wagmi/core/connectors/injected'
import { CoinbaseWalletConnector } from '@wagmi/core/connectors/coinbaseWallet'
import { coinbaseWallet, injected } from '@wagmi/connectors'Removed MetaMaskConnector
The MetaMaskConnector was removed since it was nearly the same thing as the InjectedConnector. Use the injected connector instead, along with the target parameter set to 'metaMask', for the same behavior.
import { MetaMaskConnector } from '@wagmi/core/connectors/metaMask'
import { injected } from '@wagmi/connectors'
const connector = new MetaMaskConnector()
const connector = injected({ target: 'metaMask' })Renamed connectors
In Wagmi v1, connectors were classes you needed to instantiate. In Wagmi v2, connectors are functions. As a result, the API has changed. Connectors have the following new names:
CoinbaseWalletConnectoris nowcoinbaseWallet.InjectedConnectoris nowinjected.SafeConnectoris nowsafe.WalletConnectConnectoris nowwalletConnect.
To create a connector, you now call the connector function with parameters.
import { WalletConnectConnector } from '@wagmi/core/connectors/walletConnect'
import { walletConnect } from '@wagmi/connectors'
const connector = new WalletConnectConnector({
const connector = walletConnect({
projectId: '3fcc6bba6f1de962d911bb5b5c3dba68',
})Removed WalletConnectLegacyConnector
WalletConnect v1 was sunset June 28, 2023. Use the walletConnect connector instead.
import { WalletConnectLegacyConnector } from '@wagmi/core/connectors/walletConnectLegacy'
import { walletConnect } from '@wagmi/connectors'
const connector = new WalletConnectLegacyConnector({
const connector = walletConnect({
projectId: '3fcc6bba6f1de962d911bb5b5c3dba68',
})Chains
Updated '@wagmi/core/chains' entrypoint
Chains now live in the Viem repository. As a result, the '@wagmi/core/chains' entrypoint now proxies all chains from 'viem/chains' directly.
Removed mainnet and sepolia from main entrypoint
Since the '@wagmi/core/chains' entrypoint now proxies 'viem/chains', mainnet and sepolia were removed from the main entrypoint. Use the '@wagmi/core/chains' entrypoint instead.
import { mainnet, sepolia } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'Errors
A number of errors were renamed to better reflect their functionality or replaced by Viem errors.
Miscellaneous
Removed internal ENS normalization
Before v2, Wagmi handled ENS name normalization internally for getEnsAddress, getEnsAvatar, and getEnsResolver, using Viem's normalize function. This added extra bundle size as full normalization is quite heavy. For v2, you must normalize ENS names yourself before passing them to these actions. You can use Viem's normalize function or any other function that performs UTS-46 normalization.
import { getEnsAddress } from '@wagmi/core'
import { normalize } from 'viem'
import { config } from './config'
const result = await getEnsAddress(config, {
name: 'wevm.eth',
name: normalize('wevm.eth'),
})import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})By inverting control, Wagmi let's you choose how much normalization to do. For example, maybe your project only allows ENS names that are rcnumeric so no normalization is not needed. Check out the ENS documentation for more information on normalizing names.
Removed configureChains
The Wagmi v2 Config now has native multichain support using the chains parameter so the configureChains function is no longer required.
import { configureChains, createConfig } from '@wagmi/core'
import { http, createConfig } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'
const { chains, publicClient } = configureChains(
[mainnet, sepolia],
[publicProvider(), publicProvider()],
)
export const config = createConfig({
publicClient,
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})Removed ABI exports
Import from Viem instead.
import { erc20ABI } from '@wagmi/core'
import { erc20Abi } from 'viem'Removed '@wagmi/core/providers/* entrypoints
It never made sense that we would have provider URLs hardcoded in the Wagmi codebase. Use Viem transports along with RPC provider URLs instead.
import { alchemyProvider } from '@wagmi/core/providers/alchemy'
import { http } from 'viem'
const transport = http('https://mainnet.example.com')Updated createConfig parameters
- Removed
autoConnect. The reconnecting behavior must be managed manually and is not related to the WagmiConfig. Use thereconnectaction instead. - Removed
publicClientandwebSocketPublicClient. Usetransportsorclientinstead. - Removed
logger. Wagmi no longer logs debug information to console.
Updated Config object
- Removed
config.connector. Useconfig.state.connections.get(config.state.current)?.connectorinstead. - Removed
config.data. Useconfig.state.connections.get(config.state.current)instead. - Removed
config.error. Was unused and not needed. - Removed
config.lastUsedChainId. Useconfig.state.connections.get(config.state.current)?.chainIdinstead. - Removed
config.publicClient. Useconfig.getClient()orgetPublicClientinstead. - Removed
config.status. Useconfig.state.statusinstead. - Removed
config.webSocketClient. Useconfig.getClient()orgetPublicClientinstead. - Removed
config.clearState. Was unused and not needed. - Removed
config.autoConnect(). Usereconnectaction instead. - Renamed
config.setConnectors. Useconfig._internal.setConnectorsinstead. - Removed
config.setLastUsedConnector. Useconfig.storage?.setItem('recentConnectorId', connectorId)instead. - Removed
getConfig.configshould be passed explicitly to actions instead of using globalconfig.
Deprecations
Deprecated getBalance token parameter
Moving forward, getBalance will only work for native currencies, thus the token parameter is no longer supported. Use readContracts instead.
import { getBalance } from '@wagmi/core'
import { readContracts } from '@wagmi/core'
import { erc20Abi } from 'viem'
import { config } from './config'
const result = await getBalance(config, {
address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
token: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
})
const result = await readContracts(config, {
allowFailure: false,
contracts: [
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'balanceOf',
args: ['0x4557B18E779944BFE9d78A672452331C186a9f48'],
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'decimals',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'symbol',
},
]
})Deprecated getBalance unit parameter and formatted return value
Moving forward, getBalance will not accept the unit parameter or return a formatted value. Instead you can call formatUnits from Viem directly or use another number formatting library, like dnum instead.
import { formatUnits } from 'viem'
import { getBalance } from '@wagmi/core'
const result = await getBalance({
address: '0x4557B18E779944BFE9d78A672452331C186a9f48',
unit: 'ether',
})
result.formatted
formatUnits(result.value, result.decimals)Deprecated getToken
Moving forward, getToken is no longer supported. Use readContracts instead.
import { getToken } from '@wagmi/core'
import { readContracts } from '@wagmi/core'
import { erc20Abi } from 'viem'
import { config } from './config'
const result = await getToken(config, {
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
})
const result = await readContracts(config, {
allowFailure: false,
contracts: [
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'decimals',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'name',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'symbol',
},
{
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
abi: erc20Abi,
functionName: 'totalSupply',
},
]
})Deprecated formatUnits parameters and return values
The formatUnits parameter and related return values (e.g. result.formatted) are deprecated for the following actions:
Instead you can call formatUnits from Viem directly or use another number formatting library, like dnum instead.
import { formatUnits } from 'viem'
import { getToken } from '@wagmi/core'
const result = await getToken({
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
formatUnits: 'ether',
})
result.totalSupply.formatted
formatUnits(result.totalSupply.value, 18)This allows us to invert control to users so they can handle number formatting however they want, taking into account precision, localization, and more.
Renamed actions
The following actions were renamed to better reflect their functionality and underlying Viem actions:
fetchBalanceis nowgetBalancefetchBlockNumberis nowgetBlockNumberfetchEnsAddressis nowgetEnsAddressfetchEnsAvataris nowgetEnsAvatarfetchEnsNameis nowgetEnsNamefetchEnsResolveris nowgetEnsResolverfetchTokenis nowgetTokenfetchTransactionis nowgetTransactionswitchNetworkis nowswitchChainwaitForTransactionis nowwaitForTransactionReceipt