viem Integration
viem is a popular TypeScript library for interacting with EVM chains. This topic explains how to integrate viem with IBM Digital Asset Haven wallets.
Setup
Install the required packages.
npm install viem @dfns/sdk @dfns/lib-viem
Creating a IBM Digital Asset Haven-backed viem account
- Use
DfnsWalletandtoAccountfrom 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 the IBM Digital Asset Haven 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 the IBM Digital Asset Haven 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)
IBM Digital Asset Haven wallets work with account abstraction providers for gasless transactions. The SDK includes examples for:
| Provider | Use case |
|---|---|
| Alchemy | Modular accounts with gas policies |
| Biconomy | Smart accounts with paymasters |
| Pimlico | Bundler and paymaster services |
| ZeroDev | Kernel 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 },
]],
}),
})
Reading contract data
- Use a public client for read operations (signing is not required).
import { createPublicClient, http, parseAbi } from 'viem' import { mainnet } from 'viem/chains' const publicClient = createPublicClient({ chain: mainnet, transport: http(), }) - Read the ERC-20 balance.
const balance = await publicClient.readContract({ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC abi: parseAbi(['function balanceOf(address) view returns (uint256)']), functionName: 'balanceOf', args: [walletAddress], })
Using the IBM Digital Asset Haven Broadcast API
For better policy integration, you may prefer to use the IBM Digital Asset Haven 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 IBM Digital Asset Haven.
const result = await dfnsClient.wallets.broadcastTransaction({ walletId, body: { kind: 'Evm', to: tokenAddress, data, }, })
This approach integrates with IBM Digital Asset Haven policies and provides consistent transaction tracking.