Skip to content

Web3 Integration

Monark's web3 components (Wallet, ConnectWallet, TokenAmount, NetworkBadge, TxStatus, SwapForm) are intentionally presentational. They accept props and emit callbacks; they do not fetch, sign, broadcast, or hold connection state. Wiring them to a real stack is your job, which keeps the registry connector-agnostic.

This page shows the canonical wiring for wagmi v2 + viem. The same pattern adapts to RainbowKit, Reown AppKit, or a hand-rolled connector.

ConnectWallet

"use client"

import { ConnectWallet } from "@/components/ui/connect-wallet"
import {
  useAccount,
  useConnect,
  useDisconnect,
  useEnsName,
} from "wagmi"

export function ConnectButton() {
  const { address, status } = useAccount()
  const { connect, connectors, status: connectStatus } = useConnect()
  const { disconnect } = useDisconnect()
  const { data: ensName } = useEnsName({ address })

  const derivedStatus =
    status === "connected"
      ? "connected"
      : connectStatus === "pending"
        ? "connecting"
        : "disconnected"

  return (
    <ConnectWallet
      status={derivedStatus}
      address={address}
      name={ensName ?? undefined}
      onConnect={() => connect({ connector: connectors[0] })}
      onDisconnect={() => disconnect()}
    />
  )
}

The component only cares about status, address, and two callbacks. Derive them however your connector exposes them.

TokenAmount from a balance hook

import { TokenAmount } from "@/components/ui/token-amount"
import { useBalance } from "wagmi"

export function EthBalance({ address }: { address: `0x${string}` }) {
  const { data, isLoading } = useBalance({ address })

  if (isLoading || !data) return <TokenAmount value={0n} symbol="ETH" />

  return (
    <TokenAmount
      value={data.value}
      decimals={data.decimals}
      symbol={data.symbol}
      fractionDigits={4}
    />
  )
}

For fiat conversion, compose your own price hook (CoinGecko, Chainlink, Pyth) and pass the result as usdValue; the component never fetches prices.

NetworkBadge from chain state

import { NetworkBadge } from "@/components/ui/network-badge"
import { useChainId, useChains } from "wagmi"

export function ChainPill() {
  const chainId = useChainId()
  const chains = useChains()
  const chain = chains.find((c) => c.id === chainId)

  if (!chain) return null

  return (
    <NetworkBadge
      name={chain.name}
      icon={<ChainIcon id={chain.id} />} // your own icon component
    />
  )
}

TxStatus from a write-contract flow

import { TxStatus } from "@/components/ui/tx-status"
import { useWaitForTransactionReceipt, useWriteContract } from "wagmi"

export function MintButton() {
  const { data: hash, writeContract, isPending } = useWriteContract()
  const { isLoading, isSuccess, isError } = useWaitForTransactionReceipt({ hash })

  const status =
    !hash ? undefined
    : isError ? "failed"
    : isSuccess ? "confirmed"
    : "pending"

  return (
    <>
      <button
        onClick={() =>
          writeContract({
            address: "0x...",
            abi: erc721Abi,
            functionName: "mint",
          })
        }
        disabled={isPending}
      >
        {isPending ? "Confirming in wallet..." : "Mint"}
      </button>
      {hash && status && (
        <TxStatus
          status={status}
          hash={hash}
          explorerUrl="https://etherscan.io"
        />
      )}
    </>
  )
}

SwapForm with a real quote

The form is controlled; drive fromAmount / toAmount from a debounced quote query (viem's readContract against a DEX router, 1inch API, CoW Protocol, etc.):

const debouncedFromAmount = useDebounce(fromAmount, 300)

const { data: quote, isLoading: isQuoting } = useQuery({
  queryKey: ["quote", fromToken, toToken, debouncedFromAmount],
  queryFn: () => fetchQuote(fromToken, toToken, debouncedFromAmount),
  enabled: Boolean(debouncedFromAmount),
})

useEffect(() => {
  if (quote) setToAmount(quote.outAmount)
}, [quote])

const status =
  !fromAmount ? "idle"
  : isQuoting ? "quoting"
  : quote ? "ready"
  : "error"

Then pass status into <SwapForm status={status} ... />. The submit button disables and shows "Fetching quote..." automatically.

Anti-patterns

Don't mutate address locally to strip it or reformat. Pass the raw checksummed value; WalletAddress truncates for display and exposes the full address via title.

Don't embed signing logic in the components. Keep onSwap, onConnect, onDisconnect as thin delegators; your state machine (wagmi's pending / success / error) decides what happens.

Don't fetch prices inside TokenAmount. The component is a formatter; price is an input.