useMintBurn Hook
The useMintBurn hook is the write-side transaction orchestrator for the Colored Key NFT. It wraps wagmi write/receipt hooks, mobile wallet nudges via openWalletDeeplink, and notification dispatch into a single cohesive API, so UI components can focus on what to show rather than how to talk to the blockchain.
-
Source:
dapp/app/hooks/useMintBurn.ts -
Primary consumers: components like
ButtonSection -
Companion docs:
At a high level, useMintBurn:
- Sends mint and burn transactions.
- Tracks confirmation status via
useWaitForTransactionReceipt. - Deduplicates success handling per transaction hash.
- Dispatches success and error notifications.
- On mobile WalletConnect, calls
openWalletDeeplink()after writes to nudge the wallet app.
Hook API
export interface UseMintBurnOptions {
onMintSuccess?: () => Promise<void> | void
onBurnSuccess?: () => Promise<void> | void
onMintError?: (error: Error) => void
onBurnError?: (error: Error) => void
autoRefresh?: boolean
notificationDelay?: number
}
export interface UseMintBurnReturn {
// Actions
mint: () => void
burn: (tokenId: string | number | null) => void
// Aggregate state
isProcessing: boolean
// Fine-grained state
isMinting: boolean
isBurning: boolean
isMintConfirming: boolean
isBurnConfirming: boolean
// Transaction hashes
mintHash: `0x${string}` | undefined
burnHash: `0x${string}` | undefined
// Success flags
isMintSuccess: boolean
isBurnSuccess: boolean
// Error objects
mintError: Error | null
burnError: Error | null
// Reset helpers
resetMint: () => void
resetBurn: () => void
resetAll: () => void
}
export function useMintBurn(options?: UseMintBurnOptions): UseMintBurnReturnNote
autoRefreshexists in the options interface as a future extension point. In the current implementation, refresh behavior is driven by caller-provided callbacks (onMintSuccess,onBurnSuccess), not automatic polling inside the hook.
Options (UseMintBurnOptions)
Configuration knobs for the hook:
| Option | Type | Purpose |
|---|---|---|
onMintSuccess | () => void | Promise<void> | Called after a successful mint transaction, once the hook has seen a confirmed receipt and waited for a brief propagation delay. |
onBurnSuccess | () => void | Promise<void> | Same as |
onMintError | (error: Error) => void | Optional callback invoked after mint errors are formatted and surfaced via notifications. |
onBurnError | (error: Error) => void | Optional callback invoked after burn errors are formatted and surfaced via notifications. |
notificationDelay | number | Delay (ms) before firing success notification events like |
autoRefresh | boolean | undefined | Reserved for future use. At present, the hook expects callers to refresh external
state (e.g. via |
In practice, most UI components only pass onMintSuccess, onBurnSuccess, and occasionally notificationDelay.
Return Shape (UseMintBurnReturn)
Actions
-
mint()- Wraps a write to the Colored Key NFT contract using
createMintAction()anduseWriteContract. - Resets the dapp view chain to the active contract chain before requesting the write.
- On mobile with the WalletConnect connector, calls
openWalletDeeplink()after sending the write.
- Wraps a write to the Colored Key NFT contract using
-
burn(tokenId)-
Validates
tokenIdbefore attempting a burn.- If
tokenIdisnull,undefined, or an empty string, the hook sends a user-friendly error notification ("No token ID available for burning") and does not attempt a write.
- If
-
Resets the dapp view chain to the active contract chain before requesting the write.
-
Otherwise, wraps a burn write using
createBurnAction(tokenId)anduseWriteContract. -
Triggers the same mobile wallet nudge as
mint()when on mobile + WalletConnect.
-
Transaction State Flags
-
isMinting/isBurning- Mirror the
isPendingflags from the underlyinguseWriteContractcalls.
- Mirror the
-
isMintConfirming/isBurnConfirming- Come from
useWaitForTransactionReceipt({ hash }), and indicate that a transaction has been sent and is waiting for confirmation.
- Come from
-
isProcessing-
Aggregate flag computed as:
isMinting || isBurning || isMintConfirming || isBurnConfirming
-
Recommended flag for showing global “Processing…” states in the UI.
-
Hashes and Success Flags
-
mintHash/burnHash- Populated as soon as the corresponding
writeContractcalls succeed locally. - Used by UIs (for example
ProcessingModal) to render explorer links for pending transactions.
- Populated as soon as the corresponding
-
isMintSuccess/isBurnSuccess- Become
truewhenuseWaitForTransactionReceiptreports success for the associated hash.
- Become
Errors and Reset Helpers
-
mintError/burnError- Exposed raw error objects. Internally, the hook passes them to
formatMintError/formatBurnError, which handle user-facing messaging and notification dispatch.
- Exposed raw error objects. Internally, the hook passes them to
-
resetMint()/resetBurn()- Clear wagmi’s internal state for the respective write hooks via stable refs (
resetMintRef,resetBurnRef).
- Clear wagmi’s internal state for the respective write hooks via stable refs (
-
resetAll()- Calls both resets and clears internal hash-tracking refs (
lastMintHashRef,lastBurnHashRef). - Useful when a UI wants to “hard reset” the transaction state, e.g. when closing a modal.
- Calls both resets and clears internal hash-tracking refs (
Core Responsibilities & Internal Design
Internally, useMintBurn coordinates several concerns: contract writes, confirmation tracking, mobile wallet nudges, notifications, and success deduplication.
1. Contract Writes & Receipts
The hook maintains two write flows and two receipt trackers:
-
Mint write + receipt:
useWriteContract()for mint actions.useWaitForTransactionReceipt({ hash: mintHash })for confirmations.
-
Burn write + receipt:
useWriteContract()for burn actions.useWaitForTransactionReceipt({ hash: burnHash })for confirmations.
isProcessing is derived from the union of these four flags and is the recommended signal for disabling UI buttons and showing processing labels.
2. Network Considerations
useMintBurn resets the dapp view chain to the active contract chain before each write, but it does not block wrong-chain wallet writes. If your flow requires wallet-chain validation, enforce it before calling mint() / burn() (for example, in the page wrapper or a dedicated guard). See docs/content/dapp/smart-contract-data/contract-config.mdx for how chains and addresses are configured. For view-chain behavior, see Dapp Chain State.
3. Mobile Wallet Deeplink Helper
To support mobile flows, the hook uses the lightweight deeplink helper:
- After a write is initiated, if
isMobileDevice()is true and the active connector id iswalletConnect, the hook callsopenWalletDeeplink(). - The helper is best-effort and does not persist session topics or sanitize URIs.
4. Notifications & Error Formatting
Success and error messaging is centralized via a small client-side facade plus helpers:
-
Success notifications
sendNotificationEvent('NFT_MINTED', { source: 'user' })sendNotificationEvent('NFT_BURNED', { source: 'user' })
-
Error formatting and notifications
formatMintError(error)andformatBurnError(error)map low-level wagmi/viem errors into user-friendly messages.- These helpers also send notifications through
sendErrorNotification(...).
This design means UI components (like ButtonSection) do not emit their own success/error toasts for mint/burn; they rely on the shared notifications layer instead, avoiding duplicates.
5. Success Lifecycles & Deduplication
To avoid double handling on rerenders, the hook tracks the last-seen hash for each flow:
lastMintHashRef: Ref<string | null>lastBurnHashRef: Ref<string | null>
When a transaction succeeds, the hook checks whether the hash has already been processed:
Step 1: Transaction hash appears
A write sets mintHash or burnHash when it is accepted by the client.
Step 2: Receipt confirms
useWaitForTransactionReceipt flips isMintSuccess or isBurnSuccess to true.
Step 3: Dedup check
The hook verifies that the hash is non-null and different from the last processed hash in lastMintHashRef or lastBurnHashRef.
Step 4: Notifications & callback
It logs the transaction (console.log(‘Mint transaction:’, mintHash) or ‘Burn transaction:’), schedules a success notification after notificationDelay, and schedules the appropriate success callback.
Step 5: Reset
After the callback, the hook resets wagmi state for that flow, leaving the hash ref populated so the same hash isn’t processed again on rerender.
This dedup behavior is explicitly tested in useMintBurn.test.tsx to ensure that a confirmed transaction only triggers a single success event + callback per hash.
6. Reset Helpers & Stable Refs
The hook stores several values in refs to avoid identity issues:
resetMintRef,resetBurnRefwrap wagmi’sresetfunctions.onMintSuccessRef,onBurnSuccessRefwrap the callbacks passed in options.
This allows the hook to use these callbacks safely inside effects and timers without invalidating dependencies or re-registering timers on every render.
Usage Patterns
Basic Integration
A minimal component that uses useMintBurn directly:
import { useState } from 'react'
import { useMintBurn } from '@/app/hooks/useMintBurn'
function SimpleMintButton() {
const [isLoading, setLoading] = useState(false)
const { mint, isProcessing } = useMintBurn({
onMintSuccess: () => {
setLoading(false)
// e.g., trigger a visual celebration here
},
})
const handleClick = () => {
setLoading(true)
mint()
}
const label = isProcessing ? 'Processing…' : 'Mint NFT'
return (
<button onClick={handleClick} disabled={isProcessing} aria-busy={isProcessing}>
{label}
</button>
)
}Working with the Smart Contract Data Layer
In the actual app, useMintBurn is almost always paired with the smart-contract data system:
useNFTDatahandles reads and state sync into the NFT store.useMintBurnhandles writes and transaction lifecycles.
A common pattern is:
MintPageWrappercallsuseNFTData()and passesforceRefreshdown asonRefresh.ButtonSectioncallsuseMintBurn({ onMintSuccess, onBurnSuccess }).onMintSuccess/onBurnSuccesscallonRefresh()and clear loading flags in the NFT store.
function MintPageWrapper() {
const { forceRefresh } = useNFTData()
return (
<>
{/* ...status + visualization components... */}
<ButtonSection onRefresh={forceRefresh} />
</>
)
}
// Inside ButtonSection
const handleMintSuccess = async () => {
setBlockProcessingText(true)
await onRefresh?.()
setLoading(false)
}
const handleBurnSuccess = async () => {
setBlockProcessingText(true)
await onRefresh?.()
setLoading(false)
}
const { mint, burn, isProcessing, mintHash, burnHash, resetAll } = useMintBurn({
onMintSuccess: handleMintSuccess,
onBurnSuccess: handleBurnSuccess,
})This separation keeps useMintBurn narrowly focused on transactions while the data layer remains responsible for reading and caching on-chain state.
ButtonSection Integration
ButtonSection is the canonical consumer of useMintBurn. It layers UX concerns on top of the hook:
- State-based UI (
not-connected,no-nft,has-nft,used-gate). - Hydration safety and account switching UX.
- Accessibility roles and
aria-*attributes. - Integration with
ProcessingModal. - Responsive layout and animated transitions.
useMintBurn provides the transaction brain; ButtonSection wires it into the interface:
-
Uses
isProcessingplus store flags to compute abusystate, which drives button labels ("Mint NFT"vs"Processing…"),disabledstate, andaria-busyattributes. -
Chooses the correct hash to pass into
ProcessingModal:- In the latest implementation, ButtonSection passes
mintHashorburnHashbased on which action is active. Earlier docs show amintHash || burnHashpattern; see the ButtonSection docs for current JSX.
- In the latest implementation, ButtonSection passes
-
Invokes
resetAll()and refresh callbacks from the modal’s cancel button to reset transaction state and avoid stuck UIs.
ButtonSection owns what the user sees (buttons, labels, transitions, modal), while useMintBurn owns what the blockchain does (writes, confirmations, notifications, mobile wallet nudges). This separation makes it safe to reuse the hook in alternate UIs without duplicating transaction logic.
Testing
useMintBurn has a dedicated unit test suite that validates its behavior in isolation from the UI:
Key behaviors covered in useMintBurn.test.tsx:
-
Mint flow
-
Writes the expected mint action (
{ kind: 'MINT_ACTION' }). -
Calls
openWalletDeeplink()on mobile when the connector id iswalletConnect. -
On confirmed success, the hook:
- Logs the transaction (
"Mint transaction:", hash). - Dispatches
sendNotificationEvent('NFT_MINTED', { source: 'user' }). - Invokes the
onMintSuccesscallback once. - Calls the mint
resetfunction.
- Logs the transaction (
-
Success handling is deduplicated per hash; rerenders with the same hash do not re-fire logs or notifications.
-
-
Burn flow
-
burn(null)(or an empty token ID) sends an error notification ("No token ID available for burning") and does not callwriteContract. -
For a valid token ID, on confirmed success, the hook:
- Logs the burn transaction.
- Dispatches
sendNotificationEvent('NFT_BURNED', { source: 'user' }). - Invokes
onBurnSuccessonce. - Calls the burn
resetfunction.
-
-
Error handling
-
When
mintErrororburnErrorare injected into the mocked wagmi hooks, the test asserts that:formatMintError/formatBurnErrorare called.sendErrorNotificationis called with the formatted message (e.g."Mint failed: boom mint").onMintError/onBurnErrorcallbacks are invoked.- The corresponding
resetfunctions are called exactly once.
-
-
WalletConnect behavior
- When the connector id is not
walletConnect, the hook skipsopenWalletDeeplink()for both mint and burn. - When
isMobileDevice()returns false, the hook skipsopenWalletDeeplink()for both mint and burn.
- When the connector id is not
These tests are built with @testing-library/react’s renderHook, plus explicit mocks for:
useWriteContractanduseWaitForTransactionReceiptfromwagmi.isMobileDeviceandopenWalletDeeplink.- The notifications facade and client helpers in
@lib/client/mint.client.
Together, they verify that useMintBurn implements the contract promised by this documentation.
Related Documentation
Adaptive action interface that consumes useMintBurn for mint, burn, and token gate navigation.
Read-side data orchestration for NFT ownership, colors, and token usage, paired with useMintBurn for a full read/write pipeline.
Zustand store for NFT UI state, including loading flags and account switching used by both useNFTData and ButtonSection.
High-level overview of the smart contract data system that useMintBurn plugs into.
Centralized notification facade used by useMintBurn for success and error messaging.
Architecture for mobile wallet deeplinking that useMintBurn taps into via openWalletDeeplink.
Summary
useMintBurn is the transaction brain for the Colored Key mint/burn flow:
- It encapsulates wagmi writes, transaction receipts, notifications, and mobile wallet nudges.
- It exposes a small, stable API (
mint,burn,isProcessing, hashes, reset helpers) that can be consumed by any UI. - It pairs with
useNFTDataand the NFT store to form a complete read/write pipeline for on-chain NFT state.
By centralizing transaction logic in this hook, RitoSwap keeps components like ButtonSection focused on UX, while ensuring consistent, well-tested behavior across all transaction entry points.