On This Page

  • React
  • Migration Guide

Migration Guide

If you are coming from an earlier version of wagmi, you will need to make sure to update the following APIs listed below.

1.x.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.12.x docs here.

General

Removed ethers peer dependency

The ethers peer dependency has been removed in favour of viem.

npm uninstall ethers
npm i wagmi viem@latest

If your project is using modules from ethers directly, that are dependant on wagmi (e.g. BigNumber, etc), you will need to migrate to the viem-equivalent module. Check out the Ethers.js → viem migration guide.

If you have usages of ethers that are independent of wagmi, it is highly recommended to migrate to viem to take advantage of the smaller bundle size.

"Provider" & "Signer" terminology

Ethers Provider & Signer terminology is now known as viem's Public Client & Wallet Client terminology.

This directly affects:

  • createClient
  • configureChains
  • useProvider (now usePublicClient)
  • useSigner (now useWalletClient)
  • useWebSocketProvider (now useWebSocketPublicClient)

createClient

import { WagmiConfig, createConfig } from 'wagmi'
- import { getDefaultProvider } from 'ethers'
+ import { createPublicClient, http } from 'viem'
 
const config = createConfig({
  autoConnect: true,
- provider: getDefaultProvider(),
+ publicClient: createPublicClient({
+  chain: mainnet,
+  transport: http()
+ })
})

Note: You may notice that createClient is now createConfig. Don't worry – the migration for that is below.

configureChains

import { WagmiConfig, createConfig, configureChains, mainnet } from 'wagmi'
import { publicProvider } from 'wagmi/providers/public'
 
const {
  chains,
- provider,
+ publicClient,
- webSocketProvider,
+ webSocketPublicClient
} = configureChains(
  [mainnet],
  [publicProvider()],
)
 
const config = createConfig({
  autoConnect: true,
- provider,
+ publicClient,
- webSocketProvider,
+ webSocketPublicClient
})

useProvider

- import { useProvider } from 'wagmi'
+ import { usePublicClient } from 'wagmi'
 
- const provider = useProvider()
+ const publicClient = usePublicClient()

useSigner

- import { useSigner } from 'wagmi'
+ import { useWalletClient } from 'wagmi'
 
- const { data: signer } = useSigner()
+ const { data: walletClient } = useWalletClient()

useWebSocketProvider

- import { useWebSocketProvider } from 'wagmi'
+ import { useWebSocketPublicClient } from 'wagmi'
 
- const webSocketProvider = useWebSocketProvider()
+ const webSocketPublicClient = useWebSocketPublicClient()

Types

  • ProviderPublicClient
  • SignerWalletClient
  • WebSocketProviderWebSocketPublicClient

createClientcreateConfig

The createClient function has been renamed to createConfig.

- import { createClient } from 'wagmi'
+ import { createConfig } from 'wagmi'
 
- const client = createClient({
+ const config = createConfig({
  ...
})

useClientuseConfig

If you are using useClient, that has been renamed to useConfig:

- import { useClient } from 'wagmi'
+ import { useConfig } from 'wagmi'
 
- const client = useClient()
+ const config = useConfig()

BigNumber → native BigInt

All BigNumber instances are now platform native BigInt instances. This means you can no longer use arithmatic methods such as .add, .subtract, etc because BigInt is just a primitive type like number.

- const value = BigNumber.from('69420')
+ const value = 69420n
- const value = BigNumber.from('69420')
+ const value = 69420n
 
- const value2 = BigNumber.from('42069')
+ const value2 = 42069n
 
- const value3 = value.add(value2)
+ const value3 = value + value2
⚠️

Using native BigInt with JSON.stringify will raise a TypeError as BigInt values are not serializable. Read here for instructions to mitigate.

from has been renamed to account

The from attribute has been renamed to account. Directly affects Actions that consist of a from parameter.

const { config } = usePrepareContractWrite({
  ...,
- from: '0x...'
+ account: '0x...'
})

gasLimit has been renamed to gas

The gasLimit attribute has been renamed to gas, and still implies the same meaning. It was renamed to align closer to EIP-1474 and enforce consistency. Aligning closer to EIP-1474 means that you will not need to re-map from gas to gasLimit if you are deriving from an external source.

useSendTransaction({
  request: {
    to: 'jxom.eth',
-   gasLimit: 69420n,
+   gas: 69420n,
    value: 1n
  }
})

Removed goerli export

Removed the goerli export from wagmi. Goerli is now a deprecated testnet. You will now have to import it from wagmi/chains.

-import { goerli } from 'wagmi'
+import { goerli } from 'wagmi/chains'

Removed window.ethereum global type

The window.ethereum global type has been removed in favor of an explicit wagmi/window import.

+import 'wagmi/window';
 
const isMetaMask = window.ethereum.isMetaMask

Renamed the Ethereum type to WindowProvider

The Ethereum type has been renamed to WindowProvider.

Hooks

useConnect

No longer returns the provider property in data. Use connector.getProvider() instead.

const { data } = useConnect({
  ...
})
 
- const provider = data.provider
+ const provider = await data.connector.getProvider()

No longer returns the provider property in connectAsync return type. Use connector.getProvider() instead.

const { connectAsync } = useConnect({
  ...
})
 
const result = await connectAsync(...)
- const provider = result.provider
+ const provider = await result.connector.getProvider()

useBalance

Returns a bigint instead of BigNumber

Returns a bigint instead of BigNumber for data.value. Maps to viem's getBalance.

const { data } = useBalance({
  ...
})
 
const value = data.value
//    ^? const value: bigint

useBlockNumber

Returns a bigint instead of number

Returns a bigint instead of number for data. Maps to viem's getBlockNumber.

const { data } = useBlockNumber({
  //    ^? const data: bigint
  ...
})

useContract

Hook Removed.

Use the getContract Action instead or declarative Hook (e.g. useContractRead, useContractWrite, etc.).

- import { useContract } from 'wagmi'
+ import { getContract } from 'wagmi/actions'

useEnsAvatar

Replaced address with name

Replaced address with name in order to remove internal async call to resolve address to name. Maps to viem's getEnsAvatar.

+ const { data: ensName } = useEnsName({
+  address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
+ })
const { data: avatar } = useEnsAvatar({
- address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
+ name: ensName,
})

useFeeData

Return type changes

Returns bigint instead of a BigNumber for gasPrice , maxFeePerGas , and maxPriorityFeePerGas.

const { data } = useFeeData()
data.gasPrice
//      ^? const gasPrice: bigint
data.maxFeePerGas
//      ^? const maxFeePerGas: bigint
data.maxPriorityFeePerGas
//      ^? const maxPriorityFeePerGas: bigint

useProvider

Renamed to usePublicClient

Returns viem Public Client instead of ethers Provider.

- const provider = useProvider(...)
+ const publicClient = usePublicClient(...)

useSigner

Renamed to getWalletClient

Returns viem Wallet Client instead of ethers Signer.

- const { data } = useSigner(...)
//        ^? const data: Signer
+ const { data } = useWalletClient(...)
//        ^? const data: WalletClient

useToken

Returns a bigint

Returns bigint instead of a BigNumber for totalSupply.value.

const { data } = useToken({
  ...
})
 
const value = data.totalSupply.value
//    ^? const value: bigint

useTransaction

Returns viem Transaction

Returns viem Transaction instead of an ethers Transaction. Maps to viem's getTransaction.

const { data } = useTransaction({
  //    ^? const data: Transaction
  ...
})

useContractReads

Return type structure changed

When allowFailure is truthy (default), the return structure is now in the form of { error, result, status }[] instead of an array of contract function results (Result[]).

const { data } = useContractReads({
  ...
})
 
- const result = data[0]
+ const { result } = data[0]

The return type when allowFailure is falsy has not changed.

Removed console.warn logs for failing contract methods

The console.warn logs for failing contract methods has been removed. Failing methods can now be extracted from the error property of the return type.

const { data } = useContractReads({
  ...
})
 
+ useEffect(() => {
+   if (data)
+     data.forEach(({ error, status }) => {
+       if (status === 'failure') console.warn(error.message)
+     })
+ }, [data])

Removed overrides

The overrides parameter has been removed in favor of top-level blockNumber & blockTag parameters.

const { data } = useContractReads({
  ...
- overrides: {
-   blockTag: 'safe'
- }
+ blockTag: 'safe'
})

useContractInfiniteReads

Return type structure changed

When allowFailure is truthy (default), the return structure is now in the form of { error, result, status }[] instead of an array of contract function results (Result[]).

const { data } = useContractInfiniteReads({
  ...
})
 
- const result = data.pages[0][0]
+ const { result } = data.pages[0][0]

The return type when allowFailure is falsy has not changed.

Removed console.warn logs for failing contract methods

The console.warn logs for failing contract methods has been removed. Failing methods can now be extracted from the error property of the return type.

const { data } = useContractInfiniteReads({
  ...
})
 
+ useEffect(() => {
+   if (data)
+     data.pages.flat().forEach(({ error, status }) => {
+       if (status === 'failure') console.warn(error.message)
+     })
+ }, [data])

Removed overrides

The overrides parameter has been removed in favor of top-level blockNumber & blockTag parameters.

const { data } = useContractInfiniteReads({
  ...
- overrides: {
-   blockTag: 'safe'
- }
+ blockTag: 'safe'
})

usePrepareSendTransaction

Removed request

The request parameter has been removed in favor of top-level parameters. Maps to viem's sendTransaction parameters.

const { config } = usePrepareSendTransaction({
- request: {
-   to: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
-   value: BigNumber.from('69420'),
- },
+ to: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
+ value: 69420n
})

usePrepareContractWrite

Removed overrides

The overrides parameter has been removed in favor of top-level parameters. Maps to viem's simulateContract parameters.

const { config } = usePrepareContractWrite({
  address: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
  abi: wagmigotchiABI,
  functionName: 'feed',
- overrides: {
-   from: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
-   value: BigNumber.from('69420'),
- },
+ account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
+ value: 69420n
})

Return type structure changed

The returned config.request now returns the shape of viem's WriteContractParameters, instead of Ethers' TransactionRequest.

Removed abi, address, functionName from the return value, they now belong in config.request.

useContractEvent

Callback now returns array of logs

Callback now returns an array of Event Logs (with included decoded args), instead of positional decoded args with the log.

const unwatch = useContractEvent({
  address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
  abi: ensRegistryABI,
  eventName: 'NewOwner',
- listener: (node, label, owner) => {
+ listener: (logs) => {
-   console.log(node, label, owner)
+   const { args } = logs[0]
+   console.log(args.node, args.label, args.owner)
  },
})

Removed once

The once parameter has been removed. Use unwatch to cleanup the listener instead.

const unwatch = useContractEvent({
  address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
  abi: ensRegistryABI,
  eventName: 'NewOwner',
- once: true,
  listener: (logs) => {
    const { args } = logs[0]
    console.log(args.node, args.label, args.owner)
+   unwatch()
  }
})

useContractRead

Removed overrides

The overrides parameter has been removed in favor of top-level blockNumber & blockTag parameters.

const { data } = useContractRead({
  ...
- overrides: {
-   blockTag: 'safe'
- }
+ blockTag: 'safe'
})

useContractWrite

Removed requirement for "prepare" mode

Removed the requirement for "prepare" mode. You can now use useContractWrite without calling usePrepareContractWrite first.

const { data, isLoading, isSuccess, write } = useContractWrite({
- mode: 'recklesslyUnprepared',
  address: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
  abi: wagmigotchiABI,
  functionName: 'claim',
  tokenId: 69,
})

wait has been removed from the return type

wait has been removed from the return type, favor waitForTransaction instead.

+ import { waitForTransaction } from 'wagmi/actions'
 
const { data } = useContractWrite(...)
const {
- wait,
+ hash,
} = data || {}
 
- const receipt = await wait()
+ const receipt = await waitForTransaction({ hash })

Removed overrides

The overrides parameter has been removed in favor of top-level parameters. Maps to viem's writeContract parameters.

const { config } = useContractWrite({
  ...
- overrides: {
-   gasLimit: BigNumber.from('42069'),
-   value: BigNumber.from('69420'),
- },
+ gasLimit: 42069n,
+ value: 69420n
})

Renamed recklesslySetUnpreparedArgs to args

const { data, write } = useContractWrite({
  ...
})
 
write({
- recklesslySetUnpreparedArgs: [69],
+ args: [69],
})

Removed recklesslySetUnpreparedOverrides

Removed recklesslySetUnpreparedOverrides. They now exist at the top-level of the function.

const { data, write } = useContractWrite({
  ...
})
 
write({
- recklesslySetUnpreparedOverrides: {
-   gasLimit: BigNumber.from('42069'),
-   value: BigNumber.from('69420'),
- },
+ gas: 42069n,
+ value: 69420n
})

Return type changed

Now returns a viem TransactionReceipt instead of an Ethers TransactionReceipt.

useSendTransaction

Removed requirement for "prepare" mode

Removed the requirement for "prepare" mode. You can now use useContractWrite without calling usePrepareContractWrite first.

const { data, isLoading, isSuccess, sendTransaction } = useSendTransaction({
- mode: 'recklesslyUnprepared',
  request: {
    to: 'moxey.eth',
    value: BigNumber.from('10000000000000000'),
  }
})

Removed request

The request parameter has been removed in favor of top-level parameters. Maps to viem's sendTransaction parameters.

const { hash } = useSendTransaction({
  ...
- request: {
-   to: 'jxom.eth',
-   value: BigNumber.from('69420'),
- },
+ to: 'jxom.eth',
+ value: 69420n
})

Renamed recklesslySetUnpreparedRequest to request

const { sendTransaction } = useSendTransaction({
  ...
})
 
write({
- recklesslySetUnpreparedRequest: { ... },
+ request: { ... },
})

wait has been removed from the return type

wait has been removed from the return type, favor waitForTransaction instead.

+ import { waitForTransaction } from 'wagmi/actions'
 
const { data } = useSendTransaction(...)
const {
- wait,
+ hash,
} = data || {}
 
- const receipt = await wait()
+ const receipt = await waitForTransaction({ hash })

useSignMessage

message no longer accepts a byte array

message no longer accepts a byte array, only a string value

useSignTypedData

value has been renamed to message

const signature = useSignTypedData({
  domain,
  types,
  primaryType: 'Mail',
- value,
+ message
})

primaryType is now required

The primaryType attribute is now required. Aligns closer to EIP-712, and allows consumers to specify an alternative primary type. Previously, Ethers.js did some internal stuff to figure out the primary type. But it's not hard for a consumer to just provide that – and we believe it is more clear.

const signature = useSignTypedData({
  domain,
  types,
+ primaryType: 'Mail',
  message
})

useWaitForTransaction

Renamed onSpeedUp to onReplaced

const waitForTransaction = useWaitForTransaction({
  hash: '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060',
- onSpeedUp: (transaction) => console.log(transaction),
+ onReplaced: (transaction) => console.log(transaction)
})

useWatchPendingTransactions

Callback now returns batched transaction hashes

The callback now returns a batched array of transaction hashes, instead of callback being emitted several times with singular hashes.

useWatchPendingTransactions(
- listener: (hash) => console.log(hash),
+ listener: (hashes) => console.log(hashes[0]),
)

useWebSocketProvider

Renamed to useWebSocketPublicClient

Returns viem Public Client instead of ethers Provider.

- const webSocketProvider = useWebSocketProvider(...)
+ const webSocketPublicClient = useWebSocketPublicClient(...)

configureChains

Removed quorum support

Removed quorum support: priority, minQuorum and targetQuorum (for now). viem does not support quorum in the fallback Transport yet, but will in the future!

Connectors

The breaking changes below only applies if you are building a Connector on top of wagmi.

Renamed getSigner to getWalletClient

The getSigner method has been renamed to getWalletClient, and also returns viem's WalletClient instead of an Ethers.js Signer

- async getSigner({ chainId }: { chainId?: number } = {}) {
+ async getWalletClient({ chainId }: { chainId?: number } = {}) {
  const [provider, account] = await Promise.all([
    this.getProvider(),
    this.getAccount(),
  ])
+ const chain = this.chains.find((x) => x.id === chainId) || this.chains[0]
- return new providers.Web3Provider(
-   provider,
-   chainId,
- ).getSigner(account)
+ return createWalletClient({
+   account,
+   chain,
+   transport: custom(provider),
+ })
}

Errors

ChainDoesNotSupportMulticallError

Use ChainDoesNotSupportContract from viem instead.

ContractMethodDoesNotExistError

Use ContractFunctionExecutionError from viem instead.

ContractMethodNoResultError

Use ContractFunctionZeroDataError from viem instead.

ContractMethodRevertedError

Use ContractFunctionRevertedError from viem instead.

ContractResultDecodeError

Use ContractFunctionExecutionError from viem instead.

ProviderRpcError

Use ProviderRpcError from viem instead.

ResourceUnavailableError

Use ResourceUnavailableRpcError from viem instead.

0.12.x Breaking changes

WalletConnectConnector

WalletConnect v1 has been sunset and WalletConnectConnector now uses WalletConnect v2 by default.

wagmi still supports WalletConnect v1 via a WalletConnectLegacyConnector, however, it is recommended to migrate to WalletConnect v2. Instructions can be found here.

If you are already using WalletConnect v2:

The version flag has been omitted, and qrcode has been renamed to showQrModal.

import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'
 
const connector = new WalletConnectConnector({
  options: {
-   version: '2',
    projectId: '...',
-   qrcode: true,
+   showQrModal: true,
  },
})

Read more on WalletConnectConnector

If you are still using WalletConnect v1:
⚠️

You must migrate to the WalletConnect v2 Connector before June 28, after which, the WalletConnectLegacyConnector will be removed.

-import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'
+import { WalletConnectLegacyConnector } from 'wagmi/connectors/walletConnectLegacy'
 
-const connector = new WalletConnectConnector({
+const connector = new WalletConnectLegacyConnector({
  options: {
    qrcode: true,
  },
})

Read more on WalletConnectLegacyConnector

0.11.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.10.x docs here.

Upgrade to typescript@>=4.9.4

TypeScript 5.0 is coming soon and has some great features we are excited to bring into wagmi. To prepare for this, update your TypeScript version to 4.9.4 or higher. There are likely no breaking changes if you are coming from typescript@4.7.x || typescript@4.8.x.

0.10.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.9.x docs here.

useSigner

The useSigner hook now always returns undefined when no signer is present. Previously, it returned null or undefined.

When no signer is present, the hook will be in an "idle" status.

0.9.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.8.x docs here.

Chain exports

With the introduction of the wagmi/chains entrypoint, wagmi no longer exports the following:

  • chain
  • allChains
  • defaultChains
  • defaultL2Chains
  • chainId
  • etherscanBlockExplorers
  • alchemyRpcUrls, infuraRpcUrls, publicRpcUrls

Read below for migration steps.

Removed chain

The chain export has been removed. wagmi now only exports the mainnet & goerli chains. If you need to use an alternative chain (polygon, optimism, etc), you will need to import it from the wagmi/chains entrypoint.

import {
- chain
  configureChains
} from 'wagmi'
+ import { mainnet, polygon, optimism } from 'wagmi/chains'
 
const { ... } = configureChains(
- [chain.mainnet, chain.polygon, chain.optimism],
+ [mainnet, polygon, optimism],
  {
    ...
  }
)

Removed allChains

The allChains export has been removed. If you need a list of all chains, you can utilize wagmi/chains entrypoint.

- import { allChains } from 'wagmi'
+ import * as allChains from 'wagmi/chains'
 
const { ... } = configureChains(allChains, ...)

Removed defaultChains & defaultL2Chains

The defaultChains & defaultL2Chains exports have been removed. If you still need the defaultChains or defaultL2Chains exports, you can build them yourself:

- import { defaultChains } from 'wagmi'
+ import { mainnet, goerli } from 'wagmi/chains'
 
+ const defaultChains = [mainnet, goerli]

The defaultChains export was previously populated with mainnet & goerli.

- import { defaultL2Chains } from 'wagmi'
+ import {
+   arbitrum,
+   arbitrumGoerli,
+   polygon,
+   polygonMumbai,
+   optimism,
+   optimismGoerli
+ } from 'wagmi/chains'
 
+ const defaultL2Chains = [
+  arbitrum,
+  arbitrumGoerli,
+  polygon,
+  polygonMumbai,
+  optimism
+  optimismGoerli
+ ]

The defaultL2Chains export was previously populated with arbitrum & optimism.

Removed chainId

The chainId export has been removed. You can extract a chain ID from the chain itself.

- import { chainId } from 'wagmi'
+ import { mainnet, polygon, optimism } from 'wagmi/chains'
 
-const mainnetChainId = chainId.mainnet
-const polygonChainId = chainId.polygon
-const optimismChainId = chainId.optimism
+const mainnetChainId = mainnet.id
+const polygonChainId = polygon.id
+const optimismChainId = optimism.id

Removed etherscanBlockExplorers

The etherscanBlockExplorers export has been removed. You can extract a block explorer from the chain itself.

- import { etherscanBlockExplorers } from 'wagmi'
+ import { mainnet, polygon, optimism } from 'wagmi/chains'
 
-const mainnetEtherscanBlockExplorer = etherscanBlockExplorers.mainnet
-const polygonEtherscanBlockExplorer = etherscanBlockExplorers.polygon
-const optimismEtherscanBlockExplorer = etherscanBlockExplorers.optimism
+const mainnetEtherscanBlockExplorer = mainnet.blockExplorers.default
+const polygonEtherscanBlockExplorer = polygon.blockExplorers.default
+const optimismEtherscanBlockExplorer = optimism.blockExplorers.default

Removed alchemyRpcUrls, infuraRpcUrls & publicRpcUrls

The alchemyRpcUrls, infuraRpcUrls & publicRpcUrls exports have been removed. You can extract a RPC URL from the chain itself.

- import { alchemyRpcUrls, infuraRpcUrls, publicRpcUrls } from 'wagmi'
+ import { mainnet } from 'wagmi/chains'
 
-const mainnetAlchemyRpcUrl = alchemyRpcUrls.mainnet
-const mainnetInfuraRpcUrl = infuraRpcUrls.mainnet
-const mainnetOptimismRpcUrl = publicRpcUrls.mainnet
+const mainnetAlchemyRpcUrl = mainnet.rpcUrls.alchemy
+const mainnetInfuraRpcUrl = mainnet.rpcUrls.infura
+const mainnetOptimismRpcUrl = mainnet.rpcUrls.optimism

Chain type

RPC URLs

The rpcUrls shape has changed to include an array of URLs, and also the transport method (http or webSocket):

type Chain = {
  ...
  rpcUrls: {
-   [key: string]: string
+   [key: string]: {
+     http: string[]
+     webSocket: string[]
+   }
  }
  ...
}

Note that you will also need to ensure that usage is migrated:

- const rpcUrl = mainnet.rpcUrls.alchemy
+ const rpcUrl = mainnet.rpcUrls.alchemy.http[0]

Contracts

The multicall and ens attributes have been moved into the contracts object:

type Contract = {
  address: Address
  blockCreated?: number
}
 
type Chain = {
  ...
- multicall: Contract
- ens: Contract
+ contracts: {
+   multicall3: Contract
+   ensRegistry: Contract
+ }
  ...
}

Note that you will also need to ensure that usage is migrated:

- const multicallContract = mainnet.multicall
+ const multicallContract = mainnet.contracts.multicall3

useEnsResolver

Behavioral changes

useEnsResolver's result is no longer persisted by the query client since it cannot serialize its prototype methods.

Configuration changes

Removed the cacheTime and staleTime config options.

const { data } = useEnsResolver({
  name: 'wagmi-dev.eth',
- cacheTime: 2000,
- staleTime: 2000,
})

useWaitForTransaction

Behavioral changes

useWaitForTransaction will now return an error (and invoke the onError callback) if the transaction has been reverted or cancelled.

Configuration changes

Removed the wait config option on useWaitForTransaction. Use the transaction hash instead.

const { data } = useWaitForTransaction({
- wait: transaction.wait,
+ hash: transaction.hash,
})

0.8.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.7.x docs here.

CommonJS Support Dropped

wagmi no longer supports CommonJS and only supports ES Modules. If you are using modern tooling, like Next.js, Vite, or Vitest, you likely don't need to do anything! Remix and Jest require some additional configuration. Check out this guide for more info on ESM support and Frequently Asked Questions across various tools and setups.

Deprecated chains removed

Removed the following deprecated chains:

  • ropsten
  • rinkeby
  • kovan
  • optimismKovan
  • arbitrumRinkeby

If you feel you still need to include one of these testnets in your application, you will have to define it manually:

-import { chain } from 'wagmi'
+import { Chain } from 'wagmi'
 
-export const rinkeby = chain.rinkeby
+export const rinkeby: Chain = {
+ id: 4,
+ name: 'Rinkeby',
+ network: 'rinkeby',
+ nativeCurrency: { name: 'Rinkeby Ether', symbol: 'ETH', decimals: 18 },
+ rpcUrls: {
+   alchemy: 'https://eth-rinkeby.alchemyapi.io/v2',
+   default: 'https://rpc.ankr.com/eth_rinkeby',
+   infura: 'https://rinkeby.infura.io/v3',
+   public: 'https://rpc.ankr.com/eth_rinkeby',
+  },
+ blockExplorers: {
+   etherscan: 'https://rinkeby.etherscan.io',
+   default: 'https://rinkeby.etherscan.io',
+ },
+ ens: {
+   address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
+ },
+ multicall: {
+   address: '0xca11bde05977b3631167028862be2a173976ca11',
+   blockCreated: 10299530,
+ },
+ testnet: true,
}

You can reference these removed chains here.

alchemyProvider

Made apiKey required on alchemyProvider.

import { configureChains } from 'wagmi'
 
const config = configureChains(defaultChains, [
- alchemyProvider(),
+ alchemyProvider({ apiKey: process.env.ALCHEMY_API_KEY })
])

You can find your Alchemy API key from the Alchemy Dashboard, or your Infura API key from the Infura Dashboard.

infuraProvider

Made apiKey required on infuraProvider.

import { configureChains } from 'wagmi'
 
const config = configureChains(defaultChains, [
- infuraProvider(),
+ infuraProvider({ apiKey: process.env.INFURA_API_KEY })
])

You can find your Infura API key from the Infura Dashboard, or your Infura API key from the Infura Dashboard.

useBalance

Configuration changes

addressOrName renamed to address.

const result = useBalance({
- addressOrName: '0x…',
+ address: '0x…',
})

If you were using an ENS name instead of an address, you can resolve the name to an address before passing it to the action. This allows the ENS-to-address resolution to be cached instead of being wasted work before.

+ const { data: address } = useEnsAddress({ name: 'example.eth' })
const result = useBalance({
- addressOrName: 'example.eth',
+ address,
})

useEnsAvatar

Configuration changes

addressOrName renamed to address.

const result = useEnsAvatar({
- addressOrName: '0x…',
+ address: '0x…',
})

If you were using an ENS name instead of an address, you can resolve the name to an address before passing it to the action. This allows the ENS-to-address resolution to be cached instead of being wasted work before.

+ const { data: address } = useEnsAddress({ name: 'example.eth' })
const result = useEnsAvatar({
- addressOrName: 'example.eth',
+ address,
})

0.7.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.6.x docs here.

Upgrade to typescript@>=4.7.4

wagmi can now infer types based on ABI and EIP-712 Typed Data definitions, giving you full end-to-end type-safety from your contracts to your frontend and incredible developer experience (e.g. autocomplete ABI function names and catch misspellings, type ABI function arguments, etc.).

For this to work, you must upgrade to typescript@>=4.7.4. Why is TypeScript v4.7.4 or greater necessary? TypeScript 4.7.4 introduced the ability to extend constraints on inferred type variables, which is used extensively to help narrow types for ABIs. Good news! When upgrading TypeScript from 4.6 to 4.7 there are likely no breaking changes for your set up.

See the wagmi TypeScript docs for more information on TypeScript support in this version.

When switching over to inferring types based on your ABIs, you may notice TypeScript complaining about types that worked fine before. For example, Type 'string' is not assignable to type 'BigNumber'. Previously, you were able to use string types in place of BigNumber types, BigNumber types in place of number types, etc.

The types inferred directly from ABIs are more correct and strict so you will need to convert your types to match. This should be pretty straightforward by following the error from TypeScript. Based on the error above, args: ['123'] should be updated to args: [BigNumber.from('123')].

addressOrName and contractInterface renamed for contract hooks

addressOrName and contractInterface renamed to address and abi respectively for the following contract hooks: useContract, useContractEvent, useContractRead, useContractReads, useContractInfiniteReads, useContractWrite, and usePrepareContractWrite.

import { useContractRead } from 'wagmi'
const result = useContractRead({
- addressOrName: '0x…',
+ address: '0x…',
- contractInterface: […],
+ abi: […],
  functionName: 'balanceOf',
  args: ['0x…'],
})

If you were using an ENS name instead of an address, you can resolve the name to an address before passing it to the action.

- import { useContractRead } from 'wagmi'
+ import { useContractRead, useEnsAddress } from 'wagmi'
+ const { data: address } = useEnsAddress({ name: 'example.eth' })
const result = useContractRead({
- addressOrName: 'example.eth',
+ address,
  abi: […],
  functionName: 'balanceOf',
  args: ['0x…'],
})

args type changed for contract hooks

args config option must now be an array for the following hooks: useContractRead, useContractWrite, usePrepareContractWrite, useContractReads, and useContractInfiniteReads.

const { data } = useContractRead({
  address: '0x…',
  abi: […],
  functionName: 'balanceOf',
- args: '0x…',
+ args: ['0x…'],
})

useContractEvent

Configuration changes

signerOrProvider was removed.

useContractInfiniteReads

Configuration changes

Updated paginatedIndexesConfig fn parameter return type. fn now returns an array instead of a single object.

useContractInfiniteReads({
  cacheKey: 'contracts',
  ...paginatedIndexesConfig(
-    (index) => ({
+    (index) => [{
      address: '0x…',
      abi: […],
      functionName: 'tokenURI',
      args: [BigNumber.from(index)] as const,
-    }),
+    }],
    { start: 0, perPage: 10, direction: 'increment' },
  ),
})

usePrepareContractWrite

Behavioral changes

Throws an error when a chainId is specified and the end-user is on a different chain id (the wrong network). If you wish to defer this check until the click handler is pressed, you can place the chainId in useContractWrite instead.

usePrepareSendTransaction

Behavioral changes

  • The usePrepareSendTransaction hook will now only run when the end-user is connected to their wallet. This is to reach parity with usePrepareContractWrite. If the end-user is not connected, then the usePrepareSendTransaction hook will remain idle.
  • Throws an error when a chainId is specified and the end-user is on a different chain id (the wrong network). If you wish to defer this check until the click handler is pressed, you can place chainId in useContractWrite instead.

0.6.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.5.x docs here.

All the breaking changes in this release are related to the introduction of Prepare Hooks. It is recommended to read the Prepare Hooks section before migrating to 0.6.x.

useContractWrite

💡

Not ready to migrate to the new useContractWrite yet? You can use the useDeprecatedContractWrite hook.

Behavioral changes

If a chainId is passed to useContractWrite, it will no longer attempt to switch chain before sending the transaction. Instead, it will throw an error if the user is on the wrong chain.

Why? Eagerly prompting to switch chain in these actions created a long-running async task that makes iOS App Links vulnerable.

Configuration changes

The configuration passed to the useContractWrite hook now needs to be either:

Why? To avoid long-running asynchronous tasks in event handlers. Read more about Prepare Hooks.

Prepared usage

+const { config } = usePrepareContractWrite({
+ addressOrName: '0x...',
+ contractInterface: wagmiAbi,
+ functionName: 'mint',
+ args: [tokenId]
+})
const { data } = useContractWrite({
- addressOrName: '0x...',
- contractInterface: wagmiAbi,
- functionName: 'mint',
- args: [tokenId],
+ ...config
})

Recklessly unprepared usage

If you are not ready to upgrade to usePrepareContractWrite, it is possible to use useContractWrite without preparing the configuration first by passing mode: 'recklesslyUnprepared'.

const { data } = useContractWrite({
+ mode: 'recklesslyUnprepared',
  addressOrName: '0x...',
  contractInterface: wagmiAbi,
  functionName: 'mint',
  args: [tokenId],
})

Return value changes

data now only returns hash & wait

data now returns an object only consisting of hash & wait, and not the full TransactionResponse.

If you require the full TransactionResponse, you can use useTransaction:

const {
  data: {
    hash,
    wait,
-   ...transaction
  }
} = useContractWrite(...)
+const { data: transaction } = useTransaction({ hash })

Why? The old implementation of useContractWrite created a long-running async task, causing UX pitfalls when invoked in a click handler.

write/writeAsync arguments

The write/writeAsync configuration object has now been altered to only accept "reckless" configuration. If one or more of these values are set, it can lead to UX pitfalls.

<button
  onClick={() => {
    write({
-     args: [1],
-     overrides: { from: '0x...' }
+     recklesslySetUnpreparedArgs: [1],
+     recklesslySetUnpreparedOverrides: '0x...'
    })
  }}
>
  Send
</button>

write/writeAsync can be undefined

When useContractWrite is in "prepare mode" (used with usePrepareContractWrite), write/writeAsync will be undefined until the configuration has been prepared. Ensure that your usage reflects this.

const { config } = usePrepareContractWrite({ ... })
const { write } = useContractWrite(config)
 
<button
  disabled={!write}
  onClick={() => write?.()}
>
  Send
</button>

useSendTransaction

💡

Not ready to migrate to the new useSendTransaction yet? You can use the useDeprecatedSendTransaction hook.

Behavioral changes

If a chainId is passed to useSendTransaction, it will no longer attempt to switch chain before sending the transaction. Instead, it will throw an error if the user is on the wrong chain.

Why? Eagerly prompting to switch chain in these actions created a long-running async task that makes iOS App Links vulnerable.

Configuration changes

The configuration passed to the useSendTransaction hook now needs to be either:

Why? To avoid long-running asynchronous tasks in event handlers. Read more about Prepare Hooks.

Prepared usage

import { usePrepareSendTransaction, useSendTransaction } from 'wagmi'
 
+const { config } = usePrepareSendTransaction({
+  request: {
+    to: 'moxey.eth',
+    value: parseEther('1'),
+  }
+})
const { data } = useSendTransaction({
- request: {
-   to: 'moxey.eth',
-   value: parseEther('1')
- }
+ ...config
})

Recklessly unprepared usage

If you are not ready to upgrade to usePrepareSendTransaction, it is possible to use useSendTransaction without preparing the configuration first by passing mode: 'recklesslyUnprepared'.

import { useSendTransaction } from 'wagmi'
 
const { data } = useSendTransaction({
+ mode: 'recklesslyUnprepared',
  request: {
    to: 'moxey.eth',
    value: parseEther('1'),
  }
})

Return value changes

data now only returns hash & wait

data now returns an object only consisting of hash & wait, and not the full TransactionResponse.

If you require the full TransactionResponse, you can use useTransaction:

const {
  data: {
    hash,
    wait,
-   ...transaction
  }
} = useSendTransaction(...)
+const { data: transaction } = useTransaction({ hash })

Why? The old implementation of useSendTransaction created a long-running async task, causing UX pitfalls when invoked in a click handler.

sendTransaction/sendTransactionAsync arguments

The sendTransaction/sendTransactionAsync configuration object has now been altered to only accept "reckless" configuration. If one or more of these values are set, it can lead to UX pitfalls.

<button
  onClick={() => {
    sendTransaction({
-     request: {
+     recklesslySetUnpreparedRequest:
        to: 'moxey.eth',
        value: parseEther('1')
      }
    })
  }}
>
  Send
</button>

sendTransaction/sendTransactionAsync can be undefined

When useSendTransaction is in "prepare mode" (used with usePrepareSendTransaction), sendTransaction/sendTransactionAsync will be undefined until the configuration has been prepared. Ensure that your usage reflects this.

const { config } = usePrepareSendTransaction({ ... })
const { sendTransaction } = useSendTransaction(config)
 
<button
  disabled={!sendTransaction}
  onClick={() => sendTransaction?.()}
>
  Send
</button>

alchemyProvider and infuraProvider

alchemyProvider and infuraProvider now use a generic apiKey configuration option instead of alchemyId and infuraId.

import { alchemyProvider } from 'wagmi/providers/alchemy'
import { infuraProvider } from 'wagmi/providers/infura'
 
alchemyProvider({
-  alchemyId: 'yourAlchemyApiKey',
+  apiKey: 'yourAlchemyApiKey',
})
 
infuraProvider({
-  infuraId: 'yourInfuraApiKey',
+  apiKey: 'yourInfuraApiKey',
})

0.5.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.4.x docs here.

WagmiConfig

The client prop is now required on WagmiConfig.

```tsx
import {
  createClient,
+ configureChains,
+ defaultChains
} from 'wagmi'
+import { publicProvider } from 'wagmi/providers/public'
 
+const { provider, webSocketProvider } = configureChains(defaultChains, [
+ publicProvider(),
+])
 
+const client = createClient({
+ provider,
+ webSocketProvider,
+})
 
function App() {
  return (
    <WagmiConfig
+     client={client}
    >
      <YourRoutes />
    </WagmiConfig>
  )
}

createClient

provider is now required

The provider config option is now required on createClient. It is recommended to pass the provider given from configureChains.

import {
  createClient,
+ defaultChains,
+ configureChains
} from 'wagmi'
+import { publicProvider } from 'wagmi/providers/publicProvider'
 
+const { provider } = configureChains(defaultChains, [
+ publicProvider
+])
 
const client = createClient({
+ provider
})

If you previously used an ethers.js Provider, you now need to provide your chains on the Provider instance:

import {
  createClient,
+ defaultChains
} from 'wagmi'
import ethers from 'ethers'
 
const client = createClient({
- provider: getDefaultProvider()
+ provider: Object.assign(getDefaultProvider(), { chains: defaultChains })
})

chainId removed from connectors

Removed the chainId parameter from connectors function on createClient.

const client = createClient({
- connectors({ chainId }) {
+ connectors() {
    ...
  }
})

If you previously derived RPC URLs from the chainId on connectors, you can now remove that logic as wagmi now handles RPC URLs internally when used with configureChains.

import {
  chain,
+  configureChains,
  createClient
} from 'wagmi';
 
+import { publicProvider } from 'wagmi/providers/public'
 
import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'
import { InjectedConnector } from 'wagmi/connectors/injected'
import { MetaMaskConnector } from 'wagmi/connectors/metaMask'
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'
 
+const { chains } = configureChains(
+  [chain.mainnet],
+  [publicProvider()]
+);
 
const client = createClient({
-  connectors({ chainId }) {
-    const chain = chains.find((x) => x.id === chainId) ?? defaultChain
-    const rpcUrl = chain.rpcUrls.alchemy
-      ? `${chain.rpcUrls.alchemy}/${alchemyId}`
-      : chain.rpcUrls.default
-    return [
+  connectors: [
    new MetaMaskConnector({ chains }),
    new CoinbaseWalletConnector({
      chains,
      options: {
        appName: 'wagmi',
-       chainId: chain.id,
-       jsonRpcUrl: rpcUrl,
      },
    }),
    new WalletConnectConnector({
      chains,
      options: {
        qrcode: true,
-       rpc: { [chain.id]: rpcUrl },
      },
    }),
    new InjectedConnector({
      chains,
      options: { name: 'Injected' },
    }),
  ]
-  },
})

useAccount

Return value changes

The data value is now address & connector

{
- data?: {
-   address: string
-   connector: Connector
- }
+ address?: string
+ connector?: Connector
}

Global connection status values have been added

The following global connection status values have been added:

{
+ isConnecting: boolean
+ isReconnecting: boolean
+ isConnected: boolean
+ isDisconnected: boolean
+ status: 'connecting' | 'reconnecting' | 'connected' | 'disconnected'
}

The useAccount hook is now aware of any connection event in your application, so now you can use these connection status values to determine if your user is connected, disconnected or connecting to a wallet on a global scope.

error, states & refetch values have been removed

Since the useAccount hook never dealt with asynchronous data, all of these values were redundant & unused.

{
- error?: Error
- isIdle: boolean
- isLoading: boolean
- isFetching: boolean
- isSuccess: boolean
- isError: boolean
- isFetched: boolean
- isRefetching: boolean
- refetch: (options: {
-   throwOnError: boolean
-   cancelRefetch: boolean
- }) => Promise<{
-   address: string
-   connector: Connector
- }>
- status: 'idle' | 'error' | 'loading' | 'success'
}

Summary of changes

Below is the whole diff of changes to the useAccount return value.

{
- data?: {
-   address: string
-   connector: Connector
- }
+ address?: string
+ connector?: Connector
- error?: Error
- isIdle: boolean
- isLoading: boolean
- isFetching: boolean
- isSuccess: boolean
- isError: boolean
- isFetched: boolean
- isRefetching: boolean
+ isConnecting: boolean
+ isReconnecting: boolean
+ isConnected: boolean
+ isDisconnected: boolean
- refetch: (options: {
-   throwOnError: boolean
-   cancelRefetch: boolean
- }) => Promise<{
-   address: string
-   connector: Connector
- }>
- status: 'idle' | 'error' | 'loading' | 'success'
+ status: 'connecting' | 'reconnecting' | 'connected' | 'disconnected'
}

Configuration changes

onConnect has been added

The onConnect callback is invoked when the account connects.

It provides the connected address & connector, as well as a isReconnected flag for if the user reconnected via autoConnect.

const account = useAccount({
  onConnect({ address, connector, isReconnected }) {
    console.log('Connected')
  },
})

onDisconnect has been added

The onDisconnect callback is invoked when the account disconnected.

const account = useAccount({
  onDisconnect() {
    console.log('Disconnected')
  },
})

suspense has been removed

The useAccount hook is a synchronous hook – so suspense never worked.

const account = useAccount({
-  suspense: true,
})

onError has been removed

The useAccount hook never had any error definitions – so onError was never invoked.

const account = useAccount({
- onError(error) {
-   console.log('Error', error)
- },
})

onSettled has been removed

The useAccount hook is a synchronous hook. onSettled was always invoked immediately.

const account = useAccount({
- onSettled(data) {
-   console.log('Settled', data)
- },
})

If you used onSettled, you can move the code beneath the useAccount hook:

const account = useAccount({
- onSettled(data) {
-   console.log('Address:', data.address)
- },
})
+ console.log('Address:', account.address)

onSuccess has been removed

The useAccount hook is a synchronous hook. onSuccess was always invoked immediately.

const account = useAccount({
- onSuccess(data) {
-   console.log('Success', data)
- },
})

If you used onSuccess, you can move the code beneath the useAccount hook:

const account = useAccount({
- onSuccess(data) {
-   console.log('Address:', data.address)
- },
})
+ console.log('Address:', account.address)

useConnect

Return value changes

Connection status flags have been moved

The isConnected, isConnecting, isReconnecting & isDisconnected flags have been moved to the useAccount hook.

-import { useConnect } from 'wagmi'
+import { useAccount } from 'wagmi'
 
function App() {
  const {
    isConnected,
    isConnecting,
    isReConnecting,
    isDisconnected
- } = useConnect()
+ } = useAccount()
}

New connect mutation status flags have been added

The isLoading, isSuccess and isError flags have been added to useConnect.

These flags represent the local async state of useConnect.

activeConnector has been removed

The activeConnector value has been removed. You can find the active connector on useAccount.

-import { useConnect } from 'wagmi'
+import { useAccount } from 'wagmi'
 
function App() {
- const { activeConnector } = useConnect()
+ const { connector } = useAccount()
}

connector parameter on connect & connectAsync has been removed

The connector parameter on connect & connectAsync now has to be in the config object parameter shape.

import { useConnect } from 'wagmi'
 
function App() {
  const { connect, connectors } = useConnect()
 
  return (
    <button
-     onClick={() => connect(connectors[0])}
+     onClick={() => connect({ connector: connectors[0] })}
    >
      Connect
    </button>
  )
}

Configuration changes

onBeforeConnect has been renamed

The onBeforeConnect callback has been renamed to onMutate

onConnect has been renamed

The onConnect callback has been renamed to onSuccess

useContractRead

The useContractRead hook parameters have been consolidated into a singular config parameter.

Before:

useContractRead(
  {
    addressOrName: wagmigotchiContractAddress,
    contractInterface: wagmigotchiABI,
  },
  'love',
  { args: '0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c' },
)

After:

useContractRead({
  addressOrName: wagmigotchiContractAddress,
  contractInterface: wagmigotchiABI,
  functionName: 'love',
  args: '0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c',
})

useContractWrite

The useContractWrite hook parameters have been consolidated into a singular config parameter.

Before:

useContractWrite(
  {
    addressOrName: mlootContractAddress,
    contractInterface: mlootABI,
  },
  'claim',
)

After:

useContractWrite({
  addressOrName: mlootContractAddress,
  contractInterface: mlootABI,
  functionName: 'claim',
})

useContractEvent

The useContractEvent hook parameters have been consolidated into a singular config parameter.

Before:

useContractEvent(
  {
    addressOrName: uniContractAddress,
    contractInterface: erc20ABI,
  },
  'Transfer',
  listener,
),

After:

useContractEvent({
  addressOrName: uniContractAddress,
  contractInterface: erc20ABI,
  eventName: 'Transfer',
  listener,
})

useNetwork

The "switch network" functionality has been moved out of useNetwork into a new useSwitchNetwork hook.

The useNetwork hook now accepts no configuration and only returns chain (renamed from activeChain) and chains.

import {
  useNetwork
+ useSwitchNetwork
} from 'wagmi'
 
const {
- activeChain
+ chain,
  chains,
- data,
- error,
- isError,
- isIdle,
- isLoading,
- isSuccess,
- pendingChainId,
- switchNetwork,
- switchNetworkAsync,
- status,
- reset,
-} = useNetwork({
- chainId: 69,
- onError(error) {},
- onMutate(args) {},
- onSettled(data, error) {},
- onSuccess(data) {}
-})
+} = useNetwork()
 
+const {
+ chains,
+ data,
+ error,
+ isError,
+ isIdle,
+ isLoading,
+ isSuccess,
+ pendingChainId,
+ switchNetwork,
+ switchNetworkAsync,
+ status,
+ reset,
+} = useSwitchNetwork({
+ chainId: 69,
+ onError(error) {},
+ onMutate(args) {},
+ onSettled(data, error) {},
+ onSuccess(data) {}
+})

Connector getProvider

Connectors getProvider method no longer supports the create config parameter. Use the chainId config option instead to force create a new provider.

0.4.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.3.x docs here.

createClient

Passing a function to createClient connectors has now been deprecated.

If you previously derived an RPC URL from the chainId in connectors, you will need to migrate to use the configureChains API.

Before:

import { providers } from 'ethers'
import { Provider, chain, createClient, defaultChains } from 'wagmi'
import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'
import { InjectedConnector } from 'wagmi/connectors/injected'
import { MetaMaskConnector } from 'wagmi/connectors/metaMask'
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'
 
const alchemyId = process.env.ALCHEMY_ID
 
const chains = defaultChains
const defaultChain = chain.mainnet
 
const client = createClient({
  autoConnect: true,
  connectors({ chainId }) {
    const chain = chains.find((x) => x.id === chainId) ?? defaultChain
    const rpcUrl = chain.rpcUrls.alchemy
      ? `${chain.rpcUrls.alchemy}/${alchemyId}`
      : chain.rpcUrls.default
    return [
      new MetaMaskConnector({ chains }),
      new CoinbaseWalletConnector({
        chains,
        options: {
          appName: 'wagmi',
          chainId: chain.id,
          jsonRpcUrl: rpcUrl,
        },
      }),
      new WalletConnectConnector({
        chains,
        options: {
          qrcode: true,
          rpc: { [chain.id]: rpcUrl },
        },
      }),
      new InjectedConnector({
        chains,
        options: {
          name: 'Injected',
          shimDisconnect: true,
        },
      }),
    ]
  },
})

After:

import { Provider, chain, createClient, defaultChains } from 'wagmi'
 
import { alchemyProvider } from 'wagmi/providers/alchemy'
import { publicProvider } from 'wagmi/providers/public'
 
import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'
import { InjectedConnector } from 'wagmi/connectors/injected'
import { MetaMaskConnector } from 'wagmi/connectors/metaMask'
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'
 
const alchemyId = process.env.ALCHEMY_ID
 
const { chains } = configureChains(defaultChains, [
  alchemyProvider({ alchemyId }),
  publicProvider(),
])
 
const client = createClient({
  autoConnect: true,
  connectors: [
    new MetaMaskConnector({ chains }),
    new CoinbaseWalletConnector({
      chains,
      options: {
        appName: 'wagmi',
      },
    }),
    new WalletConnectConnector({
      chains,
      options: {
        qrcode: true,
      },
    }),
    new InjectedConnector({
      chains,
      options: {
        name: 'Injected',
        shimDisconnect: true,
      },
    }),
  ],
})

Duplicate named exports were removed

Duplicate exports with different names and the same functionality were removed to simplify the public API. In addition, confusing exports were renamed to be more descriptive.

  • createWagmiClient alias was removed. Use createClient instead.
  • useWagmiClient alias was removed. Use useClient instead.
  • WagmiClient alias was removed. Use Client instead.
  • createWagmiStorage alias was removed. Use createStorage instead.
  • Provider was renamed and WagmiProvider alias is now deprecated. Use WagmiConfig instead.

0.3.x Breaking changes

💡

Not ready to migrate yet? You can find the 0.2.x docs here.

Provider

The Provider component no longer supports configuration directly as props. You will now need to create a wagmi Client via createClient, and then pass the client to Provider:

Before:

import { Provider } from 'wagmi'
 
function App() {
  return (
    <Provider autoConnect connectors={connectors} provider={provider}>
      <YourRoutes />
    </Provider>
  )
}

After:

import { Provider, createClient } from 'wagmi'
 
const client = createClient({
  autoConnect: true,
  connectors,
  provider,
})
 
function App() {
  return (
    <Provider client={client}>
      <YourRoutes />
    </Provider>
  )
}

Hooks now return a singular object

All hooks in wagmi now return a single object instead of an array pair.

Before:

const [{ data, loading, error }, disconnect] = useAccount()

After:

const { data, isLoading, error } = useAccount()

Declarative getters > imperative getters

Getter functions such as getBalance, getBlockNumber, read, etc have been removed in favor of hook parameters / refetch. The refetch function does not accept arguments/config, so you will need to restructure your components more declaratively.

Before:

import { useBalance } from 'wagmi'
 
function Example() {
  const [address, setAddress] = useState<string>('')
 
  const [{ data }, getBalance] = useBalance({
    skip: true,
  })
 
  return (
    <div>
      Get balance:
      <input onChange={(e) => setAddress(e.target.value)} value={address} />
      <button onClick={() => getBalance({ addressOrName: address })}>
        fetch
      </button>
    </div>
  )
}

After:

import { useContractRead } from 'wagmi'
 
function Example() {
  const [address, setAddress] = useState<string>('')
 
  const { data, refetch } = useBalance({
    addressOrName: address,
    enabled: Boolean(address),
  })
 
  const [value, setValue] = useState<string>('')
 
  return (
    <div>
      Get balance:
      <input onChange={(e) => setValue(e.target.value)} value={value} />
      <button
        onClick={() => (address === value ? refetch() : setAddress(value))}
      >
        fetch
      </button>
    </div>
  )
}

useAccount

  • Now returns a singular object, instead of an array pair
  • fetchEns was removed in favor of keeping useAccount as lightweight as possible. Use useEnsName and useEnsAvatar instead.
  • disconnect was removed. Use useDisconnect instead.

Before:

const [{ data, loading, error }, disconnect] = useAccount({ fetchEns: true })

After:

const { data, isLoading, error } = useAccount({ ens: true })
const { data: ensName } = useEnsName()
const { data: ensAvatar } = useEnsAvatar()
const { disconnect } = useDisconnect()

useBalance

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • getBalance was removed in favor of hook parameters / refetch. Before:
const [{ data, loading, error }, getBalance] = useBalance({ skip: true })

After:

const { data, isLoading, error, refetch } = useBalance({ enabled: false })

useBlockNumber

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • getBlockNumber was removed in favor of hook parameters / refetch. Before:
const [{ data, loading, error }, getBlockNumber] = useBlockNumber({
  skip: true,
})

After:

const { data, isLoading, error, refetch } = useBlockNumber({
  enabled: false,
})

useConnect

  • Now returns a singular object, instead of an array pair
  • connect is no longer asynchronous. Use connectAsync instead.
  • data.connected no longer exists. Use isConnected instead.
  • data.connectors no longer exists. Use connectors instead.
  • Prefer connector over data.connector Before:
const [{ data, loading, error }, connect] = useConnect()

After:

const { isConnected, connector, connectors, connectAsync } = useConnect()

useContractRead

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • read was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, read] = useContractRead(
  {
    addressOrName: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
    contractInterface: wagmigotchiABI,
  },
  'getHunger',
  { skip: true },
)

After:

const { data, error, isLoading, refetch } = useContractRead(
  {
    addressOrName: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
    contractInterface: wagmigotchiABI,
  },
  'getHunger',
  { enabled: false },
)

useContractWrite

  • Now returns a singular object, instead of an array pair Before:
const [{ data, error, loading }, write] = useContractWrite(
  {
    addressOrName: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
    contractInterface: wagmigotchiABI,
  },
  'feed',
)

After:

const { data, error, isLoading, write } = useContractWrite(
  {
    addressOrName: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
    contractInterface: wagmigotchiABI,
  },
  'feed',
)

useEnsAvatar

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • getEnsAvatar was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, getEnsAvatar] = useEnsAvatar({
  addressOrName: 'awkweb.eth',
  skip: true,
})

After:

const { data, error, isLoading, refetch } = useEnsAvatar({
  addressOrName: 'awkweb.eth',
  enabled: false,
})

useEnsLookup

  • useEnsLookup was renamed to useEnsName
  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • lookupAddress was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, lookupAddress] = useEnsLookup({
  address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
  skip: true,
})

After:

const { data, error, isLoading, refetch } = useEnsName({
  address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
  enabled: false,
})

useEnsResolveName

  • useEnsResolveName was renamed to useEnsAddress
  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • resolveName was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, resolveName] = useEnsResolveName({
  name: 'meagher.eth',
  skip: true,
})

After:

const { data, error, loading, refetch } = useEnsAddress({
  name: 'meagher.eth',
  enabled: false,
})

useEnsResolver

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • getEnsResolver was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, getEnsResolver] = useEnsResolver({
  name: 'awkweb.eth',
  skip: true,
})

After:

const { data, error, isLoading, refetch } = useEnsResolver({
  name: 'awkweb.eth',
  enabled: false,
})

useFeeData

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • getFeeData was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, getFeeData] = useFeeData({ skip: true })

After:

const { data, error, isLoading, refetch } = useFeeData({ enabled: false })

useNetwork

  • Now returns a singular object, instead of an array pair
  • data.chain is now activeChain
  • data.chains is now chains
  • switchNetwork now has sync (switchNetwork) and async (switchNetworkAsync) variants. Before:
const [{ data, error, loading }, switchNetwork] = useNetwork()

After:

const { activeChain, chains, data, isLoading, switchNetworkAsync } =
  useNetwork()

useSigner

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported.
  • getSigner was removed in favor of refetch Before:
const [{ data, error, loading }, getSigner] = useSigner()

After:

const { data, error, isLoading, refetch } = useSigner()

useSignMessage

  • Now returns a singular object, instead of an array pair
  • signMessage now has sync (signMessage) and async (signMessageAsync) variants. Before:
const [{ data, error, loading }, signMessage] = useSignMessage({
  message: 'gm wagmi frens',
})

After:

const { data, error, isLoading, signMessageAsync } = useSignMessage({
  message: 'gm wagmi frens',
})

useSignTypedData

  • Now returns a singular object, instead of an array pair
  • signTypedData now has sync (signTypedData) and async (signTypedDataAsync) variants. Before:
const [{ data, error, loading }, signTypedData] = useSignTypedData({
  domain,
  types,
  value,
})

After:

const { data, error, isLoading, signTypedDataAsync } = useSignTypedData({
  domain,
  types,
  value,
})

useToken

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • getToken was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, getToken] = useToken({
  address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
  skip: true,
})

After:

const { data, error, isLoading, refetch } = useToken({
  address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
  enabled: false,
})

useTransaction

  • useTransaction was renamed to useSendTransaction
  • Now returns a singular object, instead of an array pair
  • sendTransaction now has sync (sendTransaction) and async (sendTransactionAsync) variants. Before:
const [{ data, error, loading }, sendTransaction] = useTransaction({
  request: {
    to: 'awkweb.eth',
    value: parseEther('1'), // 1 ETH
  },
})

After:

const { data, error, isLoading, sendTransactionAsync } = useSendTransaction({
  request: {
    to: 'awkweb.eth',
    value: parseEther('1'), // 1 ETH
  },
})

useWaitForTransaction

  • Now returns a singular object, instead of an array pair
  • skip is no longer supported. It was repurposed to enabled.
  • wait was removed in favor of hook parameters / refetch. Before:
const [{ data, error, loading }, wait] = useWaitForTransaction({
  hash: '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060',
  skip: true,
})

After:

const { data, error, isLoading, refetch } = useWaitForTransaction({
  hash: '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060',
  enabled: false,
})

connector.getProvider

connector.getProvider is now asynchronous

Before:

const { connector } = useConnect()
 
const provider = connector.getProvider()

After:

const { connector } = useConnect()
 
const provider = await connector.getProvider()

WalletLinkConnector

The WalletLink connector was replaced with the Coinbase Wallet SDK.

Before:

import { WalletLinkConnector } from 'wagmi/connectors/walletLink'
 
const connector = new WalletLinkConnector({
  options: {
    appName: 'Mirror.xyz',
    jsonRpcUrl: 'https://mainnet.infura.io/v3',
  },
})

After:

import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'
 
const connector = new CoinbaseWalletConnector({
  options: {
    appName: 'Mirror.xyz',
    jsonRpcUrl: 'https://mainnet.infura.io/v3',
  },
})