<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lucas Costa</title>
    <description>The latest articles on DEV Community by Lucas Costa (@lucascosta1996).</description>
    <link>https://dev.to/lucascosta1996</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F452016%2Fbe04b611-766a-4474-8a2c-7468bfed7a89.JPG</url>
      <title>DEV Community: Lucas Costa</title>
      <link>https://dev.to/lucascosta1996</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lucascosta1996"/>
    <language>en</language>
    <item>
      <title>Introducing Web3-Hooks</title>
      <dc:creator>Lucas Costa</dc:creator>
      <pubDate>Fri, 07 Nov 2025 22:46:56 +0000</pubDate>
      <link>https://dev.to/lucascosta1996/introducing-web3-hooks-k5p</link>
      <guid>https://dev.to/lucascosta1996/introducing-web3-hooks-k5p</guid>
      <description>&lt;p&gt;For several years, I have been working on Blockchain projects, and for most of them, my library choice is React, because React is usually the best architectural decision for these projects. The most common (and more repetitive) features are those such as sending transactions, reading smart contract functionalities, and keeping UIs synchronized with on-chain data. It has been great working with available blockchain/web3 libraries — but every new dApp meant re-implementing the same patterns: request/response typing, cache keys, polling vs. subscriptions, chain switching, and error boundaries. I kept wishing there was a lightweight, adapter-agnostic library that gave me the primitives and opinions I needed, without locking me into a single provider stack.&lt;/p&gt;

&lt;p&gt;So I decided to build Web3-Hooks.&lt;/p&gt;

&lt;p&gt;The goal with Web3-Hooks is to provide a modular React hooks library for Web3 that cleanly separates concerns. You can swap transports and providers (e.g., Viem, Ethers, custom JSON-RPC), keep React-specific concerns in React, and scale your codebase without coupling every component to a specific SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why another Hooks library?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Adapter freedom. Many libraries bundle wallet connectors, transport, and React state in one box. That’s great when you want batteries included, but it can create problems for teams who need to mix &amp;amp; match RPCs (Alchemy/Infura/self-hosted), add custom middleware (retries, tracing), or target non-EVM chains later.&lt;/li&gt;
&lt;li&gt;Predictable caching. Reads in dApps are perfect for TanStack Query (React Query): cache keys, stale times, SSR awareness, and deduplication eliminate flicker and wasted requests.&lt;/li&gt;
&lt;li&gt;Typed flows end-to-end. Strong typing across JSON-RPC requests, hook signatures, and responses reduces “unknown hex” and any drift, especially with BigInt and 0x-prefixed data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Web3-Hooks ships as three composable layers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnam4gvco4wzr07hz9tnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnam4gvco4wzr07hz9tnk.png" alt="Web3-Hooks composable layers" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;em&gt;@web3-hooks/core&lt;/em&gt; — the Base Layer (Framework-agnostic)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tiny set of types, interfaces, and utilities that describe how a Web3 client performs requests. No React here.&lt;/li&gt;
&lt;li&gt;Defines a Web3Client interface (request, subscribe?, chainId?, etc.)&lt;/li&gt;
&lt;li&gt;Shapes for requests/responses and utilities for creating deterministic query keys&lt;/li&gt;
&lt;li&gt;Intentionally unopinionated about transport—it can be JSON-RPC over HTTPS, a WebSocket, or something custom&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Think of core as the protocol your app speaks to the chain, independent of React.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;2) &lt;em&gt;@web3-hooks/adapter-evm-viem&lt;/em&gt; — the EVM Adapter (Viem-powered)&lt;/p&gt;

&lt;p&gt;A small adapter that implements the core interface using Viem. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bridges core to Viem’s publicClient/walletClient&lt;/li&gt;
&lt;li&gt;Normalizes hex, numbers, and BigInts&lt;/li&gt;
&lt;li&gt;Provides EVM-specific actions (e.g., eth_chainId, eth_getBalance, log filters)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Swap this adapter out for others (E.g., Ethers, Solana, Starknet) later without touching your React code.&lt;/p&gt;

&lt;p&gt;3) &lt;em&gt;@web3-hooks/react&lt;/em&gt; — the React Layer (TanStack Query inside)&lt;/p&gt;

&lt;p&gt;The React package exposes hooks (e.g., &lt;em&gt;useBlockNumber&lt;/em&gt;, &lt;em&gt;useBalance&lt;/em&gt;, &lt;em&gt;useLogs&lt;/em&gt;) and a Web3Provider that injects your client into context, then leverages TanStack Query to cache/dedupe requests, manage refetching intervals, and provide optimistic UI patterns for writes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;React code depends only on the core contracts and query semantics. The actual RPC “how” is an adapter concern.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Design Principles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Small, sharp interfaces. Each layer does one thing well.&lt;/li&gt;
&lt;li&gt;Predictable cache keys. JSON-stable query keys ensure deduplication across components.&lt;/li&gt;
&lt;li&gt;Zero UI opinions. Bring your own design system (or none).&lt;/li&gt;
&lt;li&gt;Escape hatches. Drop to raw RPC when needed. Nothing prevents you from calling custom methods.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Start (Preset EVM)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want everything wired for an EVM dApp, use your preset bundle (or wire the three packages manually).&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add @web3-hooks/react @web3-hooks/core @web3-hooks/adapter-evm-viem @tanstack/react-query viem react react-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app root (Next.js or CRA)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;QueryClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;QueryClientProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tanstack/react-query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Web3Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@web3-hooks/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@web3-hooks/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;viemAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@web3-hooks/adapter-evm-viem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;viem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mainnet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;viem/chains&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;QueryClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;viemAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;publicClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createPublicClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mainnet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://ethereum.publicnode.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Web3Provider&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Web3Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QueryClientProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the chain data with hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useBlockNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useBalance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@web3-hooks/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;blockNumber&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useBlockNumber&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;refetchInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useBalance&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ether&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Block: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;blockNumber&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;—&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Balance: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;—&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; ETH&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Under the hood, each hook constructs stable query keys (e.g., ['blockNumber', chainId]) and leverages React Query for caching/deduplication.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How It Works (Deep Dive)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Component calls hook → &lt;code&gt;useBlockNumber({ chainId })&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Hook builds a deterministic query key and hands a queryFn to TanStack Query&lt;/li&gt;
&lt;li&gt;Query function calls &lt;code&gt;client.request({ method: 'eth_blockNumber', params: [] })&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The adapter (e.g., Viem) executes the request (HTTP/WebSocket)&lt;/li&gt;
&lt;li&gt;The core layer normalizes/validates the response (hex → BigInt/number/string as configured)&lt;/li&gt;
&lt;li&gt;React Query caches the result and dedupes concurrent calls&lt;/li&gt;
&lt;li&gt;Refetching is handled by stale times, visibility focus, or explicit refetchInterval&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlknlq61prjdt7d7arak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnlknlq61prjdt7d7arak.png" alt="UseSendTransaction workflow" width="800" height="935"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For writes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A hook (e.g., &lt;code&gt;useSendTransaction&lt;/code&gt;) calls &lt;code&gt;walletClient.sendTransaction(...)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You can optimistically update UI or invalidate queries on receipt
Errors bubble through React Query’s error states/error boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SSR &amp;amp; Next.js&lt;/strong&gt;: Because TanStack Query plays well with SSR, you can prefetch critical reads on the server (where appropriate), and hydrate on the client for instant UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Fits Among Other Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Viem — a modern, typed, EVM toolkit. web3-hooks uses it via the EVM adapter today.&lt;/li&gt;
&lt;li&gt;Ethers.js — the classic EVM library. An Ethers adapter could be added; the React layer wouldn’t change.&lt;/li&gt;
&lt;li&gt;wagmi — a feature-rich React EVM library with connectors and actions. If you want more control over transport/adapters (or you’re mixing chains), web3-hooks gives you a lean alternative.&lt;/li&gt;
&lt;li&gt;The Graph — for indexed data and historical queries. web3-hooks focuses on live RPC reads/writes; combine them when you need richer analytics/history.&lt;/li&gt;
&lt;li&gt;TypeChain — generate TypeScript typings for contract ABIs. Works great alongside web3-hooks for type-safe contract calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Additional adapters: Ethers, WS transports, non-EVM chains (Solana/Starknet)&lt;/li&gt;
&lt;li&gt;More hooks: useFeeHistory, useEnsName, useTokenList, useBundlePrice&lt;/li&gt;
&lt;li&gt;Event streams via WebSocket subscriptions with backoff/retry&lt;/li&gt;
&lt;li&gt;DevTools integ
ration (React Query + request tracing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve use cases (rollups, L2 gas oracles, MEV-resistant patterns), I’d love to hear about them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Issues/Ideas: Open a &lt;a href="https://github.com/lucascosta1996/web3-hooks" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; issue describing the use-case and API shape you expect&lt;/li&gt;
&lt;li&gt;PRs: Add tests, update docs, and keep adapters small and isolated&lt;/li&gt;
&lt;li&gt;Discussions: &lt;a href="//lucas.alves.costa96@gmail.com"&gt;Email&lt;/a&gt;/&lt;a href="https://www.linkedin.com/in/lucas-alves-costa1996/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;/PR threads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you maintain infra at Alchemy/Infura or work on chain clients, I’d love input on transport ergonomics and best-practice retries/backoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;TanStack Query (React Query): caching, dedupe, stale time, SSR hydration — &lt;a href="https://tanstack.com/query/latest" rel="noopener noreferrer"&gt;https://tanstack.com/query/latest&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Viem: typed EVM toolkit (clients, actions, transports) — &lt;a href="https://viem.sh/" rel="noopener noreferrer"&gt;https://viem.sh/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ethers.js: mature EVM library (v6 docs) — &lt;a href="https://docs.ethers.org/v6/" rel="noopener noreferrer"&gt;https://docs.ethers.org/v6/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The Graph: Subgraphs &amp;amp; Substreams for indexed on-chain data — &lt;a href="https://thegraph.com/docs/" rel="noopener noreferrer"&gt;https://thegraph.com/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;EIP-1193: Ethereum Provider API (standard) — &lt;a href="https://eips.ethereum.org/EIPS/eip-1193" rel="noopener noreferrer"&gt;https://eips.ethereum.org/EIPS/eip-1193&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JSON-RPC 2.0: transport/specification — &lt;a href="https://www.jsonrpc.org/specification" rel="noopener noreferrer"&gt;https://www.jsonrpc.org/specification&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I’m Building a Permanent Blockchain Archive - Part 2</title>
      <dc:creator>Lucas Costa</dc:creator>
      <pubDate>Fri, 18 Jul 2025 02:11:17 +0000</pubDate>
      <link>https://dev.to/lucascosta1996/how-im-building-a-permanent-blockchain-archive-part-2-1d7</link>
      <guid>https://dev.to/lucascosta1996/how-im-building-a-permanent-blockchain-archive-part-2-1d7</guid>
      <description>&lt;p&gt;&lt;strong&gt;AI Moderation and Sensitive Data Protection&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If it’s forever, it better be safe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Part 1, I introduced Immutable Notes, a decentralized archive where people can mint messages or personal notes as on-chain SVG NFTs — no IPFS, no servers, no silent failures. The entire visual output is generated in SVG and stored fully on-chain.&lt;/p&gt;

&lt;p&gt;But blockchain permanence comes with responsibility.&lt;br&gt;
If you let people write anything to the chain, you risk storing:&lt;/p&gt;

&lt;p&gt;NSFW or hateful content&lt;/p&gt;

&lt;p&gt;Personally identifiable information (PII) like emails, phone numbers, and passwords&lt;/p&gt;

&lt;p&gt;Sensitive data they might later regret putting on a permanent ledger&lt;/p&gt;

&lt;p&gt;So in Part 2, I focused on making minting safe by design, using a combination of AI moderation and custom validation logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Moderation Stack&lt;/strong&gt;&lt;br&gt;
I added a two-layer protection system:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. OpenAI Moderation API&lt;/strong&gt;&lt;br&gt;
Before minting, the user’s message is sent to the OpenAI Moderation endpoint, which checks the text for violations like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hate or harassment&lt;/li&gt;
&lt;li&gt;Sexual or violent content&lt;/li&gt;
&lt;li&gt;Self-harm&lt;/li&gt;
&lt;li&gt;Criminal activity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It returns a simple flagged: true/false response — fast, accurate, and cheap ($0.002 per 1,000 messages).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. PII Detection with Regex&lt;/strong&gt;&lt;br&gt;
OpenAI’s moderation doesn’t catch everything, so I wrote a set of custom filters to block:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Emails: &lt;a href="mailto:lucas@example.com"&gt;lucas@example.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Phone numbers: (555) 123-4567&lt;/li&gt;
&lt;li&gt;Card numbers: 4242 4242 4242 4242&lt;/li&gt;
&lt;li&gt;Passwords: password=secret123
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function containsSensitiveInfo(text: string): string | null {
  const emailRegex = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i;
  const phoneRegex = /\b(\+?\d{1,3}[-.\s]?)?\(?\d{2,4}\)?[-.\s]?\d{3,5}[-.\s]?\d{4}\b/;
  const cardRegex = /\b(?:\d[ -]*?){13,16}\b/;
  const passwordRegex = /(?:password|passwd|pass)\s*[:=]\s*\S+/i;

  if (emailRegex.test(text)) return "email address";
  if (phoneRegex.test(text)) return "phone number";
  if (cardRegex.test(text)) return "credit card number";
  if (passwordRegex.test(text)) return "password";

  return null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If any of those patterns are detected, the user gets an alert and can’t proceed to minting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Workflow&lt;/strong&gt;&lt;br&gt;
The user types their message into the NftSvgEditor.&lt;/p&gt;

&lt;p&gt;On “Submit”, the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs PII checks&lt;/li&gt;
&lt;li&gt;Sends the message to /api/moderate&lt;/li&gt;
&lt;li&gt;Receives moderation response from OpenAI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the message is flagged or contains PII, the app shows a helpful alert and blocks minting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built With&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Next.js 15.3.5&lt;/code&gt; App Router (Edge-ready)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;React 19&lt;/code&gt; with server/client components&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OpenAI Moderation API&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Regex-based&lt;/code&gt; validation for common PII patterns&lt;/li&gt;
&lt;li&gt;Custom Alert component for UX feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fas85kgn20qlr1oab6x5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fas85kgn20qlr1oab6x5c.png" alt="Custom Alert component for UX feedback" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;&lt;br&gt;
I believe blockchain permanence requires thoughtful constraints. Immutable Notes isn't just a place to write — it’s a place to preserve something that should be preserved.&lt;/p&gt;

&lt;p&gt;Letting harmful, illegal, or personal data into the system would contradict that mission, so these safeguards are essential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coming in Part 3…&lt;/strong&gt;&lt;br&gt;
Next up, I’ll show how I’m:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storing the &lt;code&gt;SHA-256 hash&lt;/code&gt; of the message for on-chain verification&lt;/li&gt;
&lt;li&gt;Using that to ensure messages aren’t altered after moderation&lt;/li&gt;
&lt;li&gt;Making the smart contract enforce this safety — not just the frontend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Want to Collaborate?&lt;/strong&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/lucascosta1996/immutable-notes" rel="noopener noreferrer"&gt;github.com/lucas-costa/immutable-notes&lt;/a&gt;&lt;br&gt;
Feedback and pull requests are welcome.&lt;/p&gt;

&lt;p&gt;If you’ve built something similar or have ideas on improving this moderation pipeline, I’d love to hear from you.&lt;/p&gt;

&lt;p&gt;Let’s build something worth remembering — and safe enough to last forever.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>blockchain</category>
      <category>react</category>
    </item>
    <item>
      <title>How I’m Building a Permanent Blockchain Archive - Part 1</title>
      <dc:creator>Lucas Costa</dc:creator>
      <pubDate>Wed, 16 Jul 2025 00:39:18 +0000</pubDate>
      <link>https://dev.to/lucascosta1996/how-im-building-a-permanent-blockchain-archive-1m3i</link>
      <guid>https://dev.to/lucascosta1996/how-im-building-a-permanent-blockchain-archive-1m3i</guid>
      <description>&lt;p&gt;Rendering Immutable SVG NFTs&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If the blockchain survives, so will these messages.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m building a dApp called &lt;em&gt;Immutable Notes&lt;/em&gt; — a decentralized archive where people can mint anonymous messages, thoughts, or notes as NFTs that will live on-chain forever.&lt;/p&gt;

&lt;p&gt;Whether it’s a memory, a thought you can’t afford to lose, or even just a timestamped idea — this project aims to preserve what matters most, permanently, without relying on platforms, servers, or external storage.&lt;/p&gt;

&lt;p&gt;To do that, I had to solve one key problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you create NFTs that don’t rely on IPFS or off-chain storage?&lt;/strong&gt;&lt;br&gt;
Most NFTs today depend on IPFS or Arweave to store the actual media or metadata. That works — until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Someone unpins the file&lt;/li&gt;
&lt;li&gt;Gateways go offline&lt;/li&gt;
&lt;li&gt;Metadata links rot silently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That breaks the very idea of “immutability.”&lt;/p&gt;

&lt;p&gt;Instead, I’m storing everything on-chain using SVG, a format that’s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lightweight&lt;/li&gt;
&lt;li&gt;Browser-native&lt;/li&gt;
&lt;li&gt;Human-readable&lt;/li&gt;
&lt;li&gt;Fully expressible in plain text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SVG can be stored directly in the smart contract and displayed without depending on any external service.&lt;/p&gt;

&lt;p&gt;If Ethereum lives, the message lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution: SVG&lt;/strong&gt;&lt;br&gt;
SVG (Scalable Vector Graphics) is ideal for this use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s just text (XML) — so it fits naturally in smart contracts&lt;/li&gt;
&lt;li&gt;It’s visual — so each note becomes a piece of generative art&lt;/li&gt;
&lt;li&gt;It supports styling — colors, fonts, layout&lt;/li&gt;
&lt;li&gt;It renders everywhere — no plugins, no IPFS dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each Immutable Note is minted as a fully self-contained SVG NFT: the layout, colors, timestamp, and user message are all stored directly inside the blockchain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: A component for editing the SVG note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3bg4r9yd2glbd54z5wp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3bg4r9yd2glbd54z5wp.png" alt="Component Image" width="800" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;code&gt;base64-encoded&lt;/code&gt; and embedded into the smart contract’s &lt;code&gt;tokenURI()&lt;/code&gt; return — making the NFT truly on-chain and self-reliant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works: The SVG Editor Component&lt;/strong&gt;&lt;br&gt;
To let users create and preview their NFTs before minting, I built a React component called NftSvgEditor. It handles everything from text layout to real-time rendering.&lt;/p&gt;

&lt;p&gt;Here’s what it does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1&lt;/strong&gt; - &lt;strong&gt;&lt;em&gt;Text Wrapping with Pixel Precision&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
SVG doesn’t support native word wrapping, so I wrote a custom function called &lt;code&gt;measureSegments()&lt;/code&gt; that uses a hidden canvas to split text into properly wrapped lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const measure = (str: string) =&amp;gt; context.measureText(str).width;

const splitByWidth = (line: string) =&amp;gt; {
  // Binary search to find the longest substring that fits
  while (start &amp;lt; line.length) {
      let low = start;
      let high = line.length;

      while (low &amp;lt; high) {
        const mid = Math.floor((low + high + 1) / 2);
        const slice = line.slice(start, mid);
        if (measure(slice) &amp;lt;= maxWidth) {
          low = mid;
        } else {
          high = mid - 1;
        }
      }

      const segment = line.slice(start, low);
      result.push(segment || '\u00A0');
      start = low;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the text never overflows the boundaries of the SVG.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2&lt;/strong&gt; - &lt;em&gt;&lt;strong&gt;Real-Time SVG Rendering&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
The component overlays a transparent &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; over the SVG so users can type naturally, while the text is rendered as  elements below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;text x="20" y={60} fontSize="18px"&amp;gt;
  {segments.map((segment, i) =&amp;gt; (
    &amp;lt;tspan key={i} x="20" dy={i === 0 ? '0' : '1.2em'}&amp;gt;
      {segment}
    &amp;lt;/tspan&amp;gt;
  ))}
&amp;lt;/text&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A “To:” field for an optional recipient label&lt;/li&gt;
&lt;li&gt;A timestamp that updates in real time&lt;/li&gt;
&lt;li&gt;A background color chosen by the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this lives inside a fixed-size SVG container (320x360px) designed for on-chain rendering consistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3&lt;/strong&gt; - &lt;em&gt;&lt;strong&gt;Live Preview = Trust&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Users can see exactly what they’re minting before they hit the blockchain. No surprises, no broken layouts. What you see is what you mint — forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters&lt;/strong&gt;&lt;br&gt;
We talk a lot about “immutability” in blockchain — but most NFTs aren’t truly immutable. They rely on links to files stored off-chain.&lt;/p&gt;

&lt;p&gt;By contrast, Immutable Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores the entire NFT in the contract&lt;/li&gt;
&lt;li&gt;Has no reliance on IPFS or Arweave&lt;/li&gt;
&lt;li&gt;Can’t be changed, erased, or hidden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is true permanence — the kind worth using when the message matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open Source &amp;amp; In Progress&lt;/strong&gt;&lt;br&gt;
You can view the source code (including the NftSvgEditor component) here:&lt;/p&gt;

&lt;p&gt;🔗 GitHub: &lt;a href="https://github.com/lucascosta1996/immutable-notes" rel="noopener noreferrer"&gt;github.com/lucas-costa/immutable-notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is still in early development — I’d love feedback, ideas, and even pull requests from other devs interested in on-chain data, SVG rendering, or Web3 UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s Next&lt;/strong&gt; (Part 2 Preview)&lt;br&gt;
In Part 2, I’ll walk through how I’m using OpenAI’s Moderation API to prevent harmful or NSFW content from ever making it on-chain — and how I enforce that on-chain using hash validation to prevent bypassing the moderation step.&lt;/p&gt;

&lt;p&gt;Stay tuned — and follow the journey as I continue building Immutable Notes in public.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to Talk?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What would you optimize in the SVG rendering or layout system?&lt;/li&gt;
&lt;li&gt;Have you worked on fully on-chain NFTs? What lessons did you learn?&lt;/li&gt;
&lt;li&gt;Do you think text-based NFTs hav
e a future in digital memory?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s chat in the comments — or connect with me on &lt;a href="https://www.linkedin.com/in/lucas-alves-costa1996" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>blockchain</category>
      <category>react</category>
      <category>solidity</category>
    </item>
  </channel>
</rss>
