Skip to Content
Welcome to RitoSwap's documentation!
dAppSmart Contract DataOverview

Smart Contract Data System

The smart contract data system forms the blockchain interaction layer of RitoSwap, managing real-time synchronization between on-chain NFT state and the user interface. It combines contract configuration, a read-side data pipeline (useNFTData + the NFT store), and a write-side transaction orchestrator (useMintBurn) to deliver responsive, accurate token data across the application.

System Overview

RitoSwap’s architecture requires seamless integration between smart contracts deployed across multiple networks and the React-based user interface. The smart contract data system accomplishes this through a carefully orchestrated stack of technologies that work together to provide real-time updates, efficient request handling, and consistent state management.

Core Architecture Components

The system consists of four primary modules that work in harmony:

  1. Contract Configuration – Centralizes addresses and ABIs for all supported networks and exposes KEY_TOKEN_ADDRESS / fullKeyTokenAbi. See Contract Configuration.
  2. NFT Store – A Zustand store that holds derived NFT UI state, account-switching flags, and selective persistence (token-gate usage). See NFT Store.
  3. useNFTData (read-side data hook) – Orchestrates wagmi reads, Token Status API calls via TanStack Query, and writes the resulting snapshot into the NFT store. See useNFTData Hook.
  4. useMintBurn (write-side transaction hook) – Coordinates mint/burn transactions, network checks, WalletConnect deeplinking, notifications, and success callbacks. See useMintBurn Hook.

At a high level, useNFTData and the NFT store form the read-side data pipeline, while useMintBurn provides the write-side transaction pipeline that feeds back into the read side via success callbacks and forceRefresh.

Documentation Map

    • index.mdx
    • contract-config.mdx
    • nft-store.mdx
    • nft-data-hook.mdx
    • use-mint-burn.mdx

Data Flow Architecture

Understanding how data flows through the system is crucial for effective development:

Step 1: Contract address resolution

The system determines which contract address to use based on environment configuration, selecting between local RitoNet, Sepolia testnet, or Ethereum mainnet via the contract configuration module.

Step 2: Blockchain queries

On the read side, useNFTData uses wagmi hooks to query the active smart contract for token ownership and metadata. On the write side, useMintBurn uses wagmi write and receipt hooks to send mint/burn transactions and track confirmations.

Step 3: Data synchronization

TanStack Query manages token-usage API requests (Token Status API), providing request deduplication and controlled freshness. Caching is deliberately minimal for token-usage data so that account switches and recent mints/burns always see up-to-date usage.

Step 4: State updates

The NFT store receives updates from the data layer (via useNFTData) and propagates changes to subscribed React components. Successful writes from useMintBurn typically call back into useNFTData.forceRefresh to re-sync on-chain and usage data.

Step 5: UI rendering

Components reflect the current state, showing token ownership, colors, and usage status in real time. Write-focused components (like ButtonSection) consume useMintBurn and the store to render mint/burn flows with accurate progress and status.

Integration with Broader Ecosystem

The smart contract data system doesn’t operate in isolation. It forms critical connections with other parts of the RitoSwap architecture.

Smart Contract Deployment

Contract addresses are automatically managed through the monorepo’s deployment system. When contracts are deployed via the colored-keys workspace, addresses are saved to JSON files stored in the monorepo root (one folder level above dapp or colored-keys) that the dApp references:

    • local_blockchain.json
    • hardhat.json
    • mainnet.json
    • sepolia.json

This automation ensures contract addresses remain synchronized between deployment and application configuration.

API Integration

The smart contract data system works closely with RitoSwap’s API endpoints to maintain consistency between on-chain and off-chain state:

  • Token Status API – Synchronizes on-chain existence with database records.
  • Gate Access API – Verifies ownership before granting access to gated content.
  • Verify Token Gate API – Updates usage records after successful verification.

These endpoints reuse the same contract configuration and query patterns as the dApp, ensuring data consistency across the stack.

Database Synchronization

While smart contracts maintain the authoritative state for token ownership, the Prisma database layer tracks additional metadata like usage status. The smart contract data system bridges these two sources of truth, ensuring they remain synchronized through a combination of automatic effects (useNFTData) and explicit refresh flows (forceRefresh after transactions).

Key Technologies

Wagmi

Wagmi provides type-safe React hooks for Ethereum interactions. The system uses several key wagmi hooks:

HookPurposeUsage in RitoSwap
useAccountManages wallet connection stateDetermines if the user is connected and provides their address to both read and write hooks
useReadContractReads data from smart contractsUsed by useNFTData to fetch token ownership and color metadata
useWriteContractSends transactions to contractsWrapped inside useMintBurn to handle minting and burning operations with network checks and WalletConnect deeplinking
useWaitForTransactionReceiptMonitors transaction confirmationsUsed by useMintBurn to drive confirmation state, success flags, and notifications

TanStack Query

TanStack Query enhances the blockchain querying experience through intelligent request management and deduplication. In RitoSwap, it specifically manages token-usage status queries for the Token Status API, preventing redundant calls when multiple components request the same data. Caching is tuned for freshness over staleness (e.g., staleTime: 0, gcTime: 0) so that recent mints, burns, and account switches are always reflected promptly.

Zustand

Zustand provides lightweight state management that bridges the gap between blockchain queries and React components. The NFT store maintains current token state, account-switching flags, loading/error helpers, and selectively persisted token-gate usage. Components subscribe to the specific slices they need, while useNFTData and transaction flows (useMintBurn success callbacks) coordinate updates.

Component Architecture

The smart contract data system supports two primary interaction patterns.

Read operations

Components like NFTScreen display token state without modifying it. These components subscribe to the NFT store and react to state changes, showing appropriate UI based on token ownership, colors, and usage status. The useNFTData hook typically runs at the route/container level (e.g., MintPageWrapper), keeping the store synchronized in the background.

Write operations

Write-oriented components, such as ButtonSection, enable users to mint and burn tokens. Rather than interacting with wagmi directly, they consume the useMintBurn hook:

  • useMintBurn handles transaction initiation, network validation, WalletConnect deeplinking, confirmation tracking, success/error notifications, and reset helpers.
  • Components layer UX concerns on top: button labels, disabled/aria states, modal visibility, and when to call onRefresh (typically wired to useNFTData.forceRefresh).

This separation keeps transaction logic centralized and testable, while allowing multiple UIs to share the same mint/burn behavior.

Performance Considerations

The system implements several strategies to ensure optimal performance:

  • Intelligent pollinguseNFTData adjusts polling frequency based on account-switching state and the disablePolling flag (e.g., faster during switches, optional disablePolling for static views).
  • Request deduplication – TanStack Query automatically deduplicates simultaneous token-status requests for the same key, reducing unnecessary API calls.
  • Selective re-rendering – Components subscribe only to specific store slices, preventing unnecessary re-renders when unrelated state changes.
  • Atomic updates – Store helpers like setTokenData update several fields together, preventing intermediate states that could cause UI flicker.

Development Workflow

When working with the smart contract data system, follow these patterns for consistency.

Reading contract data

// Run the read-side pipeline at the route or container level const { forceRefresh, isLoading } = useNFTData(); // Consume derived state where needed using selectors const hasNFT = useNFTStore((state) => state.hasNFT); const { tokenId, backgroundColor, keyColor } = useNFTStore((state) => ({ tokenId: state.tokenId, backgroundColor: state.backgroundColor, keyColor: state.keyColor, }));

Writing contract data

For user-facing flows, prefer the useMintBurn hook rather than calling wagmi write hooks directly:

import { useMintBurn } from '@/app/hooks/useMintBurn'; import { useNFTData } from '@/app/hooks/useNFTData'; function MintPageWrapper() { const { forceRefresh } = useNFTData(); return ( <> {/* Status + visualization components that read from the NFT store */} <ButtonSection onRefresh={forceRefresh} /> </> ); } function ButtonSection({ onRefresh }: { onRefresh?: () => Promise<void> | void }) { const { mint, burn, isProcessing } = useMintBurn({ onMintSuccess: async () => { await onRefresh?.(); }, onBurnSuccess: async () => { await onRefresh?.(); }, }); return ( <> <button onClick={mint} disabled={isProcessing} aria-busy={isProcessing}> {isProcessing ? 'Processing…' : 'Mint NFT'} </button> {/* Burn button would call burn(tokenId) when appropriate */} </> ); }

Lower-level libraries (such as internal utilities or tests) can still use useWriteContract directly, but route-level and UI components should treat useMintBurn as the canonical write interface for Colored Key NFTs.

Handling state updates

Whether writes come from useMintBurn or other flows, the goal is to funnel post-transaction state changes through useNFTData:

// In most cases, success callbacks passed to useMintBurn // are responsible for triggering a refresh: const { forceRefresh } = useNFTData(); const { mint } = useMintBurn({ onMintSuccess: async () => { await forceRefresh(); // Re-sync ownership, colors, and usage }, });

This keeps the data pipeline centralized and avoids scattered, ad-hoc store updates after transactions.

Error Handling and Recovery

The system implements robust error handling at multiple levels:

  • Contract errors – Wagmi errors are formatted by helpers like formatMintError/formatBurnError and surfaced through the centralized notifications layer (see Notifications). Components typically do not raise their own toasts for mint/burn flows, avoiding duplicate messaging.
  • Network mismatches – The useNetworkCheck hook prevents transactions on incorrect networks and surfaces guidance to switch chains. useMintBurn wraps its writes in this helper.
  • Account switching – The NFT store’s isSwitchingAccount and previousData fields, together with useNFTData’s cache-clearing and accelerated polling, provide a controlled transition when wallets change. The default UX is a clean reset followed by fresh data, while more advanced components can opt into using previousData for richer transitions.
  • Fallback rendering – Components implement loading and error states to maintain usability even when blockchain queries or APIs are temporarily unavailable.

Testing Considerations

Because the system coordinates wagmi, TanStack Query, and Zustand, effective tests rely on mocks and controlled environments rather than live blockchain state.

Key testing strategies include:

  • Mocking wagmi providers to simulate various ownership, color, and transaction states.
  • Using TanStack Query’s test utilities (or explicit queryClient setup) to control token-status cache behavior.
  • Testing NFT store transitions (including account-switch flows) independently of the hooks.
  • Exercising useMintBurn in isolation with mocked network checks, WalletConnect store, and notifications to validate mint/burn lifecycles, deduplication, and error handling (see useMintBurn.test.tsx).

Summary

The smart contract data system represents a sophisticated integration of modern Web3 technologies, delivering real-time blockchain data to React components through carefully orchestrated layers of configuration, data fetching, transaction orchestration, and state management. Contract configuration, useNFTData, the NFT store, and useMintBurn work together to bridge the gap between smart contracts and user interfaces.

By centralizing read logic, write logic, and state synchronization, the system prioritizes developer experience (type safety, predictable updates, clear success/error pathways) while maintaining the performance and reliability required for production blockchain applications.