Skip to main content

Making C-Chain RPC Calls Using Viem

In this guide, you will learn how to make JSON-RPC calls to Avalanche’s C-Chain using viem, a TypeScript interface for Ethereum that works seamlessly with Avalanche’s EVM-compatible C-Chain.

Prerequisites

  • Node.js (v14+) installed
  • Basic familiarity with TypeScript/JavaScript
  • Understanding of Ethereum/EVM concepts

Step 1: Install Dependencies

# Create a new project
mkdir avalanche-viem-example
cd avalanche-viem-example
npm init -y

# Install viem
npm install viem

# Install TypeScript (optional)
npm install -D typescript @types/node tsx

Step 2: Use Built-in Avalanche Chains

Viem includes Avalanche chain definitions out of the box:
import { avalanche, avalancheFuji } from 'viem/chains'

// avalanche = Avalanche Mainnet (Chain ID: 43114)
// avalancheFuji = Avalanche Fuji Testnet (Chain ID: 43113)

Step 3: Reading Blockchain Data

Create a file called readData.ts:
import { createPublicClient, http, formatEther } from 'viem'
import { avalanche } from 'viem/chains'

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

async function readData() {
  // Get the latest block
  const block = await client.getBlock()
  console.log('Latest block:', block.number)
  
  // Get AVAX balance
  const balance = await client.getBalance({ 
    address: '0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC' 
  })
  console.log('Balance:', formatEther(balance), 'AVAX')
  
  // Get gas price
  const gasPrice = await client.getGasPrice()
  console.log('Gas price:', formatEther(gasPrice), 'AVAX')
}

readData()

Step 4: Sending Transactions

Create a file called sendTransaction.ts:
import { createWalletClient, createPublicClient, http, parseEther } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { avalancheFuji } from 'viem/chains'

// Use environment variable for private key
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)

const walletClient = createWalletClient({
  account,
  chain: avalancheFuji,
  transport: http(),
})

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

async function sendAvax() {
  // Send 0.001 AVAX
  const hash = await walletClient.sendTransaction({
    to: '0x0000000000000000000000000000000000000000',
    value: parseEther('0.001'),
  })
  
  console.log('Transaction hash:', hash)
  
  // Wait for confirmation
  const receipt = await publicClient.waitForTransactionReceipt({ hash })
  console.log('Transaction confirmed:', receipt.status)
}

sendAvax()

Step 5: Smart Contract Interaction

Create a file called contractCall.ts:
import { createPublicClient, http, parseAbi, formatUnits } from 'viem'
import { avalanche } from 'viem/chains'

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

// ERC20 ABI (minimal)
const abi = parseAbi([
  'function balanceOf(address) view returns (uint256)',
  'function decimals() view returns (uint8)',
  'function symbol() view returns (string)',
])

async function readToken() {
  // USDC on Avalanche
  const usdcAddress = '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E'
  const userAddress = '0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC'
  
  // Read multiple values at once
  const [balance, decimals, symbol] = await Promise.all([
    client.readContract({
      address: usdcAddress,
      abi,
      functionName: 'balanceOf',
      args: [userAddress],
    }),
    client.readContract({
      address: usdcAddress,
      abi,
      functionName: 'decimals',
    }),
    client.readContract({
      address: usdcAddress,
      abi,
      functionName: 'symbol',
    }),
  ])
  
  console.log(`${symbol} Balance:`, formatUnits(balance, decimals))
}

readToken()

Environment Variables

Create a .env file for sensitive data:
PRIVATE_KEY=0x... # Never commit this!

Running the Examples

# Read blockchain data
npx tsx readData.ts

# Send transaction (requires PRIVATE_KEY)
npx tsx sendTransaction.ts

# Read smart contract
npx tsx contractCall.ts

Best Practices

  1. Security: Never hardcode private keys
  2. Gas Management: Always estimate gas before sending transactions
  3. Error Handling: Wrap async operations in try-catch blocks
  4. Network Selection: Use testnet (Fuji) for development

Conclusion

Viem provides a clean, type-safe interface for Avalanche C-Chain development with:
  • Built-in Avalanche chain configurations
  • Full EVM compatibility
  • Excellent TypeScript support
  • Efficient multicall and batching
For P-Chain and X-Chain operations, use the Avalanche SDK.

Resources

I