Claude-code-plugins-plus-skills quicknode-sdk-patterns

install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/quicknode-pack/skills/quicknode-sdk-patterns" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-quicknode-sdk-patterns && rm -rf "$T"
manifest: plugins/saas-packs/quicknode-pack/skills/quicknode-sdk-patterns/SKILL.md
source content

QuickNode SDK Patterns

Overview

Production-ready patterns for blockchain development with QuickNode: provider singletons, retry logic, batch RPC calls, and multi-chain support.

Prerequisites

  • Completed
    quicknode-install-auth
  • ethers.js or @quicknode/sdk installed

Instructions

Step 1: Provider Singleton

import { ethers } from 'ethers';

let _provider: ethers.JsonRpcProvider | null = null;

export function getProvider(): ethers.JsonRpcProvider {
  if (!_provider) {
    _provider = new ethers.JsonRpcProvider(process.env.QUICKNODE_ENDPOINT, undefined, {
      staticNetwork: true,  // Skip chainId lookup on every call
      batchMaxCount: 10,    // Enable batch RPC
    });
  }
  return _provider;
}

Step 2: Retry Wrapper with Backoff

async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err: any) {
      const isRetryable = err.code === 'SERVER_ERROR' || err.code === 'TIMEOUT' || err.status === 429;
      if (!isRetryable || attempt === maxRetries) throw err;
      const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error('Unreachable');
}

// Usage
const balance = await withRetry(() => getProvider().getBalance(address));

Step 3: Multi-Chain Client Factory

const ENDPOINTS: Record<string, string> = {
  ethereum: process.env.QUICKNODE_ETH_ENDPOINT!,
  polygon: process.env.QUICKNODE_POLYGON_ENDPOINT!,
  arbitrum: process.env.QUICKNODE_ARB_ENDPOINT!,
};

const providers = new Map<string, ethers.JsonRpcProvider>();

export function getChainProvider(chain: string): ethers.JsonRpcProvider {
  if (!providers.has(chain)) {
    const url = ENDPOINTS[chain];
    if (!url) throw new Error(`No endpoint for chain: ${chain}`);
    providers.set(chain, new ethers.JsonRpcProvider(url, undefined, { staticNetwork: true }));
  }
  return providers.get(chain)!;
}

Step 4: Batch RPC Calls

async function batchGetBalances(addresses: string[]): Promise<Map<string, bigint>> {
  const provider = getProvider();
  const results = new Map<string, bigint>();

  // ethers.js batches these automatically when batchMaxCount > 1
  const promises = addresses.map(async (addr) => {
    const balance = await provider.getBalance(addr);
    results.set(addr, balance);
  });

  await Promise.all(promises);
  return results;
}

Step 5: Contract Wrapper with Caching

import { LRUCache } from 'lru-cache';

const contractCache = new LRUCache<string, any>({ max: 100, ttl: 60000 });

async function cachedContractCall(contract: ethers.Contract, method: string, ...args: any[]) {
  const key = `${contract.target}:${method}:${JSON.stringify(args)}`;
  const cached = contractCache.get(key);
  if (cached) return cached;

  const result = await contract[method](...args);
  contractCache.set(key, result);
  return result;
}

Output

  • Thread-safe provider singleton with batch support
  • Retry logic for transient RPC failures
  • Multi-chain client factory
  • Cached contract calls reducing RPC usage

Error Handling

PatternUse CaseBenefit
SingletonAll RPC callsOne connection, reused
Retry wrapperTransient failuresAutomatic recovery
Multi-chain factoryCross-chain dAppsClean chain switching
Contract cacheRepeated readsFewer RPC calls

Resources

Next Steps

Build transaction workflows:

quicknode-core-workflow-a