Neon API - Wallet Authentication & Balance
Neon EVM blockchain integration for wallet-based authentication and balance checking. Supports MetaMask signature verification and real-time balance queries for native NEON tokens and USDC.
Key Features:
- Signature-based Authentication: MetaMask wallet verification using ethers.js
- Dual Balance Checking: Native NEON tokens + USDC token balances
- Address Validation: Automatic checksum formatting and validation
- Neon Devnet Integration: Direct RPC connection to Neon blockchain
Network Configuration
Neon Devnet Details
- Chain ID: 245022926
- Native Token: NEON
- RPC URL: https://245022926.rpc.thirdweb.com/
- Explorer: https://neon-devnet.blockscout.com/
- Network Type: Ethereum Virtual Machine (EVM) compatible
Sign In with Neon Wallet
Sign In with Neon Wallet
Authenticate user using MetaMask wallet signature verification. Validates the signature against the provided message and wallet address using ethers.js verifyMessage function.
Parameters
wallet_addressstringrequiredEthereum wallet address (will be converted to checksum format)
signaturestringrequiredMetaMask signature of the message
messagestringrequiredOriginal message that was signed
Request Body
{
"wallet_address": "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
"signature": "0x8f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c",
"message": "Sign in to BRDZ with Neon wallet\nTimestamp: 2024-01-15T10:30:00Z\nNonce: abc123"
}Response
{
"success": true,
"wallet_address": "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
"message": "Signature verified successfully"
}{
"error": "Missing wallet_address, signature, or message"
}{
"error": "Signature verification failed"
}{
"error": "Internal server error"
}curl -X POST https://api.brdz.link/api/neon/signin \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"wallet_address": "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
"signature": "0x8f9b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c",
"message": "Sign in to BRDZ with Neon wallet\nTimestamp: 2024-01-15T10:30:00Z\nNonce: abc123"
}'Get Neon Balance
Get Neon Wallet Balance
Retrieve native NEON token balance and USDC token balance for a specified wallet address. Automatically validates and formats the address to proper checksum format.
Parameters
wallet_addressstringrequiredEthereum wallet address (path parameter) - will be validated and converted to checksum format
Response
{
"chain": "neon",
"address": "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
"wallet_address": "0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B",
"native_balance": "125.456789123456789",
"usdc_balance": "1000.0"
}{
"error": "Invalid wallet address format"
}{
"error": "Failed to fetch balance",
"details": "RPC connection timeout"
}{
"error": "Invalid wallet address"
}curl -X GET https://api.brdz.link/api/neon/balance/0x742d35Cc6645C0532F29e3BB0B4b7c0532F29e3B \
-H "x-api-key: YOUR_API_KEY"Complete Neon Integration
MetaMask Integration Example
class NeonWalletIntegration {
constructor() {
this.isConnected = false;
this.walletAddress = null;
this.chainId = '0xE9AC0CE'; // 245022926 in hex
}
async checkMetaMaskAvailability() {
if (typeof window.ethereum === 'undefined') {
throw new Error('MetaMask is not installed. Please install MetaMask to continue.');
}
return true;
}
async addNeonNetwork() {
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: this.chainId,
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 successfully');
} catch (error) {
if (error.code === 4902) {
console.log('Neon network already added or rejected by user');
} else {
console.error('❌ Failed to add Neon network:', error);
throw error;
}
}
}
async switchToNeonNetwork() {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: this.chainId }]
});
console.log('✅ Switched to Neon network');
} catch (error) {
if (error.code === 4902) {
// Network not added yet, try to add it
await this.addNeonNetwork();
await this.switchToNeonNetwork();
} else {
console.error('❌ Failed to switch network:', error);
throw error;
}
}
}
async connectWallet() {
try {
await this.checkMetaMaskAvailability();
// 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;
// Check and switch to Neon network
const currentChainId = await window.ethereum.request({
method: 'eth_chainId'
});
if (currentChainId !== this.chainId) {
console.log('Switching to Neon network...');
await this.switchToNeonNetwork();
}
console.log('✅ Wallet connected:', this.walletAddress);
return this.walletAddress;
} catch (error) {
console.error('❌ Wallet connection failed:', error);
throw error;
}
}
async signInWithWallet() {
if (!this.isConnected) {
await this.connectWallet();
}
try {
// 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: ${this.walletAddress}`;
console.log('Requesting signature for message:', message);
// Sign message
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, this.walletAddress]
});
console.log('✅ Message signed successfully');
// Verify with API
const response = await fetch('https://api.brdz.link/api/neon/signin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
wallet_address: this.walletAddress,
signature: signature,
message: message
})
});
const result = await response.json();
if (result.success) {
console.log('✅ Authentication successful:', result.wallet_address);
// Store auth data
localStorage.setItem('neon_wallet_address', result.wallet_address);
localStorage.setItem('neon_auth_timestamp', timestamp);
return result;
} else {
throw new Error(result.error || 'Authentication failed');
}
} catch (error) {
console.error('❌ Sign in failed:', error);
throw error;
}
}
async getWalletBalance() {
if (!this.walletAddress) {
throw new Error('Wallet not connected');
}
try {
const response = await fetch(`https://api.brdz.link/api/neon/balance/${this.walletAddress}`, {
headers: {
'x-api-key': 'YOUR_API_KEY'
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const balance = await response.json();
console.log('💰 Wallet balance:', balance);
return balance;
} catch (error) {
console.error('❌ Balance fetch failed:', error);
throw error;
}
}
async setupEventListeners() {
if (!window.ethereum) return;
// Account changed
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
console.log('🔓 Wallet disconnected');
this.isConnected = false;
this.walletAddress = null;
localStorage.removeItem('neon_wallet_address');
} else {
console.log('🔄 Account changed to:', accounts[0]);
this.walletAddress = accounts[0];
localStorage.setItem('neon_wallet_address', accounts[0]);
}
});
// Chain changed
window.ethereum.on('chainChanged', (chainId) => {
console.log('🔄 Chain changed to:', chainId);
if (chainId !== this.chainId) {
console.log('⚠️ Please switch back to Neon network');
}
});
// Connection status
window.ethereum.on('connect', (connectInfo) => {
console.log('🔗 MetaMask connected:', connectInfo);
});
window.ethereum.on('disconnect', (error) => {
console.log('🔌 MetaMask disconnected:', error);
this.isConnected = false;
this.walletAddress = null;
});
}
async initializeApp() {
try {
await this.setupEventListeners();
// Check if previously connected
const savedAddress = localStorage.getItem('neon_wallet_address');
if (savedAddress) {
console.log('🔄 Reconnecting to saved wallet:', savedAddress);
await this.connectWallet();
}
console.log('✅ Neon wallet integration initialized');
} catch (error) {
console.error('❌ Initialization failed:', error);
}
}
disconnect() {
this.isConnected = false;
this.walletAddress = null;
localStorage.removeItem('neon_wallet_address');
localStorage.removeItem('neon_auth_timestamp');
console.log('🔓 Wallet disconnected');
}
}
// Usage
const neonWallet = new NeonWalletIntegration();
// Initialize the app
await neonWallet.initializeApp();
// Connect and sign in
try {
await neonWallet.connectWallet();
const authResult = await neonWallet.signInWithWallet();
console.log('Signed in successfully:', authResult);
// Get balance
const balance = await neonWallet.getWalletBalance();
console.log('Current balance:', balance);
} catch (error) {
console.error('Error:', error.message);
}
React Component Example
import React, { useState, useEffect } from 'react';
const NeonWalletConnect = () => {
const [isConnected, setIsConnected] = useState(false);
const [walletAddress, setWalletAddress] = useState('');
const [balance, setBalance] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
useEffect(() => {
// Check if wallet was previously connected
const savedAddress = localStorage.getItem('neon_wallet_address');
if (savedAddress && window.ethereum) {
setWalletAddress(savedAddress);
setIsConnected(true);
fetchBalance(savedAddress);
}
}, []);
const connectWallet = async () => {
try {
setIsLoading(true);
setError('');
if (!window.ethereum) {
throw new Error('MetaMask not installed');
}
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
const address = accounts[0];
setWalletAddress(address);
setIsConnected(true);
localStorage.setItem('neon_wallet_address', address);
await fetchBalance(address);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
const signIn = async () => {
try {
setIsLoading(true);
setError('');
const timestamp = new Date().toISOString();
const nonce = Math.random().toString(36).substring(7);
const message = `Sign in to BRDZ with Neon wallet\nTimestamp: ${timestamp}\nNonce: ${nonce}`;
const signature = await window.ethereum.request({
method: 'personal_sign',
params: [message, walletAddress]
});
const response = await fetch('https://api.brdz.link/api/neon/signin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY'
},
body: JSON.stringify({
wallet_address: walletAddress,
signature: signature,
message: message
})
});
const result = await response.json();
if (result.success) {
alert('✅ Signed in successfully!');
} else {
throw new Error(result.error);
}
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
const fetchBalance = async (address) => {
try {
const response = await fetch(`https://api.brdz.link/api/neon/balance/${address}`, {
headers: {
'x-api-key': 'YOUR_API_KEY'
}
});
if (response.ok) {
const balanceData = await response.json();
setBalance(balanceData);
} else {
console.error('Failed to fetch balance');
}
} catch (err) {
console.error('Balance fetch error:', err);
}
};
const disconnect = () => {
setIsConnected(false);
setWalletAddress('');
setBalance(null);
localStorage.removeItem('neon_wallet_address');
};
return (
<div className="neon-wallet-connect">
<h2>Neon Wallet Connection</h2>
{error && (
<div className="error">
❌ {error}
</div>
)}
{!isConnected ? (
<button
onClick={connectWallet}
disabled={isLoading}
className="connect-button"
>
{isLoading ? 'Connecting...' : 'Connect Neon Wallet'}
</button>
) : (
<div className="wallet-info">
<p><strong>Connected:</strong> {walletAddress}</p>
{balance && (
<div className="balance-info">
<h3>Balance:</h3>
<p>NEON: {parseFloat(balance.native_balance).toFixed(6)}</p>
<p>USDC: {parseFloat(balance.usdc_balance).toFixed(2)}</p>
</div>
)}
<div className="actions">
<button
onClick={signIn}
disabled={isLoading}
className="sign-in-button"
>
{isLoading ? 'Signing...' : 'Sign In'}
</button>
<button
onClick={() => fetchBalance(walletAddress)}
disabled={isLoading}
className="refresh-button"
>
Refresh Balance
</button>
<button
onClick={disconnect}
className="disconnect-button"
>
Disconnect
</button>
</div>
</div>
)}
</div>
);
};
export default NeonWalletConnect;
Environment Configuration
Required Environment Variables
# Neon Network Configuration
NEON_RPC_URL=https://245022926.rpc.thirdweb.com/
NEON_CHAIN_ID=245022926
USDC_CONTRACT_ADDRESS_NEON=0x... # USDC contract address on Neon
# Network Details
NEON_DEVNET_EXPLORER=https://neon-devnet.blockscout.com/
NATIVE_TOKEN_NEON=NEON
Network Connection Details
// Network configuration for MetaMask
const neonNetworkConfig = {
chainId: '0xE9AC0CE', // 245022926 in hexadecimal
chainName: 'Neon EVM DevNet',
nativeCurrency: {
name: 'NEON',
symbol: 'NEON',
decimals: 18
},
rpcUrls: ['https://245022926.rpc.thirdweb.com/'],
blockExplorerUrls: ['https://neon-devnet.blockscout.com/']
};
Error Handling
Common Error Scenarios
// Comprehensive error handling for Neon integration
class NeonErrorHandler {
static handleSignInError(error) {
if (error.message.includes('User denied message signature')) {
return 'User cancelled the signature request';
} else if (error.message.includes('Signature verification failed')) {
return 'Invalid signature. Please try signing again.';
} else if (error.message.includes('Missing wallet_address')) {
return 'Wallet address is required';
} else if (error.code === 4001) {
return 'User rejected the request';
} else if (error.code === -32602) {
return 'Invalid parameters';
} else {
return `Sign in failed: ${error.message}`;
}
}
static handleBalanceError(error) {
if (error.message.includes('Invalid wallet address')) {
return 'Please provide a valid Ethereum address';
} else if (error.message.includes('Failed to fetch balance')) {
return 'Unable to connect to Neon network. Please try again.';
} else if (error.message.includes('RPC connection')) {
return 'Network connection error. Please check your internet connection.';
} else {
return `Balance fetch failed: ${error.message}`;
}
}
static handleMetaMaskError(error) {
if (error.code === 4902) {
return 'Neon network not added to MetaMask. Please add the network.';
} else if (error.code === 4001) {
return 'User rejected the network switch request';
} else if (error.message.includes('wallet_addEthereumChain')) {
return 'Failed to add Neon network. Please add manually.';
} else {
return `MetaMask error: ${error.message}`;
}
}
}
// Usage in error handling
try {
await neonWallet.signInWithWallet();
} catch (error) {
const friendlyMessage = NeonErrorHandler.handleSignInError(error);
console.error(friendlyMessage);
// Show user-friendly error message
}
Address Validation
// Validate Ethereum addresses before API calls
function validateEthereumAddress(address) {
// Basic format check
if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
throw new Error('Invalid address format. Must be a 42-character hexadecimal string starting with 0x');
}
// Additional checksum validation can be added here
return address.toLowerCase();
}
// Safe balance checking with validation
async function safeGetBalance(walletAddress) {
try {
const validatedAddress = validateEthereumAddress(walletAddress);
const balance = await getNeonBalance(validatedAddress);
return balance;
} catch (error) {
const friendlyMessage = NeonErrorHandler.handleBalanceError(error);
console.error(friendlyMessage);
return null;
}
}
Security Considerations
Message Signing Best Practices
- Include Timestamp: Prevent replay attacks
- Add Nonce: Ensure message uniqueness
- Specify Purpose: Clear indication of what's being signed
- Include Domain: Prevent cross-domain attacks
// Secure message format
function createSecureMessage(walletAddress, domain = 'BRDZ') {
const timestamp = new Date().toISOString();
const nonce = crypto.randomUUID();
return `Sign in to ${domain} with Neon wallet
Address: ${walletAddress}
Timestamp: ${timestamp}
Nonce: ${nonce}
Chain: Neon EVM DevNet (245022926)`;
}
Private Key Protection
- Never request private keys in web applications
- Use MetaMask signing for all signature operations
- Validate signatures server-side using ethers.js
- Implement rate limiting for sign-in attempts
Use MetaMask's built-in signing methods for secure wallet authentication. Never request private keys directly.
Ensure users are connected to the correct Neon network (Chain ID: 245022926) before performing transactions.
Native NEON balances are returned with full precision (18 decimals). Format appropriately for display purposes.