TypeScript
viem is designed to be as type-safe as possible! Things to keep in mind:
- Types currently require using TypeScript v5.0.4 or greater.
- Changes to types in this repository are considered non-breaking and are usually released as patch semver changes (otherwise every type enhancement would be a major version!).
- It is highly recommended that you lock your
viem
package version to a specific patch release and upgrade with the expectation that types may be fixed or upgraded between any release. - The non-type-related public API of
viem
still follows semver very strictly.
To ensure everything works correctly, make sure that your tsconfig.json
has strict
mode set to true
:
{
"compilerOptions": {
"strict": true
}
}
Type Inference
viem can infer types based on ABI and EIP-712 Typed Data definitions (powered by ABIType), 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, strongly-typed ABI function arguments, etc.).
For this to work, you must either add const assertions to specific configuration parameters (more info on those below) or define them inline. For example, readContract
's abi
configuration parameter:
const const abi: readonly [{
readonly type: "function";
readonly name: "balanceOf";
readonly stateMutability: "view";
readonly inputs: readonly [{
readonly type: "address";
}];
readonly outputs: readonly [{
readonly type: "uint256";
}];
}]abi = [{
type: "function"type: 'function',
name: "balanceOf"name: 'balanceOf',
stateMutability: "view"stateMutability: 'view',
inputs: readonly [{
readonly type: "address";
}]inputs: [{ type: "address"type: 'address' }],
outputs: readonly [{
readonly type: "uint256";
}]outputs: [{ type: "uint256"type: 'uint256' }],
}] as type const = readonly [{
readonly type: "function";
readonly name: "balanceOf";
readonly stateMutability: "view";
readonly inputs: readonly [{
readonly type: "address";
}];
readonly outputs: readonly [{
readonly type: "uint256";
}];
}]const ↑ const assertion
const const result: Promise<bigint>result = const client: {
account: undefined;
batch?: {
multicall?: boolean | {
batchSize?: number | undefined;
wait?: number | undefined;
} | undefined;
} | undefined;
cacheTime: number;
chain: undefined;
key: string;
... 52 more ...;
extend: <const client extends {
...;
} & Partial<...>>(fn: (client: Client<...>) => client) => Client<...>;
}client.readContract: <readonly [{
readonly type: "function";
readonly name: "balanceOf";
readonly stateMutability: "view";
readonly inputs: readonly [{
readonly type: "address";
}];
readonly outputs: readonly [{
readonly type: "uint256";
}];
}], "balanceOf", [...]>(args: ReadContractParameters<...>) => Promise<...>Calls a read-only function on a contract, and returns the response.
Docs: https://viem.sh/docs/contract/readContract
Examples: https://stackblitz.com/github/wevm/viem/tree/main/examples/contracts/reading-contracts
readContract({
address: `0x${string}`address: '0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c',
abi: readonly [{
readonly type: "function";
readonly name: "balanceOf";
readonly stateMutability: "view";
readonly inputs: readonly [{
readonly type: "address";
}];
readonly outputs: readonly [{
readonly type: "uint256";
}];
}]abi,
functionName: "balanceOf"functionName: 'balanceOf',
args: readonly [`0x${string}`]args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC']
})
const const result: Promise<bigint>result = const client: {
account: undefined;
batch?: {
multicall?: boolean | {
batchSize?: number | undefined;
wait?: number | undefined;
} | undefined;
} | undefined;
cacheTime: number;
chain: undefined;
key: string;
... 52 more ...;
extend: <const client extends {
...;
} & Partial<...>>(fn: (client: Client<...>) => client) => Client<...>;
}client.readContract: <readonly [{
readonly type: "function";
readonly name: "balanceOf";
readonly stateMutability: "view";
readonly inputs: readonly [{
readonly type: "address";
}];
readonly outputs: readonly [{
readonly type: "uint256";
}];
}], "balanceOf", [...]>(args: ReadContractParameters<...>) => Promise<...>Calls a read-only function on a contract, and returns the response.
Docs: https://viem.sh/docs/contract/readContract
Examples: https://stackblitz.com/github/wevm/viem/tree/main/examples/contracts/reading-contracts
readContract({
address: `0x${string}`address: '0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c', ↓ defined inline abi: readonly [{
readonly type: "function";
readonly name: "balanceOf";
readonly stateMutability: "view";
readonly inputs: readonly [{
readonly type: "address";
}];
readonly outputs: readonly [{
readonly type: "uint256";
}];
}]abi: [{
type: "function"type: 'function',
name: "balanceOf"name: 'balanceOf',
stateMutability: "view"stateMutability: 'view',
inputs: readonly [{
readonly type: "address";
}]inputs: [{ type: "address"type: 'address' }],
outputs: readonly [{
readonly type: "uint256";
}]outputs: [{ type: "uint256"type: 'uint256' }],
}],
functionName: "balanceOf"functionName: 'balanceOf',
args: readonly [`0x${string}`]args: ['0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC']
})
If type inference isn't working, it's likely you forgot to add a const
assertion or define the configuration parameter inline.
Contract ABIs
The following actions and utilities support type inference when you add a const assertion to abi
or define abi
inline:
Actions
createEventFilter
watchEvent
createContractEventFilter
deployContract
estimateContractGas
multicall
readContract
simulateContract
writeContract
watchContractEvent
Utilities
decodeEventLog
decodeFunctionResult
encodeDeployData
encodeErrorResult
encodeEventTopics
encodeFunctionData
encodeFunctionResult
getAbiItem
For example, readContract
:
const const result: bigintresult = await const client: {
account: undefined;
batch?: {
multicall?: boolean | {
batchSize?: number | undefined;
wait?: number | undefined;
} | undefined;
} | undefined;
cacheTime: number;
chain: undefined;
key: string;
... 52 more ...;
extend: <const client extends {
...;
} & Partial<...>>(fn: (client: Client<...>) => client) => Client<...>;
}client.readContract: <readonly [{
readonly type: "event";
readonly name: "Approval";
readonly inputs: readonly [{
readonly indexed: true;
readonly name: "owner";
readonly type: "address";
}, {
readonly indexed: true;
readonly name: "spender";
readonly type: "address";
}, {
...;
}];
}, ... 9 more ..., {
...;
}], "balanceOf", [...]>(args: ReadContractParameters<...>) => Promise<...>Calls a read-only function on a contract, and returns the response.
Docs: https://viem.sh/docs/contract/readContract
Examples: https://stackblitz.com/github/wevm/viem/tree/main/examples/contracts/reading-contracts
readContract({
address: `0x${string}`address: '0xecb504d39723b0be0e3a9aa33d646642d1051ee1',
abi: readonly [{
readonly type: "event";
readonly name: "Approval";
readonly inputs: readonly [{
readonly indexed: true;
readonly name: "owner";
readonly type: "address";
}, {
readonly indexed: true;
readonly name: "spender";
readonly type: "address";
}, {
...;
}];
}, ... 9 more ..., {
...;
}]abi: const erc20Abi: readonly [{
readonly type: "event";
readonly name: "Approval";
readonly inputs: readonly [
{
readonly indexed: true;
readonly name: "owner";
readonly type: "address";
},
{
readonly indexed: true;
readonly name: "spender";
readonly type: "address";
},
{
readonly indexed: false;
readonly name: "value";
readonly type: "uint256";
}
];
}, {
readonly type: "event";
readonly name: "Transfer";
readonly inputs: readonly [
{
readonly indexed: true;
readonly name: "from";
readonly type: "address";
},
{
readonly indexed: true;
readonly name: "to";
readonly type: "address";
},
{
readonly indexed: false;
readonly name: "value";
readonly type: "uint256";
}
];
}, {
readonly type: "function";
readonly name: "allowance";
readonly stateMutability: "view";
readonly inputs: readonly [
{
readonly name: "owner";
readonly type: "address";
},
{
readonly name: "spender";
readonly type: "address";
}
];
readonly outputs: readonly [
{
readonly type: "uint256";
}
];
}, ... 7 more ..., {
...;
}]erc20Abi,
functionName: "symbol" | "name" | "balanceOf" | "allowance" | "decimals" | "totalSupply"functionName: 'balanceOf',
// ↑ Notice how "transfer" is not included since it is not a "read" function
args: readonly [`0x${string}`]args: ['0x27a69ffba1e939ddcfecc8c7e0f967b872bac65c'],
})
EIP-712 Typed Data
Adding a const assertion to types
or defining types
inline adds type inference to signTypedData
's value
configuration parameter:
const const result: Promise<`0x${string}`>result = const client: {
account: {
address: "0x";
type: "json-rpc";
};
batch?: {
multicall?: boolean | {
batchSize?: number | undefined;
wait?: number | undefined;
} | undefined;
} | undefined;
cacheTime: number;
... 24 more ...;
extend: <const client extends {
...;
} & Partial<...>>(fn: (client: Client<...>) => client) => Client<...>;
}client.signTypedData: <{
readonly Person: readonly [{
readonly name: "name";
readonly type: "string";
}, {
readonly name: "wallet";
readonly type: "address";
}];
readonly Mail: readonly [{
readonly name: "from";
readonly type: "Person";
}, {
...;
}, {
...;
}];
}, "Mail">(args: SignTypedDataParameters<...>) => Promise<...>Signs typed data and calculates an Ethereum-specific signature in EIP-191 format: keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))
.
Docs: https://viem.sh/docs/actions/wallet/signTypedData
JSON-RPC Methods:
JSON-RPC Accounts: eth_signTypedData_v4
Local Accounts: Signs locally. No JSON-RPC request.
signTypedData({
domain?: {
chainId?: number | undefined;
name?: string | undefined;
salt?: `0x${string}` | undefined;
verifyingContract?: `0x${string}` | undefined;
version?: string | undefined;
} | undefineddomain: {
name?: string | undefinedname: 'Ether Mail',
version?: string | undefinedversion: '1',
chainId?: number | undefinedchainId: 1,
verifyingContract?: `0x${string}` | undefinedverifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
},
types: {
readonly Person: readonly [{
readonly name: "name";
readonly type: "string";
}, {
readonly name: "wallet";
readonly type: "address";
}];
readonly Mail: readonly [{
readonly name: "from";
readonly type: "Person";
}, {
...;
}, {
...;
}];
}types: {
type Person: readonly [{
readonly name: "name";
readonly type: "string";
}, {
readonly name: "wallet";
readonly type: "address";
}]Person: [
{ name: "name"name: 'name', type: "string"type: 'string' },
{ name: "wallet"name: 'wallet', type: "address"type: 'address' },
],
type Mail: readonly [{
readonly name: "from";
readonly type: "Person";
}, {
readonly name: "to";
readonly type: "Person";
}, {
readonly name: "contents";
readonly type: "string";
}]Mail: [
{ name: "from"name: 'from', type: "Person"type: 'Person' },
{ name: "to"name: 'to', type: "Person"type: 'Person' },
{ name: "contents"name: 'contents', type: "string"type: 'string' },
],
},
primaryType: "Person" | "Mail"primaryType: 'Mail',
message: {
from: {
name: string;
wallet: `0x${string}`;
};
to: {
name: string;
wallet: `0x${string}`;
};
contents: string;
}message: {
from: {
name: string;
wallet: `0x${string}`;
}from: {
name: stringname: 'Cow',
wallet: `0x${string}`wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: string;
wallet: `0x${string}`;
}to: {
name: stringname: 'Bob',
wallet: `0x${string}`wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: stringcontents: 'Hello, Bob!',
},
})
Other
The following utilities support type inference when you use const assertions or define arguments inline:
decodeAbiParameters
encodeAbiParameters
encodePacked
parseAbi
parseAbiItem
parseAbiParameter
parseAbiParameters
Configuring Internal Types
For advanced use-cases, you may want to configure viem's internal types. Most of viem's types relating to ABIs and EIP-712 Typed Data are powered by ABIType. See ABIType's documentation for more info on how to configure types.
window
Polyfill
By importing the viem/window
Polyfill, the global window.ethereum
will typed as an EIP1193Provider
(including a fully-typed request
function & typed events).
import 'viem/window';
const const hash: string | boolean | RpcFeeHistory | RpcBlock | RpcLog[] | `0x${string}`[] | RpcProof | RpcTransaction | RpcTransactionReceipt | RpcUncle | NetworkSync | WalletPermission[] | nullhash = await var window: Window & typeof globalThiswindow.Window.ethereum?: {
on: <TEvent extends keyof EIP1193EventMap>(event: TEvent, listener: EIP1193EventMap[TEvent]) => void;
removeListener: <TEvent extends keyof EIP1193EventMap>(event: TEvent, listener: EIP1193EventMap[TEvent]) => void;
request: EIP1193RequestFn<...>;
} | undefinedethereum.request: <undefined, {
method: "web3_clientVersion";
params?: undefined;
} | {
method: "web3_sha3";
params: [data: `0x${string}`];
} | {
method: "net_listening";
params?: undefined;
} | {
method: "net_peerCount";
params?: undefined;
} | ... 51 more ... | {
...;
}, string | ... 11 more ... | null>(args: {
...;
} | ... 54 more ... | {
...;
}, options?: EIP1193RequestOptions | undefined) => Promise<...>request({
method: "e "method: 'eeth_blobGasPriceeth_blockNumbereth_chainIdeth_coinbaseeth_gasPriceeth_maxPriorityFeePerGaseth_newBlockFiltereth_newPendingTransactionFiltereth_protocolVersioneth_accountseth_requestAccountseth_syncing
})
const const hash: string | boolean | RpcFeeHistory | RpcBlock | RpcLog[] | `0x${string}`[] | RpcProof | RpcTransaction | RpcTransactionReceipt | RpcUncle | NetworkSync | WalletPermission[] | nullhash = await var window: Window & typeof globalThiswindow.Window.ethereum?: {
on: <TEvent extends keyof EIP1193EventMap>(event: TEvent, listener: EIP1193EventMap[TEvent]) => void;
removeListener: <TEvent extends keyof EIP1193EventMap>(event: TEvent, listener: EIP1193EventMap[TEvent]) => void;
request: EIP1193RequestFn<...>;
} | undefinedethereum.request: <undefined, {
method: "web3_clientVersion";
params?: undefined;
} | {
method: "web3_sha3";
params: [data: `0x${string}`];
} | {
method: "net_listening";
params?: undefined;
} | {
method: "net_peerCount";
params?: undefined;
} | ... 51 more ... | {
...;
}, string | ... 11 more ... | null>(args: {
...;
} | ... 54 more ... | {
...;
}, options?: EIP1193RequestOptions | undefined) => Promise<...>request({
method: "eth_getTransactionByHash"method: 'eth_getTransactionByHash',
params: [hash: `0x${string}`]params: [
})