Skip to content
On this page

Ethers v5 โ†’ viem Migration Guide โ€‹

This is a long document. Feel free to use the search bar above (โŒ˜ K) or the Table of Contents to the side. If there is an API you need which is missing or cannot find, create a Parity Request here.

You may notice some of the APIs in viem are a little more verbose than Ethers. We prefer boring code and we want to strongly embrace clarity & composability. We believe that verbose APIs are more flexible to move, change and remove compared to code that is prematurely abstracted and hard to change.

Provider โ†’ Client โ€‹

getDefaultProvider โ€‹

Ethers โ€‹

ts
import { getDefaultProvider } from 'ethers'

const provider = getDefaultProvider()

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

We are more verbose here โ€“ we want to be explicit and clear what chain you are connecting to & what transport you are using to avoid any confusion. ๐Ÿ˜ƒ

JsonRpcProvider โ€‹

Ethers โ€‹

This is also interchangeable with StaticJsonRpcProvider.

ts
import { providers } from 'ethers'

const provider = new providers.JsonRpcProvider('https://cloudflare-eth.com')

Custom Chain:

ts
import { providers } from 'ethers'

const provider = new providers.JsonRpcProvider('https://rpc.ankr.com/fantom/โ€‹', {
  name: 'Fantom',
  id: 250
})

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://cloudflare-eth.com')
})

Custom Chain:

ts
import { createPublicClient, http } from 'viem'
import { fantom } from 'viem/chains'

const client = createPublicClient({
  chain: fantom,
  transport: http('https://rpc.ankr.com/fantom/โ€‹')
})

viem exports custom EVM chains in the viem/chains entrypoint.

InfuraProvider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.InfuraProvider('homestead', '<apiKey>')

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://mainnet.infura.io/v3/<apiKey>')
})

viem does not have custom API Provider clients โ€“ you can just pass in their RPC URL.

AlchemyProvider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.AlchemyProvider('homestead', '<apiKey>')

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://eth-mainnet.g.alchemy.com/v2/<apiKey>')
})

viem does not have custom API Provider clients โ€“ you can just pass in their RPC URL.

CloudflareProvider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.CloudflareProvider()

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://cloudflare-eth.com/')
})

viem does not have custom API Provider clients โ€“ you can just pass in their RPC URL.

PocketProvider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.PocketProvider('homestead', '<apiKey>')

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://eth-mainnet.gateway.pokt.network/v1/lb/<apiKey>')
})

viem does not have custom API Provider clients โ€“ you can just pass in their RPC URL.

AnkrProvider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.AnkrProvider('homestead', '<apiKey>')

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://rpc.ankr.com/eth/<apiKey>')
})

viem does not have custom API Provider clients โ€“ you can just pass in their RPC URL.

FallbackProvider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const alchemy = new providers.AlchemyProvider('homestead', '<apiKey>')
const infura = new providers.InfuraProvider('homestead', '<apiKey>')
const provider = new providers.FallbackProvider([alchemy, infura])

viem โ€‹

ts
import { createPublicClient, http, fallback } from 'viem'
import { mainnet } from 'viem/chains'

const alchemy = http('https://eth-mainnet.g.alchemy.com/v2/<apiKey>')
const infura = http('https://mainnet.infura.io/v3/<apiKey>')

const client = createPublicClient({
  chain: mainnet,
  transport: fallback([alchemy, infura])
})

IpcProvider โ€‹

Coming soon.

JsonRpcBatchProvider โ€‹

Coming soon.

Web3Provider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.Web3Provider(window.ethereum)

viem โ€‹

ts
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

const client = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum)
})

WebSocketProvider โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.WebSocketProvider('wss://eth-mainnet.g.alchemy.com/v2/<apiKey>')

viem โ€‹

ts
import { createPublicClient, webSocket } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: webSocket('wss://eth-mainnet.g.alchemy.com/v2/<apiKey>')
})

Signers โ†’ Accounts โ€‹

JsonRpcSigner โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.Web3Provider(window.ethereum)

const [address] = await provider.listAccounts()
const signer = provider.getSigner(address)

signer.sendTransaction({ ... })

viem โ€‹

ts
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })

const client = createWalletClient({
  account,
  chain: mainnet,
  transport: custom(window.ethereum)
})

client.sendTransaction({ ... })

viem uses the term "Account" rather than "Signer".

Wallet โ€‹

Ethers โ€‹

ts
import { providers, Wallet } from 'ethers'

const provider = new providers.Web3Provider(window.ethereum)

const wallet = new Wallet('0x...', provider)

wallet.sendTransaction({ ... })

viem โ€‹

ts
import { createWalletClient, custom } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { mainnet } from 'viem/chains'

const account = privateKeyToAccount('0x...')

const client = createWalletClient({
  account,
  chain: mainnet,
  transport: custom(window.ethereum)
})

client.sendTransaction({ ... })

viem uses the term "Account" rather than "Signer".

Provider Methods โ€‹

Ethers โ€‹

ts
import { getDefaultProvider } from 'ethers'

const provider = getDefaultProvider()

provider.getBlock(...)
provider.getTransaction(...)
...

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

client.getBlock(...)
client.getTransaction(...)
...

Methods that extend off the Public Client are Public Actions. Read more.

There are API differences in all of these methods. Use the search bar at the top of the page to learn more about them.

Signer Methods โ€‹

JsonRpcSigner โ€‹

Ethers โ€‹

ts
import { providers } from 'ethers'

const provider = new providers.Web3Provider(window.ethereum)

const [address] = await provider.listAccounts()
const signer = provider.getSigner(address)

signer.sendTransaction(...)
signer.signMessage(...)
...

viem โ€‹

ts
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })

const client = createWalletClient({
  account,
  chain: mainnet,
  transport: custom(window.ethereum)
})

client.sendTransaction({ ... })
client.signMessage({ ... })
...

Methods that extend off the Wallet Client are Wallet Actions. Read more.

There are API differences in all of these methods. Use the search bar at the top of the page to learn more about them.

Contract Interaction โ€‹

Reading from Contracts โ€‹

Ethers โ€‹

ts
import { getDefaultProvider } from 'ethers'
import { wagmiContractConfig } from './abi'

const provider = getDefaultProvider()

const { abi, address } = wagmiContractConfig
const contract = new Contract(address, abi, provider)
const supply = await contract.totalSupply()

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { wagmiContractConfig } from './abi'

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

const supply = await client.readContract({
  ...wagmiContractConfig,
  functionName: 'totalSupply'
})

Writing to Contracts โ€‹

Ethers โ€‹

ts
import { Contract, providers } from 'ethers'
import { wagmiContractConfig } from './abi'

const provider = new providers.Web3Provider(window.ethereum)

const [address] = await provider.listAccounts()
const signer = provider.getSigner(address)

const { abi, address } = wagmiContractConfig
const contract = new Contract(address, abi, signer)
const hash = await contract.mint()

viem โ€‹

ts
import { createPublicClient, createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { wagmiContractConfig } from './abi'

const publicClient = createPublicClient({
  chain: mainnet,
  transport: http()
})
const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum)
})

const [address] = await walletClient.getAddresses()

const { request } = await publicClient.simulateContract({
  ...wagmiContractConfig,
  functionName: 'mint',
  account: address,
})
const hash = await walletClient.writeContract(request)

Deploying Contracts โ€‹

Ethers โ€‹

ts
import { ContractFactory, providers } from 'ethers'
import { abi, bytecode } from './abi'

const provider = new providers.Web3Provider(window.ethereum)

const [address] = await provider.listAccounts()
const signer = provider.getSigner(address)

const contract = new ContractFactory(abi, bytecode, signer)
await contract.deploy()

viem โ€‹

ts
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { abi, bytecode } from './abi'

const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum)
})

const [address] = await walletClient.getAddresses()

await walletClient.deployContract({
  abi,
  account: address,
  bytecode,
})

Contract Events โ€‹

Ethers โ€‹

ts
import { getDefaultProvider } from 'ethers'
import { wagmiContractConfig } from './abi'

const provider = getDefaultProvider()

const { abi, address } = wagmiContractConfig
const contract = new Contract(address, abi, provider)

const listener = (from, to, amount, event) => {
  // ...
}
contract.on('Transfer', listener)

// unsubscribe
contract.off('Transfer', listener)

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { wagmiContractConfig } from './abi'

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

const unwatch = client.watchContractEvent({
  ...wagmiContractConfig,
  eventName: 'Transfer',
  onLogs: logs => {
    const { args: { from, to, amount }, eventName } = logs[0]
    // ...
  },
})

// unsubscribe
unwatch()

Note: Logs are batched between polling intervals in viem to avoid excessive callback invocations. You can disable this behavior with batch: false however.

Gas Estimation โ€‹

Ethers โ€‹

ts
import { getDefaultProvider } from 'ethers'
import { wagmiContractConfig } from './abi'

const provider = getDefaultProvider()

const { abi, address } = wagmiContractConfig
const contract = new Contract(address, abi, provider)
const gas = await contract.estimateGas.mint()

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { wagmiContractConfig } from './abi'

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

const gas = await client.estimateContractGas({
  ...wagmiContractConfig, 
  functionName: 'mint'
})

Call โ€‹

Ethers โ€‹

ts
import { getDefaultProvider } from 'ethers'
import { wagmiContractConfig } from './abi'

const provider = getDefaultProvider()

const { abi, address } = wagmiContractConfig
const contract = new Contract(address, abi, provider)
await contract.callStatic.mint()

viem โ€‹

ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { wagmiContractConfig } from './abi'

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

await client.simulateContract({
  ...wagmiContractConfig, 
  functionName: 'mint'
})

Contract Instances โ€‹

Ethers โ€‹

ts
import { getDefaultProvider } from 'ethers'
import { wagmiContractConfig } from './abi'

const provider = getDefaultProvider()

const { abi, address } = wagmiContractConfig
const contract = new Contract(address, abi, provider)

const supply = await contract.totalSupply()
const listener = (from, to, amount, event) => {
  // ...
}
contract.on('Transfer', listener)
contract.off('Transfer', listener)

viem โ€‹

ts
import { createPublicClient, http, getContract } from 'viem'
import { mainnet } from 'viem/chains'
import { wagmiContractConfig } from './abi'

const client = createPublicClient({
  chain: mainnet,
  transport: http()
})

const contract = getContract({
  ...wagmiContractConfig,
  publicClient: client,
})

const supply = await contract.read.totalSupply()
const unwatch = contract.watchEvent.Transfer({
  onLogs: logs => {
    const { args: { from, to, amount }, eventName } = logs[0]
    // ...
  },
})
unwatch()

ABI Utilities โ€‹

abiCoder.encode โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const abiCoder = utils.defaultAbiCoder()

// Object
abiCoder.encode(
  [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }],
  [1234, 'Hello world']
)

// Human Readable
abiCoder.encode(
  ['uint', 'string'], 
  [1234, 'Hello World']
);

viem โ€‹

ts
import { encodeAbiParameters, parseAbiParameters } from 'viem'

// Object
encodeAbiParameters(
  [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }],
  [1234, 'Hello world']
)

// Human Readable
encodeAbiParameters(
  parseAbiParameters('uint, string'),
  [1234, 'Hello world']
)

abiCoder.decode โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const abiCoder = utils.defaultAbiCoder()

// Object
abiCoder.decode(
  [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }],
  '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000'
)

// Human Readable
abiCoder.decode(
  ['uint', 'string'], 
  '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000'
);

viem โ€‹

ts
import { decodeAbiParameters, parseAbiParameters } from 'viem'

// Object
decodeAbiParameters(
  [{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }],
  '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000'
)

// Human Readable
decodeAbiParameters(
  parseAbiParameters('uint, string'),
  '0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000'
)

Fragments & Interfaces โ€‹

In viem, there is no concept of "fragments" & "interfaces". We want to stick as close to the wire as possible and not introduce middleware abstractions and extra layers over ABIs. Instead of working with "fragments", we encourage you to work with the ABI itself. We provide utilities such as getAbiItem, parseAbi parseAbiItem, parseAbiParameters and parseAbiParameter which covers the use cases of interfaces & fragments.

Interface.format โ€‹

viem only supports Human Readable โ†’ Object format.

Ethers โ€‹

ts
import { utils } from 'ethers'

const interface = new Interface([
  'constructor(string symbol, string name)',
  'function transferFrom(address from, address to, uint amount)',
  'function transferFrom(address from, address to, uint amount, bool x)',
  'function mint(uint amount) payable',
  'function balanceOf(address owner) view returns (uint)'
])
const json = interface.format(utils.FormatTypes.json)

viem โ€‹

ts
import { parseAbi } from 'viem'

const json = parseAbi([
  'constructor(string symbol, string name)',
  'function transferFrom(address from, address to, uint amount)',
  'function transferFrom(address from, address to, uint amount, bool x)',
  'function mint(uint amount) payable',
  'function balanceOf(address owner) view returns (uint)',
  'event Transfer(address indexed from, address indexed to, uint256 amount)'
])

Fragment.from โ€‹

ethers โ€‹

ts
import { utils } from 'ethers'

const fragment = utils.Fragment.from('function balanceOf(address owner) view returns (uint)')

viem โ€‹

ts
import { parseAbiItem } from 'viem'

const abiItem = parseAbiItem('function balanceOf(address owner) view returns (uint)')

ParamType.from โ€‹

ethers โ€‹

ts
import { utils } from 'ethers'

const param = utils.ParamType.from('address owner')

viem โ€‹

ts
import { parseAbiParameter } from 'viem'

const param = parseAbiParameter('address owner')

Fragment Access โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const interface = new utils.Interface(abi) 
interface.getFunction('transferFrom')
interface.getEvent('Transfer')

viem โ€‹

ts
import { getAbiItem } from 'viem'
import { abi } from './abi'

getAbiItem({ abi, name: 'transferFrom' }) 
getAbiItem({ abi, name: 'Transfer' })

Interface.encodeDeploy โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const data = iface.encodeDeploy(['SYM', 'Some Name'])

viem โ€‹

ts
import { encodeDeployData } from 'viem'
import { abi, bytecode } from './abi'

const data = encodeDeployData({ 
  abi,
  bytecode,
  args: ['SYM', 'Some Name']
})

Note: viem concatinates the contract bytecode onto the ABI encoded data.

Interface.encodeErrorResult โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const data = iface.encodeErrorResult('AccountLocked', [
  '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
  utils.parseEther('1.0')
]);

viem โ€‹

ts
import { encodeErrorResult, parseEther } from 'viem'
import { abi } from './abi'

const data = encodeErrorResult({ 
  abi: wagmiAbi,
  errorName: 'AccountLocked',
  args: [
    '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
    parseEther('1.0')
  ]
})

Interface.encodeFilterTopics โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const data = iface.encodeFilterTopics('Transfer', [
  null,
  '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
])

viem โ€‹

ts
import { encodeEventTopics } from 'viem'
import { abi } from './abi'

const data = encodeEventTopics({ 
  abi,
  eventName: 'Transfer',
  args: {
    to: '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
  }
})

Interface.encodeFunctionData โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const data = iface.encodeFunctionData('transferFrom', [
  '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
  '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C',
  parseEther('1.0')
])

viem โ€‹

ts
import { encodeFunctionData, parseEther } from 'viem'
import { abi } from './abi'

const data = encodeFunctionData({ 
  abi,
  functionName: 'transferFrom',
  args: [
    '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
    '0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C',
    parseEther('1.0')
  ]
})

Interface.encodeFunctionResult โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const data = iface.encodeFunctionResult('balanceOf', [
  '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
])

viem โ€‹

ts
import { encodeFunctionResult, parseEther } from 'viem'
import { abi } from './abi'

const data = encodeFunctionResult({ 
  abi,
  functionName: 'balanceOf',
  value: ['0x8ba1f109551bD432803012645Ac136ddd64DBA72']
})

Interface.decodeErrorResult โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const result = iface.decodeErrorResult("AccountLocked", '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000')

viem โ€‹

ts
import { decodeErrorResult, parseEther } from 'viem'
import { abi } from './abi'

const result = decodeErrorResult({ 
  abi,
  data: '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000'
})

Interface.decodeEventLog โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const result = iface.decodeEventLog(
  'Transfer', 
  data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000', 
  topics: [
    '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
    '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72',
    '0x000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c'
  ]
);

viem โ€‹

ts
import { decodeEventLog, parseEther } from 'viem'
import { abi } from './abi'

const result = decodeEventLog({ 
  abi,
  data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000', 
  topics: [
    '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
    '0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72',
    '0x000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c'
  ]
})

Interface.decodeFunctionData โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const result = iface.decodeFunctionData('transferFrom', '0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000');

viem โ€‹

ts
import { decodeFunctionData, parseEther } from 'viem'
import { abi } from './abi'

const result = decodeFunctionData({ 
  abi,
  data: '0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000',
})

Interface.decodeFunctionResult โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'
import { abi } from './abi'

const iface = new utils.Interface(abi); 
const result = iface.decodeFunctionResult('balanceOf', '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000');

viem โ€‹

ts
import { decodeFunctionResult, parseEther } from 'viem'
import { abi } from './abi'

const result = decodeFunctionResult({ 
  abi,
  functionName: 'balanceOf',
  data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
})

Address Utilities โ€‹

getAddress โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const address = utils.getAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')

viem โ€‹

ts
import { getAddress } from 'viem'

const address = getAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')

isAddress โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const address = utils.isAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')

viem โ€‹

ts
import { isAddress } from 'viem'

const address = isAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')

getContractAddress โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const address = utils.getContractAddress({ from: '0x...', nonce: 5 });

viem โ€‹

ts
import { getContractAddress } from 'viem'

const address = getContractAddress({ from: '0x...', nonce: 5 })

getCreate2Address โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const from = '0x8ba1f109551bD432803012645Ac136ddd64DBA72';
const salt = '0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331';
const initCode = '0x6394198df16000526103ff60206004601c335afa6040516060f3';
const initCodeHash = utils.keccak256(initCode);

const address = utils.getCreate2Address(from, salt, initCodeHash);

viem โ€‹

ts
import { getContractAddress } from 'ethers'

const address = getContractAddress({
  bytecode: '0x6394198df16000526103ff60206004601c335afa6040516060f3',
  from: '0x8ba1f109551bD432803012645Ac136ddd64DBA72',
  opcode: 'CREATE2',
  salt: '0x7c5ea36004851c764c44143b1dcb59679b11c9a68e5f41497f6cf3d480715331',
});

BigNumber Utilities โ€‹

Ethers โ€‹

Many.

viem โ€‹

None. We use browser native BigInt.

Byte Manipulation Utilities โ€‹

isBytes โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.isBytes(new Uint8Array([1, 69, 420]))

viem โ€‹

ts
import { isBytes } from 'viem'

isBytes(new Uint8Array([1, 69, 420]))

isHexString โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.isHexString('0xdeadbeef')

viem โ€‹

ts
import { isHex } from 'viem'

isHex('0xdeadbeef')

isBytesLike โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.isBytesLike('0xdeadbeef')

viem โ€‹

ts
import { isBytes, isHex } from 'viem'

isBytes('0xdeadbeef') || isHex('0xdeadbeef')

arrayify โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.arrayify('0xdeadbeef')

viem โ€‹

ts
import { toBytes } from 'viem'

toBytes('0xdeadbeef')

hexlify โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.hexlify(new Uint8Array([1, 69, 420]))

viem โ€‹

ts
import { toHex } from 'viem'

toHex(new Uint8Array([1, 69, 420]))

hexValue โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.hexValue(1)

viem โ€‹

ts
import { toHex } from 'viem'

toHex(1)

formatBytes32String โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.formatBytes32String('Hello world')
// 0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000

viem โ€‹

ts
import { stringToHex } from 'viem'

stringToHex(
  'Hello world', 
  { size: 32 }
)
// 0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000

parseBytes32String โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.parseBytes32String('0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000')
// "Hello world"

viem โ€‹

ts
import { hexToString } from 'viem'

hexToString(
  '0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000', 
  { size: 32 }
)
// "Hello world"

concat โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.concat([new Uint8Array([69]), new Uint8Array([420])])

viem โ€‹

ts
import { concat, toBytes } from 'viem'

concat([new Uint8Array([69]), new Uint8Array([420])])

stripZeros โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.stripZeros(new Uint8Array([0, 0, 0, 0, 0, 69]))

viem โ€‹

ts
import { trim } from 'viem'

trim(new Uint8Array([0, 0, 0, 0, 0, 69]))

zeroPad โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.zeroPad(new Uint8Array([69]), 32)

viem โ€‹

ts
import { pad } from 'viem'

pad(new Uint8Array([69]), { size: 32 })

hexConcat โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.hexConcat(['0x00000069', '0x00000420'])

viem โ€‹

ts
import { concat, toBytes } from 'viem'

concat(['0x00000069', '0x00000420'])

hexDataLength โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.hexDataLength('0x00000069')

viem โ€‹

ts
import { size } from 'viem'

size('0x00000069')

hexDataSlice โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.hexDataSlice('0x00000069', 4)

viem โ€‹

ts
import { slice } from 'viem'

slice('0x00000069', 4)

hexStripZeros โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.hexStripZeros('0x00000069')

viem โ€‹

ts
import { trim } from 'viem'

trim('0x00000069')

hexZeroPad โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.hexZeroPad('0x69', 32)

viem โ€‹

ts
import { pad } from 'viem'

pad('0x69', { size: 32 })

Display Logic & Input Utilities โ€‹

formatUnits โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.formatUnits(BigNumber.from('1000000000'), 9)

viem โ€‹

ts
import { formatUnits } from 'viem'

formatUnits(1000000000n, 9)

formatEther โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.formatEther(BigNumber.from('1000000000000000000'))

viem โ€‹

ts
import { formatEther } from 'viem'

formatEther(1000000000000000000n)

parseUnits โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.parseUnits('1.0', 18)

viem โ€‹

ts
import { parseUnits } from 'viem'

parseUnits('1', 18)

parseEther โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.parseEther('1.0')

viem โ€‹

ts
import { parseEther } from 'viem'

parseEther('1')

Encoding Utilities โ€‹

RLP.encode โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.RLP.encode('0x12345678')

viem โ€‹

ts
import { toRlp } from 'viem'

toRlp('0x12345678')

RLP.decode โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.RLP.decode('0x8412345678')

viem โ€‹

ts
import { fromRlp } from 'viem'

fromRlp('0x8412345678')

Hashing Utilities โ€‹

id โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.id('function ownerOf(uint256 tokenId)')

// hash utf-8 data
utils.id('hello world')

viem โ€‹

ts
import { getFunctionSelector, keccak256, toHex } from 'viem'

getFunctionSelector('function ownerOf(uint256 tokenId)')

// hash utf-8 data
keccak256(toHex('hello world'))

keccak256 โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.keccak256(utils.toUtf8Bytes('hello world'))

viem โ€‹

ts
import { keccak256, toBytes } from 'viem'

keccak256(toBytes('hello world'))

encodeBase64/decodeBase64 โ€‹

viem does not provide Base64 encoding utilities.

You can use browser native atob and btoa instead.

encodeBase58/decodeBase58 โ€‹

viem does not provide Base58 encoding utilities.

You can use libraries such as base58-js or bs58 instead.

namehash โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.namehash('awkweb.eth')

viem โ€‹

ts
import { namehash } from 'viem'

namehash('awkweb.eth')

solidityPack & solidityKeccak256 โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.solidityPack(['int16', 'uint48'], [-1, 12])
utils.solidityKeccak256(['int16', 'uint48'], [-1, 12])

viem โ€‹

ts
import { encodePacked, keccak256 } from 'viem'

encodePacked(['int16', 'uint48'], [-1, 12])
keccak256(encodePacked(['int16', 'uint48'], [-1, 12]))

String Utilities โ€‹

toUtf8Bytes โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.toUtf8Bytes('Hello World')

viem โ€‹

ts
import { stringToBytes } from 'viem'

stringToBytes('Hello World')

toUtf8String โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

utils.toUtf8String(new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]))

viem โ€‹

ts
import { bytesToString } from 'viem'

bytesToString(new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]))

Transaction Utilities โ€‹

serializeTransaction โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const serialized = utils.serializeTransaction({
  chainId: 1,
  maxFeePerGas: utils.parseGwei('20'),
  maxPriorityFeePerGas: utils.parseGwei('2'),
  nonce: 69,
  to: "0x1234512345123451234512345123451234512345",
  type: 2,
  value: utils.parseEther('0.01'),
})

viem โ€‹

ts
import { serializeTransaction, parseEther, parseGwei } from 'viem'

const serialized = serializeTransaction({
  chainId: 1,
  gas: 21001n,
  maxFeePerGas: parseGwei('20'),
  maxPriorityFeePerGas: parseGwei('2'),
  nonce: 69,
  to: "0x1234512345123451234512345123451234512345",
  value: parseEther('0.01'),
})

parseTransaction โ€‹

Ethers โ€‹

ts
import { utils } from 'ethers'

const transaction = utils.parseTransaction('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0')

Ethers โ€‹

ts
import { parseTransaction } from 'viem'

const transaction = parseTransaction('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0')

Released under the MIT License.