Skip to main content

Neon Module

The neon module provides Neon blockchain authentication and balance checking capabilities.

Import

const neon = await brdzSDK.neon;

Methods Overview (2 methods)

MethodDescriptionAuth RequiredHTTP Endpoint
signInWithNeonVerify wallet signaturePOST /neon/signin
getBalanceGet native and USDC balanceGET /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 data
    • wallet_address (string): Ethereum wallet address
    • message (string): Original message that was signed
    • signature (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 network
  • address: Checksum-formatted wallet address
  • wallet_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

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

  1. Include Timestamp: Prevent replay attacks
  2. Add Nonce: Ensure message uniqueness
  3. Specify Chain: Prevent cross-chain attacks
  4. Clear Purpose: Indicate what's being signed
  5. Never Request Private Keys: Use MetaMask signing only
  6. Validate Server-Side: Always verify signatures on backend
  7. 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
}
};
}