Skip to main content

Wallet Connection Guide

GweAI uses Dynamic for advanced wallet authentication, providing seamless multi-wallet support with account abstraction capabilities.

Overview

Dynamic enables:
  • Multiple wallet connectors (MetaMask, Coinbase, WalletConnect)
  • Email/social authentication (no wallet required)
  • Account abstraction (gasless transactions)
  • Multi-chain support (Base Sepolia, BSC Testnet)
  • Session management (persistent authentication)
No Wallet? No Problem! Dynamic allows you to create a smart contract wallet using just your email or social accounts.

Supported Wallets

GweAI supports all major Ethereum-compatible wallets:

MetaMask

Most popular browser extension wallet with extensive dApp support

Coinbase Wallet

Integrated Coinbase smart wallet with seamless authentication

WalletConnect

Connect any mobile wallet via QR code (Trust Wallet, Rainbow, etc.)

Brave Wallet

Built-in wallet for Brave browser users

Embedded Wallet

Create wallet with email - no extension needed

Social Login

Authenticate with Google, Twitter, Discord

Installation & Setup

Install Dependencies

GweAI uses these Dynamic packages:
npm install @dynamic-labs/sdk-react-core \
            @dynamic-labs/ethereum \
            @dynamic-labs/wallet-connector-core
Versions used:
{
  "@dynamic-labs/sdk-react-core": "^4.45.1",
  "@dynamic-labs/ethereum": "^4.45.1",
  "@dynamic-labs/wallet-connector-core": "^4.45.1"
}

Environment Configuration

Set up your Dynamic environment ID:
# .env
VITE_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
1
Get Your Dynamic Environment ID
2
  • Sign up at Dynamic Dashboard
  • Create a new project
  • Copy your Environment ID from the dashboard
  • Add it to your .env file
  • 3
    Configure Networks
    4
    Dynamic needs to know which networks your app supports (Base Sepolia, BSC Testnet, etc.)

    Dynamic Provider Setup

    Here’s how GweAI configures Dynamic in main.tsx:
    import ReactDOM from 'react-dom/client'
    import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
    import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
    import App from './App.tsx'
    
    const queryClient = new QueryClient();
    
    ReactDOM.createRoot(document.getElementById('root')!).render(
      <DynamicContextProvider
        settings={{
          environmentId: import.meta.env.VITE_DYNAMIC_ENVIRONMENT_ID,
          walletConnectors: [EthereumWalletConnectors],
          
          // Use connect-and-sign for authentication support
          initialAuthenticationMode: 'connect-and-sign',
          
          // Recommended wallets configuration
          recommendedWallets: [
            { walletKey: 'metamask' },
            { walletKey: 'coinbase' },
            { walletKey: 'walletconnect' },
          ],
          
          // Privacy settings for faster load
          privacyPolicyUrl: undefined,
          termsOfServiceUrl: undefined,
          
          // Network overrides - completely override default networks
          overrides: {
            evmNetworks: () => [
              // Base Sepolia
              {
                blockExplorerUrls: ['https://sepolia.basescan.org'],
                chainId: 84532,
                chainName: 'Base Sepolia',
                iconUrls: ['https://avatars.githubusercontent.com/u/108554348?s=280&v=4'],
                name: 'Base Sepolia Testnet',
                nativeCurrency: {
                  decimals: 18,
                  name: 'ETH',
                  symbol: 'ETH',
                },
                networkId: 84532,
                rpcUrls: ['https://base-sepolia.g.alchemy.com/v2/-mGklZw8tTiO9fg9sRGQP'],
                vanityName: 'Base Sepolia',
              },
              // BNB Smart Chain Testnet
              {
                blockExplorerUrls: ['https://testnet.bscscan.com'],
                chainId: 97,
                chainName: 'BSC Testnet',
                iconUrls: ['https://s2.coinmarketcap.com/static/img/coins/64x64/1839.png'],
                name: 'Binance Smart Chain Testnet',
                nativeCurrency: {
                  decimals: 18,
                  name: 'tBNB',
                  symbol: 'tBNB',
                },
                networkId: 97,
                rpcUrls: [
                  'https://data-seed-prebsc-1-s1.bnbchain.org:8545',
                  'https://bsc-testnet.publicnode.com'
                ],
                vanityName: 'BSC Testnet',
              },
            ],
          },
          
          // Event listeners for wallet actions
          events: {
            onAuthSuccess: (args) => {
              // Store user ID for session management
              if (args.user?.userId) {
                localStorage.setItem('dynamic_user_id', args.user.userId);
              }
              console.log('✅ Auth successful:', args.user?.email || args.primaryWallet?.address);
            },
            
            onAuthFailure: (error) => {
              console.error('❌ Auth failed:', error || 'Unknown error');
            },
            
            onLogout: () => {
              localStorage.removeItem('dynamic_user_id');
            },
            
            onEmbeddedWalletCreated: () => {
              console.log('✅ Embedded wallet created');
            },
          },
        }}
      >
        <QueryClientProvider client={queryClient}>
          <App />
        </QueryClientProvider>
      </DynamicContextProvider>,
    )
    

    Key Configuration Options

    connect-and-sign: Requires users to sign a message after connecting for enhanced security.
    initialAuthenticationMode: 'connect-and-sign'
    
    This adds an extra layer of authentication beyond just wallet connection.
    Prioritizes specific wallets in the connection modal:
    recommendedWallets: [
      { walletKey: 'metamask' },
      { walletKey: 'coinbase' },
      { walletKey: 'walletconnect' },
    ]
    
    These wallets appear at the top of the connection interface.
    Completely replaces Dynamic’s default network list:
    overrides: {
      evmNetworks: () => [
        // Your custom networks
      ]
    }
    
    Important: This overrides ALL defaults. Only your specified networks will be available.
    React to wallet lifecycle events:
    events: {
      onAuthSuccess: (args) => {
        // User successfully authenticated
      },
      onAuthFailure: (error) => {
        // Authentication failed
      },
      onLogout: () => {
        // User disconnected wallet
      },
      onEmbeddedWalletCreated: () => {
        // Email wallet created
      }
    }
    

    Network Configuration

    Base Sepolia (Primary Testnet)

    {
      chainId: 84532,
      chainName: 'Base Sepolia',
      blockExplorerUrls: ['https://sepolia.basescan.org'],
      rpcUrls: ['https://base-sepolia.g.alchemy.com/v2/-mGklZw8tTiO9fg9sRGQP'],
      nativeCurrency: {
        name: 'ETH',
        symbol: 'ETH',
        decimals: 18
      }
    }
    
    Base Sepolia is GweAI’s primary testnet for development and testing.

    BSC Testnet (Cross-Chain Support)

    {
      chainId: 97,
      chainName: 'BSC Testnet',
      blockExplorerUrls: ['https://testnet.bscscan.com'],
      rpcUrls: [
        'https://data-seed-prebsc-1-s1.bnbchain.org:8545',
        'https://bsc-testnet.publicnode.com'
      ],
      nativeCurrency: {
        name: 'tBNB',
        symbol: 'tBNB',
        decimals: 18
      }
    }
    
    BSC Testnet enables cross-chain compatibility testing.
    Auto Network Switching: Dynamic automatically prompts users to switch networks if they’re on the wrong chain.

    Using Dynamic in Components

    Access wallet state and functions using Dynamic’s React hooks:
    import { DynamicUserProfile } from '@dynamic-labs/sdk-react-core';
    import App from './App.tsx';
    
    function App() {
      return (
        <>
          <App />
          {/* Dynamic User Profile - Required for wallet export */}
          <DynamicUserProfile variant="modal" />
        </>
      );
    }
    

    Get Wallet Information

    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
    
    function MyComponent() {
      const { 
        primaryWallet,      // Currently connected wallet
        user,              // User information
        isAuthenticated,   // Authentication status
        setShowAuthFlow,   // Open connection modal
      } = useDynamicContext();
      
      const address = primaryWallet?.address;
      const isConnected = isAuthenticated && !!primaryWallet;
      
      return (
        <div>
          {isConnected ? (
            <p>Connected: {address}</p>
          ) : (
            <button onClick={() => setShowAuthFlow(true)}>
              Connect Wallet
            </button>
          )}
        </div>
      );
    }
    

    Sign Messages

    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
    
    function SignMessage() {
      const { primaryWallet } = useDynamicContext();
      
      const signMessage = async () => {
        if (!primaryWallet) return;
        
        const message = 'Hello from GweAI!';
        const signature = await primaryWallet.connector.signMessage(message);
        
        console.log('Signature:', signature);
      };
      
      return <button onClick={signMessage}>Sign Message</button>;
    }
    

    Execute Transactions

    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
    import { ethers } from 'ethers';
    
    function SendTransaction() {
      const { primaryWallet } = useDynamicContext();
      
      const sendTx = async () => {
        if (!primaryWallet) return;
        
        const provider = await primaryWallet.connector.getPublicClient();
        
        const tx = {
          to: '0x...',
          value: ethers.parseEther('0.01'),
          data: '0x'
        };
        
        const txHash = await primaryWallet.connector.sendTransaction(tx);
        console.log('Transaction:', txHash);
      };
      
      return <button onClick={sendTx}>Send Transaction</button>;
    }
    

    Wallet Storage Management

    GweAI implements wallet-specific data isolation:
    import { useEffect } from 'react';
    import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
    
    export function useWalletStorageManager() {
      const { primaryWallet, isAuthenticated } = useDynamicContext();
      const address = primaryWallet?.address;
      
      useEffect(() => {
        if (!isAuthenticated || !address) {
          // Clear session data on disconnect
          sessionStorage.clear();
          return;
        }
        
        // Load wallet-specific data
        const walletData = localStorage.getItem(`wallet_${address}`);
        if (walletData) {
          // Restore wallet-specific state
          const data = JSON.parse(walletData);
          // ... apply data to app state
        }
      }, [address, isAuthenticated]);
    }
    
    This ensures:
    • Data isolation between different wallets
    • Automatic cleanup on disconnect
    • Persistent sessions across page reloads

    Connection Flow

    Here’s the complete user journey:
    1
    User Clicks Connect
    2
    The setShowAuthFlow(true) function opens the Dynamic modal.
    3
    <button onClick={() => setShowAuthFlow(true)}>
      Connect Wallet
    </button>
    
    4
    Select Wallet
    5
    User chooses from:
    6
  • MetaMask
  • Coinbase Wallet
  • WalletConnect
  • Email/Social (embedded wallet)
  • 7
    Approve Connection
    8
    Wallet prompts user to approve the connection request.
    9
    Sign Authentication Message
    10
    Because initialAuthenticationMode: 'connect-and-sign', user must sign a message:
    11
    Sign this message to authenticate with GweAI
    
    Nonce: abc123...
    
    12
    Authentication Success
    13
    The onAuthSuccess event fires:
    14
    onAuthSuccess: (args) => {
      localStorage.setItem('dynamic_user_id', args.user.userId);
      console.log('✅ Connected:', args.primaryWallet.address);
    }
    
    15
    Verify Network
    16
    Dynamic checks if the user is on Base Sepolia (chainId: 84532). If not, it prompts them to switch.
    17
    Ready to Trade
    18
    User can now access all GweAI features: DCA strategies, AI trading, portfolio management.

    Advanced Features

    Multi-Wallet Support

    Users can connect multiple wallets simultaneously:
    const { wallets } = useDynamicContext();
    
    // List all connected wallets
    wallets.forEach(wallet => {
      console.log(wallet.address, wallet.connector.name);
    });
    

    Embedded Wallets (Email/Social)

    Users without crypto wallets can create one using email:
    // Dynamic handles embedded wallet creation automatically
    onEmbeddedWalletCreated: () => {
      console.log('✅ Email wallet created');
      // Wallet is immediately usable
    }
    
    Benefits:
    • No browser extension required
    • Familiar email/social login UX
    • Full Web3 functionality
    • Account recovery via email

    Account Abstraction

    Dynamic supports ERC-4337 account abstraction:
    // Gasless transactions (coming soon)
    const txHash = await primaryWallet.connector.sendTransaction({
      to: contractAddress,
      data: encodedData,
      gasless: true  // Sponsor gas fees
    });
    

    Session Management

    Dynamic persists sessions across page reloads:
    useEffect(() => {
      // Session automatically restored on page load
      if (isAuthenticated) {
        console.log('Session restored for:', primaryWallet.address);
      }
    }, [isAuthenticated]);
    

    Troubleshooting

    Check:
    1. Environment ID is correct in .env
    2. DynamicContextProvider wraps your app
    3. setShowAuthFlow(true) is being called
    // Verify environment ID
    console.log(import.meta.env.VITE_DYNAMIC_ENVIRONMENT_ID);
    
    Solution:Make sure Base Sepolia (chainId: 84532) is in your evmNetworks override:
    overrides: {
      evmNetworks: () => [
        {
          chainId: 84532,
          // ... rest of config
        }
      ]
    }
    
    Dynamic will automatically prompt network switch.
    Cause: connect-and-sign mode requires signature on every new session.Solutions:
    1. Sign the message to complete authentication
    2. Check for errors in browser console
    3. Try refreshing and reconnecting
    To disable (not recommended for production):
    initialAuthenticationMode: 'connect-only'
    
    Check:
    1. Cookies are enabled
    2. LocalStorage is not blocked
    3. Not in private/incognito mode
    // Debug session storage
    console.log('User ID:', localStorage.getItem('dynamic_user_id'));
    
    Common causes:
    • RPC endpoint rate limits
    • Network congestion
    • Invalid RPC URL
    Solutions:
    1. Use multiple RPC endpoints with fallbacks
    2. Get your own Alchemy/Infura API key
    3. Check network status on Chainlist
    rpcUrls: [
      'https://base-sepolia.g.alchemy.com/v2/YOUR_KEY',
      'https://base-sepolia.blockpi.network/v1/rpc/public',
      'https://base-sepolia-rpc.publicnode.com'
    ]
    

    Security Best Practices

    Never commit your Dynamic Environment ID to public repositories! Use environment variables.

    Environment Variables

    # .env (DO NOT COMMIT)
    VITE_DYNAMIC_ENVIRONMENT_ID=your_secret_id
    
    # .env.example (safe to commit)
    VITE_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id_here
    

    Sign-In Verification

    Always use connect-and-sign for production:
    initialAuthenticationMode: 'connect-and-sign'
    
    This prevents attackers from connecting arbitrary wallets.

    User Data Handling

    onAuthSuccess: (args) => {
      // Only store necessary data
      const safeData = {
        userId: args.user?.userId,
        address: args.primaryWallet?.address
      };
      
      // Never store private keys or sensitive info
      localStorage.setItem('user', JSON.stringify(safeData));
    }
    

    What’s Next?

    Quick Start

    Complete the full setup flow from connection to first trade

    DCA Strategies

    Learn how to create automated investment strategies

    Dynamic Documentation

    Explore Dynamic’s complete documentation

    Wallet Integration

    Advanced wallet integration patterns

    Need Help? Join our Discord community or check the Dynamic Support resources.

    Build docs developers (and LLMs) love