Documentation

Migrating from ethers.js to viem

Guide for migrating Selendra SDK applications from ethers.js to viem

Migrating from ethers.js to viem

Starting with v2.0.0, the Selendra SDK uses viem instead of ethers.js for EVM interactions. This guide helps you migrate existing code.

Why viem?

  • Type Safety: Better TypeScript inference and compile-time checks
  • Smaller Bundle: ~35kb vs ~120kb for ethers.js
  • Modern: Built for modern JavaScript with tree-shaking
  • Performance: Optimized for speed and efficiency
  • wagmi: Seamless integration with wagmi for React apps

Quick Reference

ethers.jsviem
JsonRpcProvidercreatePublicClient
WalletcreateWalletClient
ContractgetContract
utils.formatEtherformatEther
utils.parseEtherparseEther
BigNumberNative bigint

Installation

# Remove ethers
npm uninstall ethers

# Install viem and wagmi
npm install viem wagmi @tanstack/react-query

Provider Migration

Before (ethers.js)

import { ethers } from 'ethers';

const provider = new ethers.JsonRpcProvider('https://rpc.selendra.org');

// Get balance
const balance = await provider.getBalance(address);
console.log(ethers.formatEther(balance));

// Get block
const block = await provider.getBlock('latest');

After (viem)

import { createPublicClient, http, formatEther } from 'viem';
import { selendra } from '@selendrajs/sdk/chains';

const client = createPublicClient({
  chain: selendra,
  transport: http(),
});

// Get balance
const balance = await client.getBalance({ address });
console.log(formatEther(balance));

// Get block
const block = await client.getBlock();

Wallet Migration

Before (ethers.js)

import { ethers } from 'ethers';

const wallet = new ethers.Wallet(privateKey, provider);

// Sign message
const signature = await wallet.signMessage('Hello');

// Send transaction
const tx = await wallet.sendTransaction({
  to: recipient,
  value: ethers.parseEther('1'),
});
await tx.wait();

After (viem)

import { createWalletClient, http, parseEther } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { selendra } from '@selendrajs/sdk/chains';

const account = privateKeyToAccount(privateKey);
const client = createWalletClient({
  account,
  chain: selendra,
  transport: http(),
});

// Sign message
const signature = await client.signMessage({ message: 'Hello' });

// Send transaction
const hash = await client.sendTransaction({
  to: recipient,
  value: parseEther('1'),
});

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

Contract Interaction

Before (ethers.js)

import { ethers } from 'ethers';

const contract = new ethers.Contract(address, abi, signer);

// Read
const balance = await contract.balanceOf(userAddress);

// Write
const tx = await contract.transfer(recipient, amount);
await tx.wait();

After (viem)

import { getContract } from 'viem';

const contract = getContract({
  address,
  abi,
  client: { public: publicClient, wallet: walletClient },
});

// Read
const balance = await contract.read.balanceOf([userAddress]);

// Write
const hash = await contract.write.transfer([recipient, amount]);
await publicClient.waitForTransactionReceipt({ hash });

BigNumber Migration

viem uses native JavaScript bigint instead of ethers.js BigNumber:

Before (ethers.js)

import { ethers } from 'ethers';

const amount = ethers.parseEther('1.5');
const sum = amount.add(ethers.parseEther('0.5'));
const isGreater = sum.gt(ethers.parseEther('1'));
const formatted = ethers.formatEther(sum);

After (viem)

import { parseEther, formatEther } from 'viem';

const amount = parseEther('1.5');
const sum = amount + parseEther('0.5');
const isGreater = sum > parseEther('1');
const formatted = formatEther(sum);

React Migration (wagmi)

Before (ethers.js + custom hooks)

import { ethers } from 'ethers';
import { useState, useEffect } from 'react';

function useBalance(address) {
  const [balance, setBalance] = useState(null);

  useEffect(() => {
    const provider = new ethers.JsonRpcProvider('https://rpc.selendra.org');
    provider.getBalance(address).then(setBalance);
  }, [address]);

  return balance;
}

After (wagmi)

import { useBalance } from 'wagmi';

function MyComponent({ address }) {
  const { data: balance, isLoading } = useBalance({ address });

  if (isLoading) return <div>Loading...</div>;
  return <div>Balance: {balance?.formatted} SEL</div>;
}

Wagmi Setup

import { WagmiProvider, createConfig, http } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { selendra, selendraTestnet } from '@selendrajs/sdk/chains';

const config = createConfig({
  chains: [selendra, selendraTestnet],
  transports: {
    [selendra.id]: http(),
    [selendraTestnet.id]: http(),
  },
});

const queryClient = new QueryClient();

function App() {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <YourApp />
      </QueryClientProvider>
    </WagmiProvider>
  );
}

Common Patterns

Error Handling

// viem errors are more descriptive
import { BaseError, ContractFunctionRevertedError } from 'viem';

try {
  await contract.write.transfer([recipient, amount]);
} catch (err) {
  if (err instanceof ContractFunctionRevertedError) {
    const errorName = err.data?.errorName;
    console.log(`Contract reverted: ${errorName}`);
  }
}

Gas Estimation

// Estimate gas
const gas = await publicClient.estimateGas({
  account,
  to: recipient,
  value: parseEther('1'),
});

// Get gas price
const gasPrice = await publicClient.getGasPrice();

Event Listening

// Watch for events
const unwatch = publicClient.watchContractEvent({
  address: contractAddress,
  abi,
  eventName: 'Transfer',
  onLogs: (logs) => {
    console.log('Transfer events:', logs);
  },
});

// Cleanup
unwatch();

Troubleshooting

Common Issues

  1. BigInt serialization: Use bigint replacer for JSON

    JSON.stringify(data, (_, v) => typeof v === 'bigint' ? v.toString() : v);
    
  2. Account required: Always provide account for write operations

    const client = createWalletClient({ account, chain, transport });
    
  3. Chain mismatch: Ensure chain is properly configured

    import { selendra } from '@selendrajs/sdk/chains';
    

Resources

Contribute

Found an issue or want to contribute?

Help us improve this documentation by editing this page on GitHub.

Edit this page on GitHub
Selendra - Build on Cambodian Blockchain with Testnet