HAX
← Back

Steps for Creating a 3D NFT Marketplace on Stacks #06 - Integrate Wallet Connection

2025-04-12Fabo Hax

In this part of the series, we’ll integrate wallet connection into the 3D NFT Marketplace UI, using React + Wallet context provider. This is key for allowing users to authenticate and sign on-chain transactions.


🎯 Goal

Build a wallet connect button that:

  • Connects to Xverse or Leather Wallet
  • Copies the user’s address with visual feedback
  • Disconnects from the wallet
  • Displays the connected address truncated (e.g., SP123...abcd)

🧱 Implementation

1. Component: ConnectWalletButton.tsx

'use client';
import { Box, Button, Flex, Icon, Text, Tooltip, IconButton } from '@chakra-ui/react';
import { useContext, useState } from 'react';
import { HiroWalletContext } from './HiroWalletProvider';
import { RiFileCopyLine, RiCloseLine } from 'react-icons/ri';

interface ConnectWalletButtonProps {
  children?: React.ReactNode;
  [key: string]: any;
}

export const ConnectWalletButton = (buttonProps: ConnectWalletButtonProps) => {
  const { children } = buttonProps;
  const [didCopyAddress, setDidCopyAddress] = useState(false);
  const { authenticate, isWalletConnected, mainnetAddress, testnetAddress, network, disconnect } =
    useContext(HiroWalletContext);

  const currentAddress = network === 'mainnet' ? mainnetAddress : testnetAddress;

  const copyAddress = () => {
    if (currentAddress) {
      navigator.clipboard.writeText(currentAddress);
      setDidCopyAddress(true);
      setTimeout(() => {
        setDidCopyAddress(false);
      }, 1000);
    }
  };

  const truncateMiddle = (str: string | null) => {
    if (!str) return '';
    if (str.length <= 12) return str;
    return `${str.slice(0, 6)}...${str.slice(-4)}`;
  };

  return isWalletConnected ? (
    <Flex align="center" gap={2} p={2} borderRadius="md" bg="gray.200">
      <Text color="gray.800" fontSize="sm">
        {truncateMiddle(currentAddress)}
      </Text>
      <Tooltip label="Copy address" isOpen={didCopyAddress ? true : undefined}>
        <IconButton
          aria-label="Copy address"
          icon={<Icon as={RiFileCopyLine} />}
          size="sm"
          variant="ghost"
          onClick={copyAddress}
          minW="8"
          p="1"
        />
      </Tooltip>
      <Tooltip label="Disconnect wallet">
        <IconButton
          aria-label="Disconnect wallet"
          icon={<Icon as={RiCloseLine} />}
          size="sm"
          variant="ghost"
          onClick={disconnect}
          data-testid="disconnect-wallet-address-button"
          minW="8"
          p="1"
        />
      </Tooltip>
    </Flex>
  ) : (
    <Button size="sm" onClick={authenticate} data-testid="wallet-connect-button" {...buttonProps}>
      {children || 'Connect Wallet'}
    </Button>
  );
};

Watch full code here: ConnectWallet.tsx

📎 Key Points

  • authenticate: triggers the Wallet popup.
  • isWalletConnected: checks for active session.
  • testnetAddress / mainnetAddress: dynamically switch based on environment.

🔌 What’s next?

In the next step, we’ll integrate the minting interface into the UI, so, users can upload a model and set the metadata.

Go to Step #07

Sign Up to Stay Sync:

menu