Skip to Content
Welcome to RitoSwap's documentation!

ButtonSection

The ButtonSection component serves as the primary action interface for all NFT-related operations in RitoSwap. This intelligent component adapts its presentation based on wallet connection status, NFT ownership, and token gate usage, providing users with contextually appropriate actions at each stage of their journey. Through sophisticated state management and transaction orchestration, it transforms complex blockchain operations into simple button clicks while maintaining comprehensive error handling and user feedback.

Component Architecture

ButtonSection implements a reactive architecture that continuously adapts to changing blockchain state while delegating transaction orchestration to the useMintBurn hook. useMintBurn coordinates wagmi-based contract writes, network checking, deeplinking, and notifications; ButtonSection focuses on rendering the correct actions for each state, managing UI transitions and loading text, wiring ProcessingModal, and invoking refresh callbacks when transactions complete or are reset.

State-Based UI Adaptation

The component renders different interfaces based on four primary states:

StateConditionsUI Elements
Not Connected!isConnectedConnectWrapper (wallet connection button)
No NFTisConnected && !hasNFTMint NFT button
Unused NFTisConnected && hasNFT && !hasUsedTokenGateToken Gate link + Burn button
Used NFTisConnected && hasNFT && hasUsedTokenGateBurn button only

This adaptive approach ensures users only see relevant actions, reducing cognitive load and preventing invalid operations.

Interactive Demo

Use the dropdown to change states

Props and Interface

ButtonSection exposes an optional refresh callback used to re-query NFT data after successful transactions:

type ButtonSectionProps = { onRefresh?: () => Promise<void> | void } export default function ButtonSection({ onRefresh }: ButtonSectionProps) { // UI logic only; blockchain orchestration delegated to hooks }

In the app, MintPageWrapper provides onRefresh by passing useNFTData().forceRefresh.

Using useMintBurn

ButtonSection delegates blockchain concerns to the useMintBurn hook, which handles:

  • Network validation with the current chain
  • Mobile wallet deeplinking on write actions
  • Transaction receipt tracking and success deduplication
  • Error formatting and notification dispatch

Minimal consumption pattern in the UI:

const { mint, burn, isProcessing, mintHash, burnHash } = useMintBurn({ onMintSuccess: async () => { // Prevent stale processing text, then refresh UI data setBlockProcessingText(true) await onRefresh?.() setLoading(false) }, onBurnSuccess: async () => { setBlockProcessingText(true) await onRefresh?.() setLoading(false) }, }) // Event handlers const handleMint = () => { setBlockProcessingText(false); setLoading(true); mint() } const handleBurn = () => { setBlockProcessingText(false); setLoading(true); burn(tokenId) }

Network Validation

Network checks run inside useMintBurn, preventing wrong-chain writes without the UI needing to wrap actions. This keeps ButtonSection focused on rendering and state transitions.

Mobile Wallet Integration

Mobile deeplinking is triggered by useMintBurn immediately after write actions when a WalletConnect URI is available, not by ButtonSection itself. See the dedicated guide in docs/content/dapp/wc-deeplinking.mdx for architecture and behavior details.

Success Handling

Success deduplication, notification timing, and hook resets are implemented in useMintBurn. ButtonSection handles the UI side-effects only:

const handleMintSuccess = async () => { setBlockProcessingText(true) await onRefresh?.() setLoading(false) }

Success Flow

Step 1: Transaction Confirmation

Wait for blockchain confirmation via useWaitForTransactionReceipt.

Step 2: User Notification

useMintBurn dispatches notifications (toast and optional browser) through the notifications facade.

Step 3: State Refresh Delay

Wait 2 seconds to ensure blockchain state has propagated to RPC nodes.

Step 4: Force Data Refresh

Call onRefresh() (provided by MintPageWrapper) to update NFT ownership data from blockchain.

Step 5: Reset Transaction State

Clear loading state and reset wagmi hooks for future transactions.

Error Handling

Errors are formatted by formatMintError / formatBurnError and surfaced via the notifications system, then the hook resets its state. The UI does not manually show toasts for these cases, avoiding duplicates.

See dapp/app/lib/client/mint.client.ts and docs/content/dapp/notifications.mdx for details.

ProcessingModal Integration with Transaction Hash Passing

During transactions, ButtonSection displays the ProcessingModal to guide users through the transaction process. A key feature of this integration is the ability to pass transaction hashes to the modal, enabling users to track their transactions on block explorers:

// Mint-only state <ProcessingModal isVisible={isProcessing} onCancel={handleModalCancel} transactionHash={mintHash} /> // Burn-related states <ProcessingModal isVisible={isProcessing} onCancel={handleModalCancel} transactionHash={burnHash} />

Transaction Hash Management

useMintBurn provides isProcessing, mintHash, and burnHash. ButtonSection passes mintHash in the mint-only state and burnHash in burn-related states. Hashes are available as soon as the write is dispatched.

Purpose of Transaction Hash Passing

The transaction hash serves multiple important purposes in the user experience. It provides transparency by allowing users to see their transaction on a block explorer while waiting for confirmation. It enables debugging in development environments where Blockscout provides detailed transaction information. It offers reassurance to users who can verify their transaction is being processed by the network. Finally, it maintains context by keeping users informed without requiring them to leave the application.

The transaction hash is passed as soon as it’s available from the blockchain, typically within seconds of initiating the transaction. This immediate feedback transforms an opaque waiting period into a transparent process where users can track progress in real-time.

The modal’s cancel button allows users to reset component state if transactions become stuck:

const handleModalCancel = async () => { resetAll() // resets both mint and burn states from the hook setLoading(false) setBlockProcessingText(true) await onRefresh?.() }
⚠️

The cancel button doesn’t actually cancel blockchain transactions - it only resets the UI state. Users must still clear pending transactions in their wallet if needed. The transaction will continue processing on the blockchain regardless of UI state.

Animation and Styling

ButtonSection implements sophisticated animations for smooth state transitions:

Button State Animations

.mintButton.processing::before, .burnButton.processing::before { content: ''; position: absolute; animation: waveSlide 2s ease-in-out infinite; }

The wave animation provides visual feedback during transaction processing without being distracting.

Transition Effects

.container { opacity: 1; transition: opacity 0.3s ease-in-out; } .container.transitioning { opacity: 0; }

Fade transitions between states prevent jarring button swaps.

Hydration Safety

The component implements careful hydration handling to prevent SSR mismatches:

const [isHydrated, setIsHydrated] = useState(false) useEffect(() => { const timer = setTimeout(() => setIsHydrated(true), 50) return () => clearTimeout(timer) }, []) if (!isHydrated || renderState === 'loading') { return ( <div className={styles.container} role="status" aria-live="polite"> <button className={styles.loadingButton} disabled aria-label="Loading NFT actions" > Loading... </button> </div> ) }

This approach ensures consistent server and client rendering while providing immediate visual feedback.

Account Switching UX

To avoid flicker and stale states when users switch accounts, the component:

  • Freezes UI transitions while isSwitchingAccount is true.
  • Uses a blockProcessingText guard to prevent showing “Processing…” after a state swap.

This creates a smoother UX around account changes without confusing transient labels.

Accessibility

ButtonSection applies a consistent accessibility layer:

  • Uses role="status" and aria-live="polite" during loading states.
  • Groups actions with role="group" and descriptive aria-labels.
  • Applies aria-busy to buttons while processing.

These patterns improve screen reader clarity during asynchronous interactions.

Store Integration

ButtonSection consumes only the state it needs and delegates blockchain orchestration:

Store/HookData UsedPurpose
useAccountisConnectedDetermines if wallet is connected
NFT StorehasNFT, hasUsedTokenGate, tokenId, setLoading, isLoading, isSwitchingAccountNFT ownership state and loading management
useMintBurnmint, burn, isProcessing, mintHash, burnHash, resetAllAll blockchain write orchestration and notifications
onRefresh (prop)onRefresh() provided by MintPageWrapperForce data refresh after tx success or cancel

Notification Integration

Notifications (toast and optional browser) are dispatched by the hook via a shared facade; the component does not emit its own success/error toasts for mint/burn.

See docs/content/dapp/notifications.mdx for configuration and behavior.

Performance Optimizations

ButtonSection implements several performance optimizations:

Hydration Timer Cleanup - The initial hydration timeout is properly cleaned up to prevent memory leaks during component unmount.

Conditional Rendering - Components only render when in appropriate states, reducing React reconciliation work.

Transaction Deduplication - Implemented in useMintBurn; ButtonSection remains stateless regarding tx IDs.

Store Subscriptions - Components subscribe only to the specific store fields they need, minimizing re-renders when unrelated state changes.

Responsive Design

The component adapts its layout for mobile devices:

@media (max-width: 768px) { .container { flex-direction: column; gap: 0.8rem; min-height: 140px; /* Taller for vertical layout */ } .mintButton, .gateButton, .burnButton { width: 100%; max-width: 300px; } }

Buttons stack vertically on mobile while maintaining appropriate touch targets.

Testing Strategies

When testing ButtonSection, prefer mocking useMintBurn for UI behavior and keep wagmi-level contract mocks in hook unit tests. Key scenarios:

  • State transitions across connection and ownership states
  • Processing state and aria attributes while isProcessing is true
  • Passing the correct transactionHash to ProcessingModal
  • Cancel behavior: resetAll(), onRefresh(), and processing text guard

Common Integration Patterns

ButtonSection is typically placed below the NFT visualization:

function MintInterface() { return ( <> <TokenStatus /> <NFTScreen /> <ButtonSection /> {/* In app, MintPageWrapper supplies onRefresh */} </> ) }

This arrangement creates a natural flow from status to visualization to actions.

Customization Options

The component can be extended for additional functionality:

Additional Actions

New buttons can be added for extended functionality:

if (hasNFT && !isListed) { return <button onClick={handleList}>List on Marketplace</button> }

Custom Contract Functions

The pattern can be extended to call other contract functions:

const handleTransfer = () => { executeWithNetworkCheck(() => { transfer({ address: KEY_TOKEN_ADDRESS, abi: fullKeyTokenAbi, functionName: 'transferFrom', args: [address, recipientAddress, tokenId] }) }) }

Alternative Styling

The modular CSS allows complete visual customization while maintaining functionality.

Best Practices

When working with ButtonSection or similar transaction components:

Always Validate Network - Handled by useMintBurn to prevent wrong-chain transactions.

Provide Clear Feedback - Use both toast and modal feedback for transaction states, including transaction hashes for transparency.

Handle All Error Cases - Implement error handling for every possible failure scenario.

Test Mobile Flows - Always test wallet deep linking on actual mobile devices.

Respect Processing States - Disable buttons during transactions to prevent double-spending attempts.

Pass Transaction Context - Always forward transaction hashes to feedback components for user transparency.

Troubleshooting Guide

IssueCommon CauseSolution
Buttons don’t appearHydration mismatchEnsure hydration safety logic
Transaction won’t completeWrong networkCheck network validation
Multiple success toastsEffect re-runsImplement hash deduplication
State doesn’t update after mintInsufficient delayEnsure onRefresh() is wired; allow brief propagation delay
No explorer link in modalHash not passedEnsure transactionHash prop is set
UI flicker on account switchState recompute during switchFreeze UI while isSwitchingAccount is true
Stuck “Processing…” labelRender swap mid-transactionUse blockProcessingText when transitions occur

On Sepolia, ButtonSection renders a faucet link to help users obtain test ETH. The link is gated by the active chain check and appears below the action area.

Cross-References

  • Hook: dapp/app/hooks/useMintBurn.ts:1
  • Client helpers: dapp/app/lib/client/mint.client.ts:1
  • Wrapper: dapp/app/mint/components/MintPageWrapper.tsx:15
  • Deeplinking Guide: docs/content/dapp/wc-deeplinking.mdx
  • Notifications: docs/content/dapp/notifications.mdx

Summary

ButtonSection focuses on adaptive UI, accessibility, modal integration, and refresh actions. The useMintBurn hook owns blockchain writes, network validation, deeplinking, notifications, and success/error lifecycles. This separation keeps the UI simple and resilient while maintaining a polished transaction experience.