npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@neynar/smart-contract-ts-generator

v0.7.1

Published

Generate TypeScript contract wrappers from contract addresses on any EVM chain

Readme

Contract TypeScript Generator

Automatically generate TypeScript wrappers for smart contracts from their addresses on various blockchain networks.

Features

  • Fetches contract ABIs from Etherscan (and variants) or Sourcify
  • Generates type-safe TypeScript code using abitype
  • Creates React hooks using wagmi for contract interaction
  • Optional class-based client for server-side or complex use cases
  • Supports multiple chains (Base, Optimism, Ethereum, and testnets)

Installation

NPM

npm install -g @neynar/smart-contract-ts-generator

Yarn

yarn global add @neynar/smart-contract-ts-generator

NPX (No Installation)

npx @neynar/smart-contract-ts-generator --address <CONTRACT_ADDRESS> --chain <CHAIN_NAME> --name <CONTRACT_NAME>

Local Development (Without Publishing)

For testing locally without publishing to npm:

# Clone and set up
git clone https://github.com/neynarxyz/smart-contract-ts-generator.git
cd smart-contract-ts-generator
yarn install
yarn build

# Option 1: Link globally (recommended)
yarn link
# Now you can use: smart-contract-gen ...

# Option 2: Use yarn from within the repo
yarn generate --address 0x... --chain base --name USDC

# Option 3: Use npx from within the repo
npx . --address 0x... --chain base --name USDC

Usage

Basic Usage

smart-contract-gen \
  --address <CONTRACT_ADDRESS> \
  --chain <CHAIN_NAME> \
  --name <CONTRACT_NAME>

Or with npx:

npx @neynar/smart-contract-ts-generator \
  --address <CONTRACT_ADDRESS> \
  --chain <CHAIN_NAME> \
  --name <CONTRACT_NAME>

Examples

Basic Usage - USDC on Base

smart-contract-gen \
  --address 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
  --chain base \
  --name USDC

Output: ./base/usdc/

Custom Root Directory

smart-contract-gen \
  --address 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
  --chain base \
  --name USDC \
  --output ./contracts

Output: ./contracts/base/usdc/

CLI Options

Required Options

  • -a, --address <address> - Contract address (0x-prefixed hex)
  • -c, --chain <chain> - Chain name (see supported chains below)
  • -n, --name <name> - Contract name for generated files (e.g., USDC, MyToken)

Optional Options

  • -o, --output <root> - Output root directory (default: current directory). Files are generated at <root>/<chain>/<name>
  • -k, --api-key <key> - Etherscan API key (or set ETHERSCAN_API_KEY env var)
  • --no-class - Skip generating class-based client (only generate hooks)
  • -h, --help - Show help message

Supported Chains

| Chain | Chain ID | Explorer API | | ---------------- | -------- | ----------------------------------- | | base | 8453 | api.basescan.org | | base-sepolia | 84532 | api-sepolia.basescan.org | | optimism | 10 | api-optimistic.etherscan.io | | optimism-sepolia | 11155420 | api-sepolia-optimistic.etherscan.io | | ethereum | 1 | api.etherscan.io | | sepolia | 11155111 | api-sepolia.etherscan.io |

Using Etherscan API Key

For better rate limits and reliability, provide an Etherscan API key:

# Via environment variable
export ETHERSCAN_API_KEY=your_api_key
smart-contract-gen ...

# Or via CLI argument
smart-contract-gen \
  --api-key your_api_key \
  --address ... \
  --chain ... \
  --name ...

Get a free API key from:

  • Base: https://basescan.org/apis
  • Optimism: https://optimistic.etherscan.io/apis
  • Ethereum: https://etherscan.io/apis

Generated Files

The generator creates a directory with the following structure:

<output-directory>/
├── abi.ts         # Raw ABI with const assertion
├── hooks.ts       # React hooks for wagmi
├── client.ts      # Class-based client (optional)
└── index.ts       # Public exports

Example Generated Code

React Hooks (hooks.ts)

import { useReadContract, useWriteContract } from 'wagmi';
import type { Address } from 'viem';
import { CONTRACT_ABI } from './abi.ts';

// Read function hook
export function useUsdcBalanceOf(
  contractAddress: Address,
  account: Address,
  options?: { enabled?: boolean; watch?: boolean }
) {
  return useReadContract({
    address: contractAddress,
    abi: CONTRACT_ABI,
    functionName: 'balanceOf',
    args: [account],
    query: options,
  });
}

// Write function hook
export function useUsdcTransfer(contractAddress: Address) {
  const { writeContract, ...rest } = useWriteContract();

  const write = ({ to, amount }: { to: Address; amount: bigint }) => {
    return writeContract({
      address: contractAddress,
      abi: CONTRACT_ABI,
      functionName: 'transfer',
      args: [to, amount],
    });
  };

  return { ...rest, write, writeAsync: write };
}

Class-Based Client (client.ts)

import type { PublicClient, WalletClient, Address, Hex } from 'viem';
import { CONTRACT_ABI } from './abi.ts';

export class UsdcContract {
  private publicClient: PublicClient;
  private walletClient?: WalletClient;
  private address: Address;

  constructor(address: Address, publicClient: PublicClient, walletClient?: WalletClient) {
    this.address = address;
    this.publicClient = publicClient;
    this.walletClient = walletClient;
  }

  // Read method
  async balanceOf(account: Address): Promise<bigint> {
    return await this.publicClient.readContract({
      address: this.address,
      abi: CONTRACT_ABI,
      functionName: 'balanceOf',
      args: [account],
    });
  }

  // Write method
  async transfer(to: Address, amount: bigint): Promise<Hex> {
    if (!this.walletClient) {
      throw new Error('Wallet client required for write operations');
    }

    const { request } = await this.publicClient.simulateContract({
      address: this.address,
      abi: CONTRACT_ABI,
      functionName: 'transfer',
      args: [to, amount],
      account: this.walletClient.account,
    });

    return await this.walletClient.writeContract(request);
  }
}

Using Generated Code

In React Components (Hooks)

import { useUsdcBalanceOf, useUsdcTransfer } from './contracts/usdc';

function MyComponent() {
  const contractAddress = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
  const userAddress = '0x...';

  // Read balance
  const { data: balance, isLoading } = useUsdcBalanceOf(contractAddress, userAddress);

  // Write transfer
  const { write: transfer, isPending } = useUsdcTransfer(contractAddress);

  const handleTransfer = () => {
    transfer({
      to: '0xRecipient...',
      amount: 1000000n, // 1 USDC (6 decimals)
    });
  };

  return (
    <div>
      <p>Balance: {balance?.toString()}</p>
      <button onClick={handleTransfer} disabled={isPending}>
        Transfer
      </button>
    </div>
  );
}

In Server-Side Code (Class)

import { createPublicClient, createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { UsdcContract } from './contracts/usdc';

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

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

const usdc = new UsdcContract(
  '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
  publicClient,
  walletClient
);

// Read balance
const balance = await usdc.balanceOf('0x...');

// Transfer tokens
const txHash = await usdc.transfer('0xRecipient...', 1000000n);

How It Works

  1. Fetch ABI: Retrieves the contract ABI from Etherscan API (primary) or Sourcify (fallback)
  2. Parse ABI: Validates and categorizes functions (read vs write), events, and constructor
  3. Generate Code: Creates TypeScript files with:
    • ABI constant with type assertion
    • React hooks for each function
    • Optional class-based client
    • Type-safe function arguments and return types
  4. Write Files: Outputs formatted TypeScript files to the specified directory

Type Safety

All generated code is fully type-safe using:

  • abitype: Provides strict TypeScript types derived from the ABI
  • viem: Ensures type-safe contract interactions
  • wagmi: Type-safe React hooks for Ethereum

The generator automatically converts Solidity types to TypeScript:

  • uint256bigint
  • addressAddress (from viem)
  • bytesHex (from viem)
  • boolboolean
  • stringstring
  • Arrays → readonly T[]

Troubleshooting

Contract Not Found

Make sure the contract is verified on Etherscan or Sourcify. Unverified contracts cannot have their ABIs fetched.

Rate Limiting

If you encounter rate limiting errors:

  1. Get a free Etherscan API key (see links above)
  2. Use the --api-key option or ETHERSCAN_API_KEY environment variable
  3. The tool automatically falls back to Sourcify if Etherscan fails

Invalid ABI

If the generated code has issues, check that:

  1. The contract address is correct
  2. The contract is on the specified chain
  3. The contract is verified on the block explorer

Contributing

To add support for new chains:

  1. Add the chain configuration to CHAIN_CONFIGS in fetch-abi.ts
  2. Include the chain ID, name, Etherscan API URL, and Sourcify support flag

License

Part of the Neynar monorepo.