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. The component orchestrates multiple wagmi hooks for transaction management, integrates with the network checking system to prevent wrong-chain transactions, and provides real-time feedback through both the ProcessingModal component and toast notifications. This multi-layered approach ensures users always understand what actions are available and receive clear feedback during asynchronous blockchain operations.
State-Based UI Adaptation
The component renders different interfaces based on four primary states:
State | Conditions | UI Elements |
---|---|---|
Not Connected | !isConnected | ConnectWrapper (wallet connection button) |
No NFT | isConnected && !hasNFT | Mint NFT button |
Unused NFT | isConnected && hasNFT && !hasUsedTokenGate | Token Gate link + Burn button |
Used NFT | isConnected && hasNFT && hasUsedTokenGate | Burn 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 follows the zero-configuration pattern established by other components:
export default function ButtonSection() {
// All data from hooks and stores - no props required
}
This design maintains consistency across the component hierarchy and simplifies integration.
Transaction Management
The component implements comprehensive transaction management using wagmi hooks:
Write Contract Hooks
const {
writeContract: mint,
data: mintHash,
isPending: isMinting,
error: mintError,
reset: resetMint
} = useWriteContract()
const {
writeContract: burn,
data: burnHash,
isPending: isBurning,
error: burnError,
reset: resetBurn
} = useWriteContract()
Each hook provides complete transaction lifecycle management from initiation through confirmation.
Transaction Confirmation
The component monitors transaction confirmations to trigger appropriate state updates:
const { isLoading: isMintConfirming, isSuccess: isMintSuccess } =
useWaitForTransactionReceipt({
hash: mintHash,
query: { enabled: !!mintHash },
})
This pattern ensures the UI accurately reflects blockchain state rather than optimistically updating.
Network Validation
Before executing any transaction, ButtonSection validates the user is on the correct network:
const handleMint = () => {
setLoading(true)
executeWithNetworkCheck(() => {
mint({
address: KEY_TOKEN_ADDRESS,
abi: fullKeyTokenAbi,
functionName: 'mint',
})
if (displayUri) openWallet()
})
}
The executeWithNetworkCheck
wrapper prevents transactions on wrong chains, protecting users from costly mistakes.
Mobile Wallet Integration
The component includes special handling for mobile wallet interactions:
if (displayUri) openWallet()
When a WalletConnect URI is available and the user is on mobile, the component automatically triggers the wallet deep link after initiating transactions. This reduces friction for mobile users who would otherwise need to manually switch to their wallet app.
Success Handling
ButtonSection implements a sophisticated success handling flow that ensures proper state updates and user feedback:
Deduplication Logic
const [lastMintHash, setLastMintHash] = useState<string | null>(null)
const [lastBurnHash, setLastBurnHash] = useState<string | null>(null)
useEffect(() => {
if (isMintSuccess && mintHash && mintHash !== lastMintHash) {
setLastMintHash(mintHash)
// Handle success only once per transaction
}
}, [isMintSuccess, mintHash])
This pattern prevents duplicate success handlers when effects re-run due to dependency changes.
Success Flow
Step 1: Transaction Confirmation
Wait for blockchain confirmation via useWaitForTransactionReceipt
.
Step 2: User Notification
Display toast notification and trigger browser notification if permissions granted.
Step 3: State Refresh Delay
Wait 2 seconds to ensure blockchain state has propagated to RPC nodes.
Step 4: Force Data Refresh
Call forceRefresh()
to update NFT ownership data from blockchain.
Step 5: Reset Transaction State
Clear loading state and reset wagmi hooks for future transactions.
Error Handling
The component implements comprehensive error handling for all failure scenarios:
useEffect(() => {
if (mintError) {
toast.error(`Failed to mint NFT: ${mintError.message}`)
setLoading(false)
resetMint()
}
}, [mintError, setLoading, resetMint])
Error messages are extracted and presented in user-friendly format through toast notifications, while the component state resets to allow retry attempts.
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:
<ProcessingModal
isVisible={isProcessing}
onCancel={handleModalCancel}
transactionHash={mintHash || burnHash} // Pass current transaction hash
/>
Transaction Hash Management
The component intelligently determines which transaction hash to pass based on the current operation:
const isProcessing = isMinting || isBurning || isMintConfirming || isBurnConfirming
const transactionHash = mintHash || burnHash // Current transaction hash
This approach ensures that the ProcessingModal always receives the relevant transaction hash, whether the user is minting or burning an NFT. The hash becomes available immediately after the transaction is submitted to the blockchain, before confirmation completes.
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.
Modal Cancel Functionality
The modal’s cancel button allows users to reset component state if transactions become stuck:
const handleModalCancel = async () => {
resetMint()
resetBurn()
setLoading(false)
await forceRefresh()
}
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}>
<button className={styles.loadingButton} disabled>
Loading...
</button>
</div>
)
}
This approach ensures consistent server and client rendering while providing immediate visual feedback.
Store Integration
ButtonSection integrates with multiple stores for state management:
Store/Hook | Data Used | Purpose |
---|---|---|
useAccount | isConnected | Determines if wallet is connected |
NFT Store | hasNFT , hasUsedTokenGate , tokenId , setLoading , isLoading | NFT ownership state and loading management |
Wallet Connect Store | openWallet , displayUri | Mobile wallet deep linking |
useNFTData | forceRefresh | Trigger data updates after transactions |
Notification Integration
The component triggers both toast and browser notifications for important events:
toast.success('NFT minted successfully!')
setTimeout(() => {
NFTNotifications.minted()
}, 100)
The delay ensures notification permissions are ready before attempting to send browser notifications.
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 - Success handlers only fire once per unique transaction hash.
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
Testing ButtonSection requires extensive mocking of blockchain interactions:
// Mock transaction flow
vi.mock('wagmi', () => ({
useWriteContract: vi.fn(() => ({
writeContract: mockWriteContract,
data: '0x123',
isPending: false,
error: null,
reset: vi.fn()
})),
useWaitForTransactionReceipt: vi.fn(() => ({
isLoading: false,
isSuccess: true
}))
}))
Key test scenarios include successful minting and burning flows, error handling and recovery, state transitions between different ownership states, mobile wallet deep linking behavior, and transaction hash passing to ProcessingModal.
Common Integration Patterns
ButtonSection is typically placed below the NFT visualization:
function MintInterface() {
return (
<>
<TokenStatus />
<NFTScreen />
<ButtonSection /> {/* Actions at bottom */}
</>
)
}
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 - Never skip network validation 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
Issue | Common Cause | Solution |
---|---|---|
Buttons don’t appear | Hydration mismatch | Ensure hydration safety logic |
Transaction won’t complete | Wrong network | Check network validation |
Multiple success toasts | Effect re-runs | Implement hash deduplication |
State doesn’t update after mint | Insufficient delay | Increase refresh delay |
No explorer link in modal | Hash not passed | Ensure transactionHash prop is set |
Summary
ButtonSection exemplifies best practices for blockchain transaction interfaces in React applications. Through its adaptive UI, comprehensive transaction management, mobile optimization, and extensive error handling, it transforms complex Web3 operations into intuitive user interactions. The component’s integration of network validation, processing feedback with transaction transparency, and notification systems creates a professional experience that guides users through every step of NFT ownership. The ability to pass transaction hashes to the ProcessingModal for block explorer integration demonstrates the component’s commitment to transparency and user empowerment. While currently optimized for minting and burning operations, its patterns provide a solid foundation for extending functionality to support additional blockchain interactions while maintaining the same level of polish and user experience.