Neon Module
The neon module provides Neon blockchain authentication and balance checking capabilities.
Import
const neon = await brdzSDK.neon;
Methods Overview (2 methods)
| Method | Description | Auth Required | HTTP Endpoint |
|---|---|---|---|
signInWithNeon | Verify wallet signature | ❌ | POST /neon/signin |
getBalance | Get native and USDC balance | ❌ | GET /neon/balance/:wallet_address |
Configuration
// Configure SDK
const config = await brdzSDK.config;
config.setBaseUrl('https://api.brdz.link/api');
config.setApiKey('your_api_key'); // Only API key required, no JWT token
Methods
signInWithNeon
Verify Neon wallet signature for authentication purposes.
Syntax:
await neon.signInWithNeon(payload)
Parameters:
payload(object): Signature verification datawallet_address(string): Ethereum wallet addressmessage(string): Original message that was signedsignature(string): MetaMask signature of the message
Returns:
{
success: true,
wallet_address: "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
message: "Signature verified successfully"
}
Example:
try {
// Step 1: Connect to MetaMask
if (!window.ethereum) {
throw new Error('MetaMask not installed');
}
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
const walletAddress = accounts[0];
// Step 2: Create message to sign
const timestamp = new Date().toISOString();
const nonce = Math.random().toString(36).substring(7);
const message = `Sign in to BRDZ with Neon wallet
Timestamp: ${timestamp}
Nonce: ${nonce}
Address: ${walletAddress}`;
// Step 3: Sign message with MetaMask
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, walletAddress]
});
// Step 4: Verify signature with API
const result = await neon.signInWithNeon({
wallet_address: walletAddress,
message: message,
signature: signature
});
if (result.success) {
console.log('✅ Signature verified for:', result.wallet_address);
localStorage.setItem('neon_wallet', result.wallet_address);
return result;
} else {
console.error('❌ Signature verification failed');
}
} catch (error) {
console.error('Sign in failed:', error.message);
}
Use Cases:
- Wallet-based authentication without passwords
- Verify wallet ownership for sensitive operations
- Implement "Sign in with Wallet" functionality
Notes:
- Does not return JWT tokens - only verification result
- Signature verification done server-side using ethers.js
- Message should include timestamp and nonce for security
getBalance
Get native NEON tokens and USDC balance for a wallet address.
Syntax:
await neon.getBalance(wallet_address)
Parameters:
wallet_address(string): Ethereum wallet address (will be validated and converted to checksum format)
Returns:
{
chain: "neon",
address: "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
wallet_address: "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
native_balance: "125.456789123456789",
usdc_balance: "1000.0"
}
Example:
try {
const walletAddress = '0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B';
const balance = await neon.getBalance(walletAddress);
console.log('Balance Details:');
console.log(`Chain: ${balance.chain}`);
console.log(`Address: ${balance.address}`);
console.log(`Native NEON: ${balance.native_balance}`);
console.log(`USDC: ${balance.usdc_balance}`);
// Format for display
const neonFormatted = parseFloat(balance.native_balance).toFixed(6);
const usdcFormatted = parseFloat(balance.usdc_balance).toFixed(2);
console.log(`Formatted - NEON: ${neonFormatted}, USDC: ${usdcFormatted}`);
// Check if user has sufficient balance for operations
const hasNeonBalance = parseFloat(balance.native_balance) > 0.001; // Minimum for gas
const hasUsdcBalance = parseFloat(balance.usdc_balance) > 0;
console.log(`Has NEON for gas: ${hasNeonBalance}`);
console.log(`Has USDC: ${hasUsdcBalance}`);
return balance;
} catch (error) {
if (error.message.includes('Invalid wallet address')) {
console.error('❌ Please provide a valid Ethereum address');
} else if (error.message.includes('Failed to fetch balance')) {
console.error('❌ Network error - please try again');
} else {
console.error('❌ Balance fetch failed:', error.message);
}
}
Balance Properties:
chain: Always "neon" for Neon networkaddress: Checksum-formatted wallet addresswallet_address: Same as address (backward compatibility)native_balance: NEON tokens with full precision (18 decimals)usdc_balance: USDC tokens formatted to readable units
Use Cases:
- Check wallet balance before transactions
- Display user portfolio in UI
- Validate sufficient balance for operations
- Monitor balance changes over time
Complete Integration Example
class NeonWalletManager {
constructor() {
this.neon = null;
this.walletAddress = null;
this.isConnected = false;
this.init();
}
async init() {
// Initialize SDK
const config = await brdzSDK.config;
config.setBaseUrl('https://api.brdz.link/api');
config.setApiKey('your_api_key');
this.neon = await brdzSDK.neon;
}
async connectWallet() {
try {
if (!window.ethereum) {
throw new Error('MetaMask not installed. Please install MetaMask to continue.');
}
// Request account access
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
if (accounts.length === 0) {
throw new Error('No accounts found. Please unlock MetaMask.');
}
this.walletAddress = accounts[0];
this.isConnected = true;
console.log('✅ Wallet connected:', this.walletAddress);
return this.walletAddress;
} catch (error) {
console.error('❌ Wallet connection failed:', error.message);
throw error;
}
}
async signIn() {
if (!this.isConnected) {
await this.connectWallet();
}
try {
// Create secure message
const timestamp = new Date().toISOString();
const nonce = Math.random().toString(36).substring(7);
const message = `Sign in to BRDZ with Neon wallet
Timestamp: ${timestamp}
Nonce: ${nonce}
Address: ${this.walletAddress}`;
// Request signature
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, this.walletAddress]
});
// Verify with API
const result = await this.neon.signInWithNeon({
wallet_address: this.walletAddress,
message: message,
signature: signature
});
if (result.success) {
console.log('✅ Authentication successful');
localStorage.setItem('neon_auth_wallet', result.wallet_address);
localStorage.setItem('neon_auth_time', timestamp);
return result;
} else {
throw new Error('Signature verification failed');
}
} catch (error) {
console.error('❌ Sign in failed:', error.message);
throw error;
}
}
async getBalance() {
if (!this.walletAddress) {
throw new Error('Wallet not connected');
}
try {
const balance = await this.neon.getBalance(this.walletAddress);
// Store latest balance
this.lastBalance = balance;
this.lastBalanceTime = new Date().toISOString();
return balance;
} catch (error) {
console.error('❌ Balance fetch failed:', error.message);
throw error;
}
}
async refreshBalance() {
return await this.getBalance();
}
formatBalance(balance) {
return {
neon: parseFloat(balance.native_balance).toFixed(6),
usdc: parseFloat(balance.usdc_balance).toFixed(2),
hasGas: parseFloat(balance.native_balance) > 0.001,
hasUsdc: parseFloat(balance.usdc_balance) > 0
};
}
disconnect() {
this.walletAddress = null;
this.isConnected = false;
this.lastBalance = null;
localStorage.removeItem('neon_auth_wallet');
localStorage.removeItem('neon_auth_time');
console.log('🔓 Wallet disconnected');
}
// Monitor balance changes
async startBalanceMonitoring(intervalSeconds = 30) {
if (this.balanceInterval) {
clearInterval(this.balanceInterval);
}
this.balanceInterval = setInterval(async () => {
try {
const newBalance = await this.getBalance();
if (this.lastBalance) {
const prevNeon = parseFloat(this.lastBalance.native_balance);
const newNeon = parseFloat(newBalance.native_balance);
const prevUsdc = parseFloat(this.lastBalance.usdc_balance);
const newUsdc = parseFloat(newBalance.usdc_balance);
if (Math.abs(newNeon - prevNeon) > 0.001) {
const change = newNeon - prevNeon;
console.log(`🔄 NEON balance changed: ${change > 0 ? '+' : ''}${change.toFixed(6)}`);
}
if (Math.abs(newUsdc - prevUsdc) > 0.01) {
const change = newUsdc - prevUsdc;
console.log(`💰 USDC balance changed: ${change > 0 ? '+' : ''}${change.toFixed(2)}`);
}
}
} catch (error) {
console.error('Balance monitoring error:', error.message);
}
}, intervalSeconds * 1000);
console.log(`📊 Started balance monitoring (${intervalSeconds}s interval)`);
}
stopBalanceMonitoring() {
if (this.balanceInterval) {
clearInterval(this.balanceInterval);
this.balanceInterval = null;
console.log('⏹️ Stopped balance monitoring');
}
}
}
// Usage
const walletManager = new NeonWalletManager();
// Connect and authenticate
try {
await walletManager.connectWallet();
const authResult = await walletManager.signIn();
console.log('Authenticated:', authResult);
// Get and display balance
const balance = await walletManager.getBalance();
const formatted = walletManager.formatBalance(balance);
console.log(`NEON: ${formatted.neon} (Gas available: ${formatted.hasGas})`);
console.log(`USDC: ${formatted.usdc} (Has balance: ${formatted.hasUsdc})`);
// Start monitoring
walletManager.startBalanceMonitoring(30);
} catch (error) {
console.error('Setup failed:', error.message);
}
Network Configuration
Neon DevNet Details
- Chain ID: 245022926 (0xE9AC0CE in hex)
- Native Token: NEON
- RPC URL: https://245022926.rpc.thirdweb.com/
- Explorer: https://neon-devnet.blockscout.com/
MetaMask Network Setup
// Add Neon network to MetaMask
async function addNeonNetwork() {
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: '0xE9AC0CE', // 245022926 in hex
chainName: 'Neon EVM DevNet',
nativeCurrency: {
name: 'NEON',
symbol: 'NEON',
decimals: 18
},
rpcUrls: ['https://245022926.rpc.thirdweb.com/'],
blockExplorerUrls: ['https://neon-devnet.blockscout.com/']
}]
});
console.log('✅ Neon network added to MetaMask');
} catch (error) {
console.error('❌ Failed to add network:', error);
}
}
Error Handling
Common Error Patterns
try {
const result = await neon.signInWithNeon(payload);
} catch (error) {
if (error.message.includes('Missing wallet_address')) {
console.error('Wallet address is required');
} else if (error.message.includes('Signature verification failed')) {
console.error('Invalid signature - please try signing again');
} else if (error.message.includes('User denied message signature')) {
console.error('User cancelled the signature request');
} else {
console.error('Unexpected error:', error.message);
}
}
try {
const balance = await neon.getBalance(walletAddress);
} catch (error) {
if (error.message.includes('Invalid wallet address')) {
console.error('Please provide a valid Ethereum address');
} else if (error.message.includes('Failed to fetch balance')) {
console.error('Network error - please check your connection');
} else {
console.error('Balance fetch failed:', error.message);
}
}
Address Validation
// Validate address before API calls
function validateEthereumAddress(address) {
if (!address || typeof address !== 'string') {
throw new Error('Address must be a string');
}
if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
throw new Error('Invalid Ethereum address format');
}
return address.toLowerCase();
}
// Safe balance checking
async function safeGetBalance(walletAddress) {
try {
const validAddress = validateEthereumAddress(walletAddress);
const balance = await neon.getBalance(validAddress);
return balance;
} catch (error) {
console.error('Safe balance check failed:', error.message);
return null;
}
}
Security Best Practices
Message Signing Security
// Create secure sign-in message
function createSecureMessage(walletAddress) {
const timestamp = new Date().toISOString();
const nonce = Math.random().toString(36).substring(7);
return `Sign in to BRDZ with Neon wallet
Address: ${walletAddress}
Timestamp: ${timestamp}
Nonce: ${nonce}
Chain: Neon EVM DevNet (245022926)
Action: Authentication`;
}
Best Practices
- Include Timestamp: Prevent replay attacks
- Add Nonce: Ensure message uniqueness
- Specify Chain: Prevent cross-chain attacks
- Clear Purpose: Indicate what's being signed
- Never Request Private Keys: Use MetaMask signing only
- Validate Server-Side: Always verify signatures on backend
- Rate Limiting: Implement limits on sign-in attempts
Balance Precision
Native NEON Balance
- Precision: 18 decimals (like ETH)
- Display: Usually show 6 decimal places
- Gas Requirements: Minimum ~0.001 NEON for transactions
USDC Balance
- Precision: Variable (check contract decimals)
- Display: Usually show 2 decimal places
- Minimum: Depends on contract implementation
// Format balances for display
function formatBalanceForDisplay(balance) {
const neonBalance = parseFloat(balance.native_balance);
const usdcBalance = parseFloat(balance.usdc_balance);
return {
neon: {
full: balance.native_balance,
display: neonBalance.toFixed(6),
hasMinimum: neonBalance > 0.001
},
usdc: {
full: balance.usdc_balance,
display: usdcBalance.toFixed(2),
hasBalance: usdcBalance > 0
}
};
}