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
import { getDefaultProvider } from 'ethers'
const provider = getDefaultProvider()
viem
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
.
import { providers } from 'ethers'
const provider = new providers.JsonRpcProvider('https://cloudflare-eth.com')
Custom Chain:
import { providers } from 'ethers'
const provider = new providers.JsonRpcProvider('https://rpc.ankr.com/fantom/', {
name: 'Fantom',
id: 250
})
viem
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http('https://cloudflare-eth.com')
})
Custom Chain:
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
import { providers } from 'ethers'
const provider = new providers.InfuraProvider('homestead', '<apiKey>')
viem
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
import { providers } from 'ethers'
const provider = new providers.AlchemyProvider('homestead', '<apiKey>')
viem
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
import { providers } from 'ethers'
const provider = new providers.CloudflareProvider()
viem
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
import { providers } from 'ethers'
const provider = new providers.PocketProvider('homestead', '<apiKey>')
viem
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
import { providers } from 'ethers'
const provider = new providers.AnkrProvider('homestead', '<apiKey>')
viem
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
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
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
import { providers } from 'ethers'
const provider = new providers.Web3Provider(window.ethereum)
viem
import { createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'
const client = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum)
})
WebSocketProvider
Ethers
import { providers } from 'ethers'
const provider = new providers.WebSocketProvider('wss://eth-mainnet.g.alchemy.com/v2/<apiKey>')
viem
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
import { providers } from 'ethers'
const provider = new providers.Web3Provider(window.ethereum)
const [address] = await provider.listAccounts()
const signer = provider.getSigner(address)
signer.sendTransaction({ ... })
viem
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
import { providers, Wallet } from 'ethers'
const provider = new providers.Web3Provider(window.ethereum)
const wallet = new Wallet('0x...', provider)
wallet.sendTransaction({ ... })
viem
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
import { getDefaultProvider } from 'ethers'
const provider = getDefaultProvider()
provider.getBlock(...)
provider.getTransaction(...)
...
viem
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
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
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
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
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
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
import { createPublicClient, createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { wagmiContractConfig } from './abi'
const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum)
})
const [address] = await walletClient.getAddresses()
const hash = await walletClient.writeContract({
...wagmiContractConfig,
functionName: 'mint',
account: address,
})
Deploying Contracts
Ethers
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
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
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
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
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
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
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
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
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
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,
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
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
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
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
import { decodeAbiParameters, parseAbiParameters } from 'viem'
// Object
decodeAbiParameters(
[{ type: 'uint', name: 'x' }, { type: 'string', name: 'y' }],
'0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000'
)
// Human Readable
decodeAbiParameters(
parseAbiParameters('uint, string'),
'0x00000000000000000000000000000000000000000000000000000000000004d20000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000b48656c6c6f20576f726c64000000000000000000000000000000000000000000'
)
Notice: different from ethers, viem only supports standard tuple expression for Human Readable.
example: (uint a, string b)
is valid, but tuple(uint a, string b)
is not.
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
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
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
import { utils } from 'ethers'
const fragment = utils.Fragment.from('function balanceOf(address owner) view returns (uint)')
viem
import { parseAbiItem } from 'viem'
const abiItem = parseAbiItem('function balanceOf(address owner) view returns (uint)')
ParamType.from
ethers
import { utils } from 'ethers'
const param = utils.ParamType.from('address owner')
viem
import { parseAbiParameter } from 'viem'
const param = parseAbiParameter('address owner')
Fragment Access
Ethers
import { utils } from 'ethers'
import { abi } from './abi'
const interface = new utils.Interface(abi)
interface.getFunction('transferFrom')
interface.getEvent('Transfer')
viem
import { getAbiItem } from 'viem'
import { abi } from './abi'
getAbiItem({ abi, name: 'transferFrom' })
getAbiItem({ abi, name: 'Transfer' })
Interface.encodeDeploy
Ethers
import { utils } from 'ethers'
import { abi } from './abi'
const iface = new utils.Interface(abi);
const data = iface.encodeDeploy(['SYM', 'Some Name'])
viem
import { encodeDeployData } from 'viem'
import { abi, bytecode } from './abi'
const data = encodeDeployData({
abi,
bytecode,
args: ['SYM', 'Some Name']
})
Note: viem concatenates the contract bytecode onto the ABI encoded data.
Interface.encodeErrorResult
Ethers
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
import { encodeErrorResult, parseEther } from 'viem'
import { abi } from './abi'
const data = encodeErrorResult({
abi: wagmiAbi,
errorName: 'AccountLocked',
args: [
'0x8ba1f109551bD432803012645Ac136ddd64DBA72',
parseEther('1.0')
]
})
Interface.encodeFilterTopics
Ethers
import { utils } from 'ethers'
import { abi } from './abi'
const iface = new utils.Interface(abi);
const data = iface.encodeFilterTopics('Transfer', [
null,
'0x8ba1f109551bD432803012645Ac136ddd64DBA72'
])
viem
import { encodeEventTopics } from 'viem'
import { abi } from './abi'
const data = encodeEventTopics({
abi,
eventName: 'Transfer',
args: {
to: '0x8ba1f109551bD432803012645Ac136ddd64DBA72'
}
})
Interface.encodeFunctionData
Ethers
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
import { encodeFunctionData, parseEther } from 'viem'
import { abi } from './abi'
const data = encodeFunctionData({
abi,
functionName: 'transferFrom',
args: [
'0x8ba1f109551bD432803012645Ac136ddd64DBA72',
'0xaB7C8803962c0f2F5BBBe3FA8bf41cd82AA1923C',
parseEther('1.0')
]
})
Interface.encodeFunctionResult
Ethers
import { utils } from 'ethers'
import { abi } from './abi'
const iface = new utils.Interface(abi);
const data = iface.encodeFunctionResult('balanceOf', [
'0x8ba1f109551bD432803012645Ac136ddd64DBA72'
])
viem
import { encodeFunctionResult, parseEther } from 'viem'
import { abi } from './abi'
const data = encodeFunctionResult({
abi,
functionName: 'balanceOf',
value: ['0x8ba1f109551bD432803012645Ac136ddd64DBA72']
})
Interface.decodeErrorResult
Ethers
import { utils } from 'ethers'
import { abi } from './abi'
const iface = new utils.Interface(abi);
const result = iface.decodeErrorResult("AccountLocked", '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000')
viem
import { decodeErrorResult, parseEther } from 'viem'
import { abi } from './abi'
const result = decodeErrorResult({
abi,
data: '0xf7c3865a0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000000de0b6b3a7640000'
})
Interface.decodeEventLog
Ethers
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
import { decodeEventLog, parseEther } from 'viem'
import { abi } from './abi'
const result = decodeEventLog({
abi,
data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
topics: [
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72',
'0x000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c'
]
})
Interface.decodeFunctionData
Ethers
import { utils } from 'ethers'
import { abi } from './abi'
const iface = new utils.Interface(abi);
const result = iface.decodeFunctionData('transferFrom', '0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000');
viem
import { decodeFunctionData, parseEther } from 'viem'
import { abi } from './abi'
const result = decodeFunctionData({
abi,
data: '0x23b872dd0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000ab7c8803962c0f2f5bbbe3fa8bf41cd82aa1923c0000000000000000000000000000000000000000000000000de0b6b3a7640000',
})
Interface.decodeFunctionResult
Ethers
import { utils } from 'ethers'
import { abi } from './abi'
const iface = new utils.Interface(abi);
const result = iface.decodeFunctionResult('balanceOf', '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000');
viem
import { decodeFunctionResult, parseEther } from 'viem'
import { abi } from './abi'
const result = decodeFunctionResult({
abi,
functionName: 'balanceOf',
data: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000',
})
Interface.getSighash
Ethers
import { Interface, FunctionFragment } from '@ethersproject/abi';
const hash = Interface.getSighash(FunctionFragment.from('function ownerOf(uint256)'));
viem
import { toFunctionHash } from 'viem'
const hash = toFunctionHash('function ownerOf(uint256)')
Address Utilities
getAddress
Ethers
import { utils } from 'ethers'
const address = utils.getAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')
viem
import { getAddress } from 'viem'
const address = getAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')
isAddress
Ethers
import { utils } from 'ethers'
const address = utils.isAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')
viem
import { isAddress } from 'viem'
const address = isAddress('0x8ba1f109551bd432803012645ac136ddd64dba72')
getContractAddress
Ethers
import { utils } from 'ethers'
const address = utils.getContractAddress({ from: '0x...', nonce: 5 });
viem
import { getContractAddress } from 'viem'
const address = getContractAddress({ from: '0x...', nonce: 5 })
getCreate2Address
Ethers
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
import { getContractAddress } from 'viem'
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
import { utils } from 'ethers'
utils.isBytes(new Uint8Array([1, 69, 420]))
viem
import { isBytes } from 'viem'
isBytes(new Uint8Array([1, 69, 420]))
isHexString
Ethers
import { utils } from 'ethers'
utils.isHexString('0xdeadbeef')
viem
import { isHex } from 'viem'
isHex('0xdeadbeef')
isBytesLike
Ethers
import { utils } from 'ethers'
utils.isBytesLike('0xdeadbeef')
viem
import { isBytes, isHex } from 'viem'
isBytes('0xdeadbeef') || isHex('0xdeadbeef')
arrayify
Ethers
import { utils } from 'ethers'
utils.arrayify('0xdeadbeef')
viem
import { toBytes } from 'viem'
toBytes('0xdeadbeef')
hexlify
Ethers
import { utils } from 'ethers'
utils.hexlify(new Uint8Array([1, 69, 420]))
viem
import { toHex } from 'viem'
toHex(new Uint8Array([1, 69, 420]))
hexValue
Ethers
import { utils } from 'ethers'
utils.hexValue(1)
viem
import { toHex } from 'viem'
toHex(1)
formatBytes32String
Ethers
import { utils } from 'ethers'
utils.formatBytes32String('Hello world')
// 0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000
viem
import { stringToHex } from 'viem'
stringToHex('Hello world', { size: 32 })
// 0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000
parseBytes32String
Ethers
import { utils } from 'ethers'
utils.parseBytes32String('0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000')
// "Hello world"
viem
import { hexToString } from 'viem'
hexToString('0x48656c6c6f20776f726c642e0000000000000000000000000000000000000000', { size: 32 })
// "Hello world"
concat
Ethers
import { utils } from 'ethers'
utils.concat([new Uint8Array([69]), new Uint8Array([420])])
viem
import { concat, toBytes } from 'viem'
concat([new Uint8Array([69]), new Uint8Array([420])])
stripZeros
Ethers
import { utils } from 'ethers'
utils.stripZeros(new Uint8Array([0, 0, 0, 0, 0, 69]))
viem
import { trim } from 'viem'
trim(new Uint8Array([0, 0, 0, 0, 0, 69]))
zeroPad
Ethers
import { utils } from 'ethers'
utils.zeroPad(new Uint8Array([69]), 32)
viem
import { pad } from 'viem'
pad(new Uint8Array([69]), { size: 32 })
hexConcat
Ethers
import { utils } from 'ethers'
utils.hexConcat(['0x00000069', '0x00000420'])
viem
import { concat, toBytes } from 'viem'
concat(['0x00000069', '0x00000420'])
hexDataLength
Ethers
import { utils } from 'ethers'
utils.hexDataLength('0x00000069')
viem
import { size } from 'viem'
size('0x00000069')
hexDataSlice
Ethers
import { utils } from 'ethers'
utils.hexDataSlice('0x00000069', 4)
viem
import { slice } from 'viem'
slice('0x00000069', 4)
hexStripZeros
Ethers
import { utils } from 'ethers'
utils.hexStripZeros('0x00000069')
viem
import { trim } from 'viem'
trim('0x00000069')
hexZeroPad
Ethers
import { utils } from 'ethers'
utils.hexZeroPad('0x69', 32)
viem
import { pad } from 'viem'
pad('0x69', { size: 32 })
Display Logic & Input Utilities
formatUnits
Ethers
import { utils } from 'ethers'
utils.formatUnits(BigNumber.from('1000000000'), 9)
viem
import { formatUnits } from 'viem'
formatUnits(1000000000n, 9)
formatEther
Ethers
import { utils } from 'ethers'
utils.formatEther(BigNumber.from('1000000000000000000'))
viem
import { formatEther } from 'viem'
formatEther(1000000000000000000n)
parseUnits
Ethers
import { utils } from 'ethers'
utils.parseUnits('1.0', 18)
viem
import { parseUnits } from 'viem'
parseUnits('1', 18)
parseEther
Ethers
import { utils } from 'ethers'
utils.parseEther('1.0')
viem
import { parseEther } from 'viem'
parseEther('1')
Encoding Utilities
RLP.encode
Ethers
import { utils } from 'ethers'
utils.RLP.encode('0x12345678')
viem
import { toRlp } from 'viem'
toRlp('0x12345678')
RLP.decode
Ethers
import { utils } from 'ethers'
utils.RLP.decode('0x8412345678')
viem
import { fromRlp } from 'viem'
fromRlp('0x8412345678')
Hashing Utilities
id
Ethers
import { utils } from 'ethers'
utils.id('function ownerOf(uint256 tokenId)')
// hash utf-8 data
utils.id('hello world')
viem
import { toFunctionSelector, keccak256, toHex } from 'viem'
toFunctionSelector('function ownerOf(uint256 tokenId)')
// hash utf-8 data
keccak256(toHex('hello world'))
keccak256
Ethers
import { utils } from 'ethers'
utils.keccak256(utils.toUtf8Bytes('hello world'))
viem
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
import { utils } from 'ethers'
utils.namehash('awkweb.eth')
viem
import { namehash } from 'viem'
namehash('awkweb.eth')
solidityPack & solidityKeccak256
Ethers
import { utils } from 'ethers'
utils.solidityPack(['int16', 'uint48'], [-1, 12])
utils.solidityKeccak256(['int16', 'uint48'], [-1, 12])
viem
import { encodePacked, keccak256 } from 'viem'
encodePacked(['int16', 'uint48'], [-1, 12])
keccak256(encodePacked(['int16', 'uint48'], [-1, 12]))
String Utilities
toUtf8Bytes
Ethers
import { utils } from 'ethers'
utils.toUtf8Bytes('Hello World')
viem
import { stringToBytes } from 'viem'
stringToBytes('Hello World')
toUtf8String
Ethers
import { utils } from 'ethers'
utils.toUtf8String(new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]))
viem
import { bytesToString } from 'viem'
bytesToString(new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]))
Transaction Utilities
serializeTransaction
Ethers
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
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
import { utils } from 'ethers'
const transaction = utils.parseTransaction('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0')
viem
import { parseTransaction } from 'viem'
const transaction = parseTransaction('0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0')