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
Get Your Dynamic Environment ID
Sign up at Dynamic Dashboard
Create a new project
Copy your Environment ID from the dashboard
Add it to your .env file
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
initialAuthenticationMode
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" />
</>
);
}
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:
The setShowAuthFlow(true) function opens the Dynamic modal.
< button onClick = {() => setShowAuthFlow ( true )} >
Connect Wallet
</ button >
MetaMask
Coinbase Wallet
WalletConnect
Email/Social (embedded wallet)
Wallet prompts user to approve the connection request.
Sign Authentication Message
Because initialAuthenticationMode: 'connect-and-sign', user must sign a message:
Sign this message to authenticate with GweAI
Nonce: abc123...
The onAuthSuccess event fires:
onAuthSuccess : ( args ) => {
localStorage . setItem ( 'dynamic_user_id' , args . user . userId );
console . log ( '✅ Connected:' , args . primaryWallet . address );
}
Dynamic checks if the user is on Base Sepolia (chainId: 84532). If not, it prompts them to switch.
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
Connection Modal Not Appearing
Check:
Environment ID is correct in .env
DynamicContextProvider wraps your app
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.
Signature Request Keeps Appearing
Cause : connect-and-sign mode requires signature on every new session.Solutions:
Sign the message to complete authentication
Check for errors in browser console
Try refreshing and reconnecting
To disable (not recommended for production):initialAuthenticationMode : 'connect-only'
Check:
Cookies are enabled
LocalStorage is not blocked
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:
Use multiple RPC endpoints with fallbacks
Get your own Alchemy/Infura API key
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.