Madewgn.
← Back to blog

Type-safe contracts with viem in 2026

How const-asserted ABIs turn smart-contract integration from runtime guesswork into compile-time guarantees.

web3typescriptviem

If you're still hand-rolling ethers.Contract types, you're losing free wins. viem's ABI inference catches whole classes of bugs before they ship.

The problem with stringly-typed ABIs

const c = new ethers.Contract(addr, abi, provider);
const x = await c.balanceOf(wallet); // any

x is any. The compiler can't help when you typo balanceof, swap argument order, or change a return tuple. You find out at runtime, often in production.

viem's approach

import { getContract } from "viem";
 
const erc20 = getContract({
  address: addr,
  abi: erc20Abi, // ABI as a `const`-asserted tuple
  client: publicClient,
});
 
const balance = await erc20.read.balanceOf([wallet]);
//    ^? bigint  — inferred from the ABI

The as const on the ABI is doing all the work. viem walks the literal type and reconstructs argument tuples, return shapes, event payloads — every function call is fully typed end-to-end.

Why this matters in practice

  • Renames break the build, not the app. Rename a function in your Solidity contract, regenerate the ABI, and TypeScript will point at every callsite.
  • Event payloads aren't any. Log decoders get the same treatment — args.from, args.to, args.value are properly typed.
  • No more as. Casts are a smell. With viem, you almost never need one.

One catch

Make sure your ABI export is as const:

export const erc20Abi = [
  // ...
] as const;

If you import from a tool that doesn't preserve literal types (looking at you, certain auto-generators), wrap it in a typed re-export.

Bottom line

For new code in 2026, default to viem + wagmi. The DX gap on ethers v6 is too wide to justify the migration cost on greenfield projects.