Skip to main content
skill CX Codex

viem-integration — Claude Code skill for EVM integration

Guides TypeScript and JavaScript projects through EVM integration with viem. Use it to read chain data, call contracts, or send transactions.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add Uniswap/uniswap-ai --skill viem-integration -a claude-code
Open on GitHub

What viem-integration does

The skill maps common tasks to viem clients: PublicClient for reads and WalletClient for writes and signatures. Its examples cover fetching a balance, reading a contract, sending a transaction, and simulating a contract call before submitting it. React projects can use wagmi; webSocket() is shown for real-time event subscriptions.

It also covers chain imports, unit conversion, and address checks. Before generating code, the skill requires validating addresses and ABI inputs. Private keys belong in process.env.PRIVATE_KEY, not in source code, and RPC URLs must use https:// or wss://. An RPC URL with another protocol fails that requirement.

What it helps with

  • fetch an address balance on an EVM chain
  • read values from a smart contract
  • submit a transaction and wait for its receipt
  • connect a React app with wagmi
  • validate an address before a contract call

Example prompts for the agent

  • Read this address's balance with viem and display it in ETH.
  • Set up the React app to connect to an EVM chain with wagmi.
  • Prepare a contract write: simulate the call first, then submit the transaction.

How to install viem-integration

1Skills CLI (Vercel)

npx skills add Uniswap/uniswap-ai --skill viem-integration -a claude-code

2Manually: put the files in place

~/.claude/skills/viem-integration/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/viem-integration/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about viem-integration

How do I install viem-integration in Claude Code?

Run npx skills add Uniswap/uniswap-ai --skill viem-integration -a claude-code. The skill is installed at ~/.claude/skills/viem-integration/SKILL.md.

What does viem-integration do?

It guides EVM development with viem, including reading chain data, calling smart contracts, and sending transactions. It also covers wagmi for React apps.

Where should I keep the private key?

The examples read it from process.env.PRIVATE_KEY. The skill says not to hardcode private keys and to validate them at runtime.

Source text

SKILL.md · MIT
---
name: viem-integration
description: Integrate EVM blockchains using viem. Use when user says "read blockchain data", "send transaction", "interact with smart contract", "connect to Ethereum", "use viem", "use wagmi", "wallet integration", "viem setup", or mentions blockchain/EVM development with TypeScript.
allowed-tools: Read, Write, Edit, Glob, Grep, Bash(npm:*), Bash(npx:*), WebFetch, Task(subagent_type:viem-integration-expert)
model: opus
license: MIT
metadata:
  author: uniswap
  version: '1.0.0'
---

# viem Integration

Integrate EVM blockchains using viem for TypeScript/JavaScript applications.

## Quick Decision Guide

| Building...                | Use This                       |
| -------------------------- | ------------------------------ |
| Node.js script/backend     | viem with http transport       |
| React/Next.js frontend     | wagmi hooks (built on viem)    |
| Real-time event monitoring | viem with webSocket transport  |
| Browser wallet integration | wagmi or viem custom transport |

## Installation

```bash
# Core library
npm install viem

# For React apps, also install wagmi
npm install wagmi viem @tanstack/react-query
```

## Core Concepts

### Clients

viem uses two client types:

| Client           | Purpose              | Example Use                              |
| ---------------- | -------------------- | ---------------------------------------- |
| **PublicClient** | Read-only operations | Get balances, read contracts, fetch logs |
| **WalletClient** | Write operations     | Send transactions, sign messages         |

### Transports

| Transport     | Use Case                          |
| ------------- | --------------------------------- |
| `http()`      | Standard RPC calls (most common)  |
| `webSocket()` | Real-time event subscriptions     |
| `custom()`    | Browser wallets (window.ethereum) |

### Chains

viem includes 50+ chain definitions. Import from `viem/chains`:

```typescript
import { mainnet, arbitrum, optimism, base, polygon } from 'viem/chains';
```

---

## Input Validation Rules

Before interpolating ANY user-provided value into generated TypeScript code:

- **Ethereum addresses**: MUST match `^0x[a-fA-F0-9]{40}$` — use viem's `isAddress()` for validation
- **Chain IDs**: MUST be from viem's supported chain definitions
- **Private keys**: MUST NEVER be hardcoded — always use `process.env.PRIVATE_KEY` with runtime validation
- **RPC URLs**: MUST use `https://` or `wss://` protocols only
- **ABI inputs**: Validate types match expected Solidity types before encoding

## Quick Start Examples

### Read Balance

```typescript
import { createPublicClient, http, formatEther } from 'viem';
import { mainnet } from 'viem/chains';

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

const balance = await client.getBalance({
  address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
});

console.log(`Balance: ${formatEther(balance)} ETH`);
```

### Read Contract

```typescript
import { createPublicClient, http, parseAbi } from 'viem';
import { mainnet } from 'viem/chains';

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

const abi = parseAbi([
  'function balanceOf(address) view returns (uint256)',
  'function decimals() view returns (uint8)',
]);

const balance = await client.readContract({
  address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  abi,
  functionName: 'balanceOf',
  args: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'],
});
```

### Send Transaction

```typescript
import { createWalletClient, http, parseEther } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { mainnet } from 'viem/chains';

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

const client = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
});

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

console.log(`Transaction hash: ${hash}`);
```

### Write to Contract

```typescript
import { createWalletClient, createPublicClient, http, parseAbi, parseUnits } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { mainnet } from 'viem/chains';

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);

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

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

const abi = parseAbi(['function transfer(address to, uint256 amount) returns (bool)']);

// Simulate first to catch errors
const { request } = await publicClient.simulateContract({
  address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  abi,
  functionName: 'transfer',
  args: ['0x...', parseUnits('100', 6)],
  account,
});

// Execute the transaction
const hash = await walletClient.writeContract(request);

// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log(`Confirmed in block ${receipt.blockNumber}`);
```

---

## Reference Documentation

For deeper coverage of specific topics:

| Topic                            | Reference File                                                 |
| -------------------------------- | -------------------------------------------------------------- |
| Client setup, transports, chains | [Clients & Transports](./references/clients-and-transports.md) |
| Reading blockchain data          | [Reading Data](./references/reading-data.md)                   |
| Sending transactions             | [Writing Transactions](./references/writing-transactions.md)   |
| Private keys, HD wallets         | [Accounts & Keys](./references/accounts-and-keys.md)           |
| ABI handling, multicall          | [Contract Patterns](./references/contract-patterns.md)         |
| React/wagmi hooks                | [Wagmi React](./references/wagmi-react.md)                     |

---

## Related Plugins

Once you're comfortable with viem basics, the **uniswap-trading** plugin provides comprehensive Uniswap swap integration:

- Uniswap Trading API integration
- Universal Router SDK usage
- Token swap implementations

Install it with: `claude plugin add @uniswap/uniswap-trading`

---

## Common Utilities

### Unit Conversion

```typescript
import { parseEther, formatEther, parseUnits, formatUnits } from 'viem';

// ETH
parseEther('1.5'); // 1500000000000000000n (wei)
formatEther(1500000000000000000n); // "1.5"

// Tokens (e.g., USDC with 6 decimals)
parseUnits('100', 6); // 100000000n
formatUnits(100000000n, 6); // "100"
```

### Address Utilities

```typescript
import { getAddress, isAddress } from 'viem';

isAddress('0x...'); // true/false
getAddress('0x...'); // checksummed address
```

### Hashing

```typescript
import { keccak256, toHex } from 'viem';

keccak256(toHex('hello')); // 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
```

---

## Error Handling

viem throws typed errors that can be caught and handled:

```typescript
import { ContractFunctionExecutionError, InsufficientFundsError } from 'viem'

try {
  await client.writeContract(...)
} catch (error) {
  if (error instanceof ContractFunctionExecutionError) {
    console.error('Contract call failed:', error.shortMessage)
  }
  if (error instanceof InsufficientFundsError) {
    console.error('Not enough ETH for gas')
  }
}
```

---

## Resources

- [viem Documentation](https://viem.sh)
- [wagmi Documentation](https://wagmi.sh)
- [viem GitHub](https://github.com/wevm/viem)
- [wagmi GitHub](https://github.com/wevm/wagmi)

© uniswap · MIT · Uniswap/uniswap-ai

Similar skills

All skills for Backend and APIs →

From the same set13

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor