@ponder/utils – Ponder
Skip to content

@ponder/utils

API reference

The @ponder/utils package provides utility functions for common tasks in Ponder apps.

mergeAbis

Combines many ABIs into one. Removes duplicate items if necessary.

Usage

index.ts
import { mergeAbis } from "@ponder/utils";
import { erc20Abi, erc4626Abi } from "viem";
 
const tokenAbi = mergeAbis([erc20Abi, erc4626Abi]);

Parameters

ParameterTypeDescription
ABIsAbi[]A list of ABIs to merge.

Returns

A new ABI including all items from the input list, with duplicates removed.

Usage in Ponder

Use mergeAbis to include multiple ABIs for a single contract. This is especially useful for proxy contracts that have had several different implementation ABIs.

For convenience, ponder re-exports mergeAbis from @ponder/utils.

ponder.config.ts
import { createConfig, mergeAbis } from "ponder"; 
 
import { ERC1967ProxyAbi } from "./abis/ERC1967Proxy";  
import { NameRegistryAbi } from "./abis/NameRegistry";  
import { NameRegistry2Abi } from "./abis/NameRegistry2";  
 
export default createConfig({
  contracts: {
    FarcasterNameRegistry: {
      abi: mergeAbis([ERC1967ProxyAbi, NameRegistryAbi, NameRegistry2Abi]),  
      // ...
    },
  },
  // ...
});

replaceBigInts

Replaces all BigInt values in an object (deep traversal) with a new value, specified by a replacer function.

Usage

This example simply converts BigInt values to a string.

index.ts
import { replaceBigInts } from "@ponder/utils";
 
const obj = { a: 100n, b: [-12n, 3_000_000_000n] };
 
const result = replaceBigInts(obj, (v) => String(v));
//    ?^ { a: '100', b: [ '-12', '3000000000' ] }

Parameters

ParameterTypeDescription
valueanyThe scalar, array, or object containing BigInt values to be replaced.
replacer(value: bigint) => JSONSerializableA custom replacer function that will be called for each BigInt value.

Returns

The scalar, array, or object with all BigInt values replaced.

Replacer functions

Here are three common ways to replace BigInt values.

EncodingReplacer typeReplacer function
Hex0x${string}numberToHex
StringstringString
Lossless string#bigint.${string}(x) => #bigint.${String(x)}

See the Wagmi FAQ for more information on BigInt serialization.

Usage in Ponder: json columns

The json column type does not support BigInt values. Use replaceBigInts to prepare objects containing BigInt values for insertion.

src/index.ts
import { ponder } from "ponder:registry";
import { userOperations } from "ponder:schema";
import { replaceBigInts } from "@ponder/utils";
import { toHex } from "viem";
 
ponder.on("EntryPoint:UserOp", async ({ event, context }) => {
  await context.db.insert(userOperations).values({
    id: event.log.id,
    receipt: replaceBigInts(event.transactionReceipt, toHex),
  });
});

To maintain type safety for column values, use the ReplaceBigInts helper type in the column $type annotation.

ponder.schema.ts
import { onchainTable } from "ponder";
import type { ReplaceBigInts } from "@ponder/utils";
import type { TransactionReceipt, Hex } from "viem";
 
export const userOperations = onchainTable("user_operations", (t) => ({
  id: t.text().primaryKey(),
  receipt: t.json<ReplaceBigInts<TransactionReceipt, Hex>>(),
}));

Usage in Ponder: API endpoints

The GraphQL API automatically serializes BigInt values to strings before returning them in HTTP responses. In custom API endpoints, you need to handle this serialization process manually.

src/api/index.ts
import { ponder } from "ponder:registry";
import { accounts } from "ponder:schema";
import { replaceBigInts } from "@ponder/utils"; 
import { numberToHex } from "viem";
 
ponder.get("/whale-balances", async (c) => {
  const rows = await c.db
    .select({
      address: accounts.address,
      ethBalance: accounts.ethBalance,
      dogeBalance: accounts.dogeBalance,
    })
    .from(accounts)
    .where(eq(accounts.address, address));
 
  const result = replaceBigInts(rows, (v) => numberToHex(v)); 
 
  return c.json(result);
});

Transports

The @ponder/utils package exports two new Viem transports, loadBalance and rateLimit. These transports are useful for managing the RPC request workloads that Ponder apps commonly encounter.

loadBalance

The loadBalance Transport distributes requests across a list of inner Transports in a simple round-robin scheme.

Usage

import { loadBalance } from "@ponder/utils"; 
import { createPublicClient, fallback, http, webSocket } from "viem";
import { mainnet } from "viem/chains";
 
const transport = loadBalance([ 
  http("https://cloudflare-eth.com"), 
  webSocket("wss://ethereum-rpc.publicnode.com"), 
  rateLimit(http("https://rpc.ankr.com/eth"), { requestsPerSecond: 5 }), 
]), 
 
const client = createPublicClient({ chain: mainnet, transport });

Parameters

ParameterTypeDescription
TransportsTransport[]A list of Transports to load balance requests across.

Usage in Ponder

For convenience, ponder re-exports loadBalance from @ponder/utils.

ponder.config.ts
import { createConfig, loadBalance } from "ponder";
import { http, webSocket, rateLimit } from "viem";
 
export default createConfig({
  chains: {
    mainnet: {
      id: 1,
      transport: loadBalance([ 
        http("https://cloudflare-eth.com"), 
        http("https://eth-mainnet.public.blastapi.io"), 
        webSocket("wss://ethereum-rpc.publicnode.com"), 
        rateLimit(http("https://rpc.ankr.com/eth"), { requestsPerSecond: 5 }), 
      ]), 
    },
  },
  // ...
});

rateLimit

The rateLimit Transport limits the number of requests per second submitted to an inner Transport using a first-in-first-out queue.

Usage

import { rateLimit } from "@ponder/utils";
import { createPublicClient, fallback, http } from "viem";
import { mainnet } from "viem/chains";
 
const client = createPublicClient({
  chain: mainnet,
  transport: rateLimit(http("https://eth-mainnet.g.alchemy.com/v2/..."), {
    requestsPerSecond: 25,
  }),
});

Parameters

ParameterTypeDescription
TransportTransportAn inner transport to rate limit.
requestsPerSecondnumberThe maximum number of requests per second to allow.
browserboolean (default: true)If false, the internal queue will use the Node.js-specific process.nextTick() API to schedule requests. This leads to more predictable behavior in Node.js, but is not available in the browser.

Usage in Ponder

For convenience, ponder re-exports rateLimit from @ponder/utils.

ponder.config.ts
import { createConfig, rateLimit } from "ponder";
import { http } from "viem";
 
export default createConfig({
  chains: {
    mainnet: {
      id: 1,
      transport: rateLimit(http(process.env.PONDER_RPC_URL_1), {
        requestsPerSecond: 25,
      }),
    },
  },
  contracts: {
    // ...
  },
});