All skills
uniswap avatar

/viem-integration

@a514e62
by Uniswap Labsuniswap/uniswap-ai233 stars
39

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.

Use this Skill: https://skilld.dev/gh/uniswap/uniswap-ai/viem-integration

This session only. Nothing lands on disk.

referencesclients-and-transports.md

≈2.8k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Clients and Transports

Detailed reference for viem client setup, transports, and chain configuration.

PublicClient (Read Operations)

The PublicClient is used for all read-only blockchain operations.

Basic Setup

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

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

With Custom RPC

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY'),
});

Configuration Options

const client = createPublicClient({
  chain: mainnet,
  transport: http(),
  batch: {
    multicall: true, // Enable automatic batching via Multicall3
  },
  cacheTime: 4_000, // Cache duration in ms (default: 4000)
  pollingInterval: 4_000, // Polling interval for subscriptions
});

WalletClient (Write Operations)

The WalletClient is used for signing and sending transactions.

With Local Account (Node.js)

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

const account = privateKeyToAccount('0x...');

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

With Browser Wallet (Frontend)

import { createWalletClient, custom } from 'viem';
import { mainnet } from 'viem/chains';

const client = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum!),
});

// Request account access
const [address] = await client.requestAddresses();

Get Addresses

// Get all connected addresses
const addresses = await client.getAddresses();

// Request connection (browser wallet)
const addresses = await client.requestAddresses();

Transport Types

HTTP Transport

Standard RPC transport for most use cases.

import { http } from 'viem';

// Default (uses chain's public RPC)
http();

// Custom RPC URL
http('https://eth-mainnet.g.alchemy.com/v2/KEY');

// With options
http('https://...', {
  batch: true, // Enable JSON-RPC batching
  fetchOptions: {
    // Custom fetch options
    headers: {
      'x-api-key': 'KEY',
    },
  },
  retryCount: 3, // Retry failed requests
  retryDelay: 150, // Delay between retries (ms)
  timeout: 10_000, // Request timeout (ms)
});

WebSocket Transport

For real-time event subscriptions.

import { webSocket } from 'viem';

// Basic
webSocket('wss://eth-mainnet.g.alchemy.com/v2/KEY');

// With options
webSocket('wss://...', {
  keepAlive: true, // Enable keep-alive pings
  reconnect: true, // Auto-reconnect on disconnect
  retryCount: 3,
});

Custom Transport

For browser wallet providers.

import { custom } from 'viem';

// MetaMask / injected wallet
custom(window.ethereum!);

// Any EIP-1193 provider
custom(provider);

Fallback Transport

Use multiple transports with automatic failover.

import { fallback, http, webSocket } from 'viem';

const transport = fallback([
  webSocket('wss://...'),
  http('https://...'),
  http(), // Public RPC as last resort
]);

Chain Configuration

Built-in Chains

viem includes many chain definitions. Import the chain definitions you need, and define any newer chain locally with defineChain if your installed viem version does not export it yet:

import {
  // Mainnets
  mainnet,
  arbitrum,
  avalanche,
  base,
  blast,
  bsc,
  celo,
  linea,
  optimism,
  polygon,
  unichain,
  worldchain,
  zkSync,
  zora,

  // Testnets
  sepolia,
  baseSepolia,
  unichainSepolia,
} from 'viem/chains';

Chain Properties

Each chain includes:

import { mainnet } from 'viem/chains';

mainnet.id; // 1
mainnet.name; // "Ethereum"
mainnet.nativeCurrency; // { name: "Ether", symbol: "ETH", decimals: 18 }
mainnet.rpcUrls; // { default: { http: [...] } }
mainnet.blockExplorers; // { default: { name: "Etherscan", url: "..." } }

Uniswap Trading API Supported Chain IDs

The Uniswap Trading API supported-chain list is the source of truth for chains that can be used with the swapping API. Use the corresponding viem chain export when available; otherwise define the chain locally with defineChain.

Chain ID viem import / definition
Ethereum Mainnet 1 mainnet
OP Mainnet 10 optimism
BNB Smart Chain 56 bsc
Unichain 130 unichain
Polygon 137 polygon
Monad 143 define locally if unavailable
X Layer 196 define locally if unavailable
zkSync 324 zkSync
World Chain 480 worldchain
Soneium 1868 define locally if unavailable
Tempo 4217 define locally if unavailable
Robinhood Chain 4663 define locally if unavailable
Base 8453 base
Arbitrum 42161 arbitrum
Celo 42220 celo
Avalanche 43114 avalanche
Linea 59144 linea
Blast 81457 blast
Zora 7777777 zora
Unichain Sepolia (testnet) 1301 unichainSepolia
Base Sepolia (testnet) 84532 baseSepolia
Ethereum Sepolia (testnet) 11155111 sepolia

Custom Chain Definition

import { defineChain } from 'viem';

const myChain = defineChain({
  id: 123456,
  name: 'My Chain',
  nativeCurrency: {
    name: 'My Token',
    symbol: 'MYT',
    decimals: 18,
  },
  rpcUrls: {
    default: {
      http: ['https://rpc.mychain.com'],
    },
  },
  blockExplorers: {
    default: {
      name: 'My Explorer',
      url: 'https://explorer.mychain.com',
    },
  },
});

RPC Providers

Public RPCs

Each chain has a default public RPC (rate-limited):

const client = createPublicClient({
  chain: mainnet,
  transport: http(), // Uses public RPC
});

Alchemy

const ALCHEMY_KEY = process.env.ALCHEMY_API_KEY;

const client = createPublicClient({
  chain: mainnet,
  transport: http(`https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`),
});

// Chain-specific URLs
// Arbitrum: arb-mainnet.g.alchemy.com/v2/KEY
// Optimism: opt-mainnet.g.alchemy.com/v2/KEY
// Base: base-mainnet.g.alchemy.com/v2/KEY
// Polygon: polygon-mainnet.g.alchemy.com/v2/KEY

Infura

const INFURA_KEY = process.env.INFURA_API_KEY;

const client = createPublicClient({
  chain: mainnet,
  transport: http(`https://mainnet.infura.io/v3/${INFURA_KEY}`),
});

// Chain-specific URLs
// Arbitrum: arbitrum-mainnet.infura.io/v3/KEY
// Optimism: optimism-mainnet.infura.io/v3/KEY
// Polygon: polygon-mainnet.infura.io/v3/KEY

QuickNode

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://your-endpoint.quiknode.pro/your-key/'),
});

Rate Limits and Best Practices

Public RPCs and free-tier provider plans enforce rate limits. Exceeding them causes 429 Too Many Requests errors or silent throttling.

Provider Free Tier Limit Notes
Public RPCs ~5-10 req/s Shared across all users; avoid for production
Alchemy (free) 330 CU/s (~30 req/s) Compute Units vary by method
Infura (free) 10 req/s Per-second burst limit
QuickNode (free) 25 req/s Varies by plan

Mitigation strategies:

  1. Use batch.multicall — Batches multiple eth_call reads into a single Multicall3 contract call, dramatically reducing request count.
  2. Use the fallback transport — Automatically retries on a backup RPC when the primary is rate-limited.
  3. Set retryCount and retryDelay on transports to handle transient 429s gracefully.
  4. Cache aggressively — The cacheTime option on createPublicClient avoids redundant calls for data that doesn't change within the cache window.
// Production-ready client with rate limit resilience
const client = createPublicClient({
  chain: mainnet,
  transport: fallback([
    http('https://eth-mainnet.g.alchemy.com/v2/KEY', {
      retryCount: 3,
      retryDelay: 500,
    }),
    http('https://mainnet.infura.io/v3/KEY', {
      retryCount: 2,
      retryDelay: 1000,
    }),
    http(), // Public RPC last resort
  ]),
  batch: {
    multicall: true,
  },
  cacheTime: 4_000,
});

Multi-Chain Setup

Separate Clients

import { createPublicClient, http } from 'viem';
import { mainnet, arbitrum, base } from 'viem/chains';

const clients = {
  [mainnet.id]: createPublicClient({
    chain: mainnet,
    transport: http(),
  }),
  [arbitrum.id]: createPublicClient({
    chain: arbitrum,
    transport: http(),
  }),
  [base.id]: createPublicClient({
    chain: base,
    transport: http(),
  }),
};

// Use by chain ID
const balance = await clients[1].getBalance({ address: '0x...' });

Factory Function

import { createPublicClient, http, type Chain } from 'viem';
import { mainnet, arbitrum, base } from 'viem/chains';

const chains: Record<number, Chain> = {
  [mainnet.id]: mainnet,
  [arbitrum.id]: arbitrum,
  [base.id]: base,
};

function getClient(chainId: number) {
  const chain = chains[chainId];
  if (!chain) throw new Error(`Unsupported chain: ${chainId}`);

  return createPublicClient({
    chain,
    transport: http(),
  });
}

Environment Variables Pattern

Recommended setup for Node.js applications:

// config.ts
import { createPublicClient, createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { mainnet } from 'viem/chains';

if (!process.env.RPC_URL) {
  throw new Error('RPC_URL environment variable is required');
}

export const publicClient = createPublicClient({
  chain: mainnet,
  transport: http(process.env.RPC_URL),
});

// Only create wallet client if private key is available
export const walletClient = process.env.PRIVATE_KEY
  ? createWalletClient({
      account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
      chain: mainnet,
      transport: http(process.env.RPC_URL),
    })
  : null;

Example .env:

RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
PRIVATE_KEY=0x...

Source: SKILL.md on GitHub

2 warnings17d4 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    The skill provides comprehensive documentation and secure coding examples for integrating EVM blockchains using the viem and wagmi libraries. It correctly emphasizes the use of environment variables for secret management and provides standard development test keys for educational purposes.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: MEDIUM · 1 issue

  • Runlayer6mo

    2/7 files flagged

Signed by skilld at a514e62. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 7 months ago
What it can do
Reads files Edits files Runs commands Network
Modelopus
model
opus
metadata
{
  "author": "uniswap",
  "version": "1.0.0"
}
All 9 allowed tools
ReadWriteEditGlobGrepBash(npm:*)Bash(npx:*)WebFetchTask(subagent_type:viem-integration-expert)

README badge

README badge for uniswap/uniswap-ai/viem-integration