Skip to main content

Overview

Staxiq uses @stacks/connect for wallet authentication, providing seamless integration with popular Stacks wallets like Leather, Xverse, and Asigna. Authentication persists across sessions using the UserSession API.

Authentication Flow

The authentication system is managed through the useWallet hook, which handles:
  • Wallet connection prompts
  • Session persistence
  • Network detection (testnet/mainnet)
  • Address extraction
  • Disconnection and cleanup
1

Initialize App Config

Configure app permissions and create a UserSession instance.
import { AppConfig, UserSession } from '@stacks/connect';

const appConfig = new AppConfig(['store_write', 'publish_data']);
const userSession = new UserSession({ appConfig });
2

Trigger Authentication

Call the authenticate function with app details and callbacks.
import { authenticate } from '@stacks/connect';

authenticate({
  userSession,
  appDetails: {
    name: 'Staxiq',
    icon: window.location.origin + '/favicon.ico',
  },
  onFinish: (payload) => {
    const userData = payload.userSession.loadUserData();
    const address = userData?.profile?.stxAddress?.mainnet;
    // Handle successful connection
  },
  onCancel: () => {
    // Handle user cancellation
  },
});
3

Check Existing Session

On app load, check if user is already authenticated.
if (userSession.isUserSignedIn()) {
  const userData = userSession.loadUserData();
  const address = userData?.profile?.stxAddress?.mainnet;
  // User is already connected
}
4

Handle Disconnection

Sign out and clear session data.
userSession.signUserOut();
window.location.reload(); // Refresh to reset app state

Using the useWallet Hook

The recommended way to handle authentication is through the useWallet hook:
import { useWallet } from './hooks/useWallet';

function WalletButton() {
  const { 
    connected, 
    address, 
    shortAddress, 
    connectWallet, 
    disconnectWallet, 
    loading 
  } = useWallet();

  if (loading) {
    return <button disabled>Connecting...</button>;
  }

  if (connected) {
    return (
      <div>
        <span>Connected: {shortAddress(address)}</span>
        <button onClick={disconnectWallet}>Disconnect</button>
      </div>
    );
  }

  return <button onClick={connectWallet}>Connect Wallet</button>;
}
View full useWallet documentation →

Network Detection

Staxiq automatically detects the correct network based on the user’s address:
testnet
boolean
Addresses starting with ST use testnet
mainnet
boolean
Addresses starting with SP use mainnet
const getStxAddress = (userData) => {
  return network === 'testnet'
    ? userData?.profile?.stxAddress?.testnet
    : userData?.profile?.stxAddress?.mainnet;
};

Session Persistence

User sessions are automatically persisted in browser storage. When users return to your app:
useEffect(() => {
  if (userSession.isUserSignedIn()) {
    const userData = userSession.loadUserData();
    const addr = getStxAddress(userData);
    if (addr) {
      setAddress(addr);
      setConnected(true);
    }
  }
}, []);

App Configuration

Required Permissions

store_write
string
required
Allows the app to write to Gaia storage (user’s decentralized storage)
publish_data
string
required
Enables publishing data to the blockchain

App Details

name
string
required
Application name shown in wallet connection prompt
icon
string
required
App icon URL displayed during authentication (must be absolute URL)

User Data Structure

After successful authentication, the user data object contains:
profile
object
User profile information
stxAddress
object
Stacks addresses for different networks
mainnet
string
Mainnet address (starts with SP)
testnet
string
Testnet address (starts with ST)
username
string
BNS username if registered

Error Handling

Handle authentication errors gracefully:
try {
  await authenticate({
    userSession,
    appDetails,
    onFinish: (payload) => {
      console.log('Authentication successful');
      console.log('isSignedIn:', payload.userSession.isUserSignedIn());
    },
    onCancel: () => {
      console.log('User cancelled authentication');
      setLoading(false);
    },
  });
} catch (error) {
  console.error('Authentication error:', error);
  setLoading(false);
}

Logout Flow

Properly clean up user sessions on logout:
function disconnectWallet() {
  try {
    userSession.signUserOut();
  } catch (err) {
    console.error('Logout error:', err);
  }
  setConnected(false);
  setAddress(null);
  window.location.reload();
}
The page reload ensures all components and contexts are reset to their unauthenticated state.

Address Formatting

Utility function to display shortened addresses:
function shortAddress(addr) {
  if (!addr) return '';
  return `${addr.slice(0, 5)}...${addr.slice(-4)}`;
}

// Example: "SP2H8...9ABC"

Complete Example

import { useWallet } from './hooks/useWallet';
import { useNetwork } from './context/NetworkContext';

function App() {
  const { connected, address, connectWallet, disconnectWallet } = useWallet();
  const { network } = useNetwork();

  return (
    <div>
      <header>
        <h1>Staxiq - Bitcoin DeFi Copilot</h1>
        {connected ? (
          <div>
            <span>Network: {network}</span>
            <span>Address: {address}</span>
            <button onClick={disconnectWallet}>Disconnect</button>
          </div>
        ) : (
          <button onClick={connectWallet}>Connect Wallet</button>
        )}
      </header>
      
      {connected && (
        <main>
          <Dashboard address={address} />
        </main>
      )}
    </div>
  );
}

Best Practices

Loading States

Always show loading indicators during wallet connection to improve UX

Error Recovery

Provide clear error messages and retry options for failed connections

Session Checking

Check for existing sessions on app load to auto-reconnect users

Clean Logout

Always call signUserOut() and reload to prevent state inconsistencies

Build docs developers (and LLMs) love