Skip to main content
viem is a popular TypeScript library for interacting with EVM chains. This guide shows how to use viem with Dfns wallets.
See complete working examples in the dfns-sdk-ts repository, including account abstraction integrations with Alchemy, Biconomy, Pimlico, and ZeroDev.

Setup

Install the required packages:
npm install viem @dfns/sdk @dfns/lib-viem

Creating a Dfns-backed viem account

Use DfnsWallet and toAccount from the SDK to create a viem-compatible account:
import { DfnsWallet } from '@dfns/lib-viem'
import { DfnsApiClient } from '@dfns/sdk'
import { AsymmetricKeySigner } from '@dfns/sdk-keysigner'
import { createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { toAccount } from 'viem/accounts'

// Initialize Dfns client
const signer = new AsymmetricKeySigner({
  credId: process.env.DFNS_CRED_ID!,
  privateKey: process.env.DFNS_PRIVATE_KEY!,
})

const dfnsClient = new DfnsApiClient({
  authToken: process.env.DFNS_AUTH_TOKEN!,
  baseUrl: process.env.DFNS_API_URL!,
  signer,
})

// Initialize Dfns wallet
const dfnsWallet = await DfnsWallet.init({
  walletId: process.env.DFNS_WALLET_ID!,
  dfnsClient,
})

// Create viem wallet client
const walletClient = createWalletClient({
  account: toAccount(dfnsWallet),
  chain: mainnet,
  transport: http(),
})

Sending transactions

Simple ETH transfer

import { parseEther } from 'viem'

const hash = await walletClient.sendTransaction({
  to: '0x...',
  value: parseEther('0.1'),
})

// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash })

Contract interaction

import { parseAbi, parseUnits } from 'viem'

const hash = await walletClient.writeContract({
  address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  abi: parseAbi(['function transfer(address to, uint256 amount) returns (bool)']),
  functionName: 'transfer',
  args: ['0xRecipient...', parseUnits('100', 6)], // 100 USDC
})

EIP-712 typed data signing

const signature = await walletClient.signTypedData({
  domain: {
    name: 'My App',
    version: '1',
    chainId: 1,
    verifyingContract: '0x...',
  },
  types: {
    Order: [
      { name: 'maker', type: 'address' },
      { name: 'amount', type: 'uint256' },
    ],
  },
  primaryType: 'Order',
  message: {
    maker: walletAddress,
    amount: 1000000n,
  },
})

Account abstraction (gasless transactions)

Dfns wallets work with account abstraction providers for gasless transactions. The SDK includes examples for:
ProviderUse case
AlchemyModular accounts with gas policies
BiconomySmart accounts with paymasters
PimlicoBundler and paymaster services
ZeroDevKernel smart accounts

Example with Alchemy

import { createModularAccountAlchemyClient } from '@alchemy/aa-alchemy'
import { LocalAccountSigner } from '@alchemy/aa-core'
import { sepolia } from 'viem/chains'

const smartAccountClient = await createModularAccountAlchemyClient({
  apiKey: process.env.ALCHEMY_API_KEY!,
  chain: sepolia,
  signer: new LocalAccountSigner(toAccount(dfnsWallet)),
  gasManagerConfig: {
    policyId: process.env.ALCHEMY_GAS_POLICY_ID!,
  },
})

// Transactions are now gasless for your users
const hash = await smartAccountClient.sendTransaction({
  to: '0x...',
  data: '0x...',
})

Multicall (batch transactions)

Batch multiple contract calls in a single transaction:
import { encodeFunctionData, parseAbi } from 'viem'

const multiCallAddress = '0x...' // Multicall3 contract

const hash = await walletClient.sendTransaction({
  to: multiCallAddress,
  data: encodeFunctionData({
    abi: multicallAbi,
    functionName: 'aggregate3',
    args: [[
      { target: token1, callData: transferData1 },
      { target: token2, callData: transferData2 },
    ]],
  }),
})
See the multicall example for complete implementation.

Reading contract data

Use a public client for read operations (no signing needed):
import { createPublicClient, http, parseAbi } from 'viem'
import { mainnet } from 'viem/chains'

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

// Read ERC-20 balance
const balance = await publicClient.readContract({
  address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  abi: parseAbi(['function balanceOf(address) view returns (uint256)']),
  functionName: 'balanceOf',
  args: [walletAddress],
})

Using Dfns Broadcast API instead

For better policy integration, you may prefer using the Dfns Broadcast API directly:
import { encodeFunctionData, parseAbi } from 'viem'

// Encode the contract call with viem
const data = encodeFunctionData({
  abi: parseAbi(['function transfer(address to, uint256 amount) returns (bool)']),
  functionName: 'transfer',
  args: [recipient, amount],
})

// Broadcast via Dfns
const result = await dfnsClient.wallets.broadcastTransaction({
  walletId,
  body: {
    kind: 'Evm',
    to: tokenAddress,
    data,
  },
})
This approach integrates with Dfns policies and provides consistent transaction tracking.

viem examples

Complete working examples

TypeScript SDK

Dfns TypeScript SDK documentation

EVM Signing

EVM signature types reference

EVM Broadcast

EVM transaction broadcast reference
Last modified on January 27, 2026