Skip to main content

Overview

The useWalletProtocols hook provides a React-friendly interface to detect which DeFi protocols a connected wallet has active positions in. It automatically fetches and updates when the wallet address changes. Location: src/hooks/useWalletProtocols.js

Import

import { useWalletProtocols } from '../hooks/useWalletProtocols';

Signature

function useWalletProtocols(address: string | null): {
  walletProtocols: Array<WalletProtocol>;
  loading: boolean;
  error: string | null;
}

Parameters

address
string | null
required
Stacks wallet address to analyze. Pass null or empty string when wallet is disconnected.Example: SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7

Return Values

walletProtocols
Array<WalletProtocol>
required
Array of detected protocol positions
loading
boolean
required
true while fetching wallet data, false once complete
error
string | null
required
Error message if detection failed, otherwise null

Usage Examples

import { useWalletProtocols } from '../hooks/useWalletProtocols';

function WalletDashboard({ walletAddress }) {
  const { walletProtocols, loading, error } = useWalletProtocols(walletAddress);

  if (loading) return <div>Scanning wallet...</div>;
  if (error) return <div>Error: {error}</div>;
  if (walletProtocols.length === 0) {
    return <div>No DeFi positions found</div>;
  }

  return (
    <div>
      <h2>Your Active Positions ({walletProtocols.length})</h2>
      {walletProtocols.map(protocol => (
        <div key={protocol.id} style={{ borderLeft: `4px solid ${protocol.color}` }}>
          <h3>{protocol.name}</h3>
          <p>{protocol.description}</p>
          <p>Balance: {protocol.balanceNum.toFixed(6)} {protocol.asset}</p>
          <span>{protocol.confidence === 'confirmed' ? '✓ Confirmed' : '~ Likely'}</span>
        </div>
      ))}
    </div>
  );
}

Features

Auto-Update

Automatically re-scans when wallet address changes

Empty State

Returns empty array when no address provided (graceful handling)

Error Recovery

Captures API errors without crashing the component

Two-Factor Detection

Checks both token balances AND transaction history for accuracy

Behavior

Address Changes

The hook automatically re-runs detection when the address parameter changes:
useEffect(() => {
  if (!address) {
    setWalletProtocols([]);
    return;
  }
  // ... fetch positions
}, [address]); // Re-run when address changes

No Address Provided

When address is null, undefined, or empty string:
  • Returns [] for walletProtocols
  • Sets loading to false
  • No API calls made

Loading States

  1. Initial: loading = false, walletProtocols = []
  2. Fetching: loading = true
  3. Success: loading = false, walletProtocols = [...]
  4. Error: loading = false, error = "message"

Detection Methods

Token Balance Check

Scans wallet for protocol-specific LP or receipt tokens:
const tokenBalance = tokenBalances[tokenKey]?.balance ?? '0';
const hasToken = BigInt(tokenBalance) > 0n;
Result: confidence = "confirmed"

Transaction History Check

Analyzes last 50 transactions for protocol contract interactions:
const hasInteracted = [...interactedContracts].some(id =>
  id.startsWith(contractAddr)
);
Result: confidence = "likely"

Implementation Details

Source Code

import { useState, useEffect } from 'react';
import { detectWalletProtocols } from '../services/portfolioProtocols';

export function useWalletProtocols(address) {
    const [walletProtocols, setWalletProtocols] = useState([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);

    useEffect(() => {
        if (!address) {
            setWalletProtocols([]);
            return;
        }
        setLoading(true);
        setError(null);
        detectWalletProtocols(address)
            .then(setWalletProtocols)
            .catch(e => setError(e.message))
            .finally(() => setLoading(false));
    }, [address]);

    return { walletProtocols, loading, error };
}

Supported Protocols

The hook can detect positions in these Stacks DeFi protocols:
Type: Stacking
Token: stSTX (liquid staking receipt)
Contract: SP4SZE494VC2YC5JYG7AYFQ44F5Q4PYV7DVMDPBG.ststx-token
Type: Lending
Token: zsBTC (lending position)
Contract: SP2VCQJGH7PHP2DJK7Z0V48AGBHQAW3R3ZW1QF4N.zest-reward-dist
Type: DEX / Yield
Token: atALEX (auto-compounding)
Contract: SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.auto-alex-v3
Type: DEX LP
Token: stxSTX-LP (liquidity pool)
Contract: STTWD9SPRQVD3P733V89SV0P8EP8QSB5B00ZBZQ.stxstx-lp-token-v-1-2
Type: Yield
Token: USDh (yield-bearing stablecoin)
Contract: SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1N.token-usdh
Type: DEX LP
Token: WELSH-LP (liquidity pool)
Contract: SP1Y5YSTAHZ88XYK1VPDH24GY0HPX5J4JECTMY4A1.wstx-welsh-lp-token
Type: Borrowing
Token: sBTC Collateral
Contract: SP2XD7417HGPRTREMKF748VNEQPDRR0RMANB7X1N.granite-vault

Performance

Initial Detection: ~1-2s for parallel API calls (balances + transactions)
Subsequent Updates: Triggered only when address changes
No Auto-Refresh: Hook does not poll for updates (call with new address to refresh)

Comparison with Service

FeatureuseWalletProtocols HookdetectWalletProtocols Service
UsageReact componentsAny JavaScript context
State ManagementBuilt-in with useStateManual
Address ChangesAuto-detects via useEffectManual re-call
Error HandlingExposed as stateTry/catch required
Loading StateBuilt-inManual

Common Patterns

Wallet Connection Integration

import { useConnect } from '@stacks/connect-react';
import { useWalletProtocols } from '../hooks/useWalletProtocols';

function App() {
  const { authenticate, userSession } = useConnect();
  const address = userSession?.loadUserData()?.profile?.stxAddress?.mainnet;
  
  const { walletProtocols, loading } = useWalletProtocols(address);

  return (
    <div>
      {!address ? (
        <button onClick={() => authenticate()}>Connect Wallet</button>
      ) : (
        <div>
          <p>Positions: {walletProtocols.length}</p>
        </div>
      )}
    </div>
  );
}

Portfolio Value Calculation

import { useWalletProtocols } from '../hooks/useWalletProtocols';
import { useProtocolData } from '../hooks/useProtocolData';

function PortfolioValue({ address }) {
  const { walletProtocols } = useWalletProtocols(address);
  const { protocols } = useProtocolData();

  // Calculate total value (simplified - would need price data)
  const positions = walletProtocols.filter(wp => wp.hasToken);
  
  return (
    <div>
      <h2>Portfolio Summary</h2>
      <p>Active Positions: {positions.length}</p>
      <p>Protocols Used: {new Set(positions.map(p => p.id)).size}</p>
    </div>
  );
}

Portfolio Protocols Service

Underlying service that detects wallet positions

useProtocolData Hook

Fetch protocol TVL and APY data

Build docs developers (and LLMs) love