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/connectors
npm install @wagmi/core viem@2.x @wagmi/connectors
yarn add @wagmi/core viem@2.x @wagmi/connectors
bun add @wagmi/core viem@2.x @wagmi/connectors
Wagmi 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.chains
instead to getchains
.tsimport { getNetwork } from '@wagmi/core' const { chains } = getNetwork() const chains = config.chains
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(), }, })
Use
getAccount
andconfig.chains
instead 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().chain
could result in an invalid chain if the active connector'schainId
was not configured in the list ofconfig.chains
. UsinggetAccount
andconfig.chains
is more work, but ensures that chain is either valid or not defined. You can also usegetAccount(config).chain
if you don't care about the chain beingundefined
when not configured.Use
watchAccount
instead 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
result
Updated 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/connectors
npm install @wagmi/connectors
yarn add @wagmi/connectors
bun add @wagmi/connectors
And 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 its 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:
CoinbaseWalletConnector
is nowcoinbaseWallet
.InjectedConnector
is nowinjected
.SafeConnector
is nowsafe
.WalletConnectConnector
is 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 lets you choose how much normalization to do. For example, maybe your project only allows ENS names that are numeric 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 thereconnect
action instead. - Removed
publicClient
andwebSocketPublicClient
. Usetransports
orclient
instead. - Removed
logger
. Wagmi no longer logs debug information to console.
Updated Config
object
- Removed
config.connector
. Useconfig.state.connections.get(config.state.current)?.connector
instead. - 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)?.chainId
instead. - Removed
config.publicClient
. Useconfig.getClient()
orgetPublicClient
instead. - Removed
config.status
. Useconfig.state.status
instead. - Removed
config.webSocketClient
. Useconfig.getClient()
orgetPublicClient
instead. - Removed
config.clearState
. Was unused and not needed. - Removed
config.autoConnect()
. Usereconnect
action instead. - Renamed
config.setConnectors
. Useconfig._internal.setConnectors
instead. - Removed
config.setLastUsedConnector
. Useconfig.storage?.setItem('recentConnectorId', connectorId)
instead. - Removed
getConfig
.config
should 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:
fetchBalance
is nowgetBalance
fetchBlockNumber
is nowgetBlockNumber
fetchEnsAddress
is nowgetEnsAddress
fetchEnsAvatar
is nowgetEnsAvatar
fetchEnsName
is nowgetEnsName
fetchEnsResolver
is nowgetEnsResolver
fetchToken
is nowgetToken
fetchTransaction
is nowgetTransaction
switchNetwork
is nowswitchChain
waitForTransaction
is nowwaitForTransactionReceipt