Files
proxmox/scripts/besu_balances_106_117.js
defiQUG dbd517b279 Sync workspace: config, docs, scripts, CI, operator rules, and submodule pointers.
- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains
- Omit embedded publish git dirs and empty placeholders from index

Made-with: Cursor
2026-04-12 06:12:20 -07:00

325 lines
10 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Query balances from Besu RPC nodes (current production fleet by default)
*
* Note: Only RPC nodes expose RPC endpoints.
* Validators (1000-1004) and sentries (1500-1503) don't have RPC enabled.
*
* Usage:
* RPC_URLS="http://192.168.11.23:8545,http://192.168.11.24:8545,http://192.168.11.25:8545" \
* WETH9_ADDRESS="0x..." \
* WETH10_ADDRESS="0x..." \
* node scripts/besu_balances_106_117.js
*
* Or use template:
* RPC_TEMPLATE="http://192.168.11.{vmid}:8545" VMID_START=211 VMID_END=212 \
* node scripts/besu_balances_106_117.js
*/
import { ethers } from 'ethers';
// Configuration
const WALLET_ADDRESS = '0xa55A4B57A91561e9df5a883D4883Bd4b1a7C4882';
// Validators and sentries do not expose RPC. Default to the current core/public trio.
const VMID_START = process.env.VMID_START ? parseInt(process.env.VMID_START) : 2101;
const VMID_END = process.env.VMID_END ? parseInt(process.env.VMID_END) : 2103;
const CONCURRENCY_LIMIT = 4;
const REQUEST_TIMEOUT = 15000; // 15 seconds
// ERC-20 minimal ABI
const ERC20_ABI = [
'function balanceOf(address owner) view returns (uint256)',
'function decimals() view returns (uint8)',
'function symbol() view returns (string)'
];
// Default token addresses
const WETH9_ADDRESS = process.env.WETH9_ADDRESS || '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const WETH10_ADDRESS = process.env.WETH10_ADDRESS || null;
// VMID to IP mapping for better VMID detection
const VMID_TO_IP = {
'192.168.11.211': 2101,
'192.168.11.212': 2102,
'192.168.11.217': 2103,
'192.168.11.221': 2201,
'192.168.11.232': 2301,
'192.168.11.233': 2303,
'192.168.11.234': 2304,
'192.168.11.235': 2305,
'192.168.11.236': 2306,
'192.168.11.237': 2307,
'192.168.11.238': 2308,
'192.168.11.240': 2400,
'192.168.11.241': 2401,
'192.168.11.242': 2402,
'192.168.11.243': 2403,
'192.168.11.172': 2420,
'192.168.11.173': 2430,
'192.168.11.174': 2440,
'192.168.11.246': 2460,
'192.168.11.247': 2470,
'192.168.11.248': 2480,
};
// Reverse IP to VMID mapping (VMID -> IP)
const IP_TO_VMID = {
2101: '192.168.11.211',
2102: '192.168.11.212',
2103: '192.168.11.217',
2201: '192.168.11.221',
2301: '192.168.11.232',
2303: '192.168.11.233',
2304: '192.168.11.234',
2305: '192.168.11.235',
2306: '192.168.11.236',
2307: '192.168.11.237',
2308: '192.168.11.238',
2400: '192.168.11.240',
2401: '192.168.11.241',
2402: '192.168.11.242',
2403: '192.168.11.243',
2420: '192.168.11.172',
2430: '192.168.11.173',
2440: '192.168.11.174',
2460: '192.168.11.246',
2470: '192.168.11.247',
2480: '192.168.11.248',
};
// RPC endpoint configuration
function getRpcUrls() {
if (process.env.RPC_URLS) {
return process.env.RPC_URLS.split(',').map(url => url.trim());
}
const template = process.env.RPC_TEMPLATE;
const urls = [];
for (let vmid = VMID_START; vmid <= VMID_END; vmid++) {
if (template && template.includes('{vmid}')) {
// Use template if provided
urls.push(template.replace('{vmid}', vmid.toString()));
} else {
// Use actual IP from mapping (default behavior)
const ip = IP_TO_VMID[vmid];
if (ip) {
urls.push(`http://${ip}:8545`);
} else {
// Fallback to template or direct VMID
const fallbackTemplate = template || 'http://192.168.11.{vmid}:8545';
urls.push(fallbackTemplate.replace('{vmid}', vmid.toString()));
}
}
}
return urls;
}
// Get VMID from URL
function getVmidFromUrl(url) {
// Try IP mapping first
const ipMatch = url.match(/(\d+\.\d+\.\d+\.\d+)/);
if (ipMatch && VMID_TO_IP[ipMatch[1]]) {
return VMID_TO_IP[ipMatch[1]];
}
// Fallback to pattern matching
const match = url.match(/(?:\.|:)(\d{3})(?::|\/)/);
return match ? parseInt(match[1]) : null;
}
// Format balance with decimals
function formatBalance(balance, decimals, symbol) {
const formatted = ethers.formatUnits(balance, decimals);
return `${formatted} ${symbol}`;
}
// Query single RPC endpoint
async function queryRpc(url, walletAddress) {
const vmid = getVmidFromUrl(url) || 'unknown';
const result = {
vmid,
url,
success: false,
chainId: null,
blockNumber: null,
ethBalance: null,
ethBalanceWei: null,
weth9: null,
weth10: null,
errors: []
};
try {
// Create provider with timeout
const provider = new ethers.JsonRpcProvider(url, undefined, {
staticNetwork: false,
batchMaxCount: 1,
batchMaxSize: 250000,
staticNetworkCode: false,
});
// Set timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT);
try {
// Health checks
const [chainId, blockNumber] = await Promise.all([
provider.getNetwork().then(n => Number(n.chainId)),
provider.getBlockNumber()
]);
clearTimeout(timeoutId);
result.chainId = chainId;
result.blockNumber = blockNumber;
// Query ETH balance
const ethBalance = await provider.getBalance(walletAddress);
result.ethBalanceWei = ethBalance.toString();
result.ethBalance = ethers.formatEther(ethBalance);
// Query WETH9 balance
try {
const weth9Contract = new ethers.Contract(WETH9_ADDRESS, ERC20_ABI, provider);
const [balance, decimals, symbol] = await Promise.all([
weth9Contract.balanceOf(walletAddress),
weth9Contract.decimals(),
weth9Contract.symbol()
]);
result.weth9 = {
balance: balance.toString(),
decimals: Number(decimals),
symbol: symbol,
formatted: formatBalance(balance, decimals, symbol)
};
} catch (err) {
result.errors.push(`WETH9: ${err.message}`);
}
// Query WETH10 balance (if address provided)
if (WETH10_ADDRESS) {
try {
const weth10Contract = new ethers.Contract(WETH10_ADDRESS, ERC20_ABI, provider);
const [balance, decimals, symbol] = await Promise.all([
weth10Contract.balanceOf(walletAddress),
weth10Contract.decimals(),
weth10Contract.symbol()
]);
result.weth10 = {
balance: balance.toString(),
decimals: Number(decimals),
symbol: symbol,
formatted: formatBalance(balance, decimals, symbol)
};
} catch (err) {
result.errors.push(`WETH10: ${err.message}`);
}
}
result.success = true;
} catch (err) {
clearTimeout(timeoutId);
throw err;
}
} catch (err) {
result.errors.push(err.message);
result.success = false;
}
return result;
}
// Process URLs with concurrency limit
async function processWithConcurrencyLimit(urls, walletAddress, limit) {
const results = [];
const executing = [];
for (const url of urls) {
const promise = queryRpc(url, walletAddress).then(result => {
results.push(result);
});
executing.push(promise);
if (executing.length >= limit) {
await Promise.race(executing);
executing.splice(executing.findIndex(p => p === promise), 1);
}
}
await Promise.all(executing);
return results.sort((a, b) => (a.vmid === 'unknown' ? 999 : a.vmid) - (b.vmid === 'unknown' ? 999 : b.vmid));
}
// Format and print results
function printResults(results) {
let successCount = 0;
for (const result of results) {
console.log(`VMID: ${result.vmid}`);
console.log(`RPC: ${result.url}`);
if (result.success) {
successCount++;
console.log(`chainId: ${result.chainId}`);
console.log(`block: ${result.blockNumber}`);
console.log(`ETH: ${result.ethBalance} (wei: ${result.ethBalanceWei})`);
if (result.weth9) {
console.log(`WETH9: ${result.weth9.formatted} (raw: ${result.weth9.balance})`);
} else {
console.log(`WETH9: error (see errors below)`);
}
if (WETH10_ADDRESS) {
if (result.weth10) {
console.log(`WETH10: ${result.weth10.formatted} (raw: ${result.weth10.balance})`);
} else {
console.log(`WETH10: error (see errors below)`);
}
} else {
console.log(`WETH10: skipped (missing address)`);
}
if (result.errors.length > 0) {
console.log(`Errors: ${result.errors.join('; ')}`);
}
} else {
console.log(`Status: FAILED`);
if (result.errors.length > 0) {
console.log(`Errors: ${result.errors.join('; ')}`);
}
}
console.log('---');
}
return successCount;
}
// Main execution
async function main() {
console.log('Querying balances from Besu RPC nodes...');
console.log(`Wallet: ${WALLET_ADDRESS}`);
console.log(`WETH9: ${WETH9_ADDRESS}`);
console.log(`WETH10: ${WETH10_ADDRESS || 'not set (will skip)'}`);
console.log('');
const rpcUrls = getRpcUrls();
console.log(`Checking ${rpcUrls.length} RPC endpoints (concurrency: ${CONCURRENCY_LIMIT})...`);
console.log('');
const results = await processWithConcurrencyLimit(rpcUrls, WALLET_ADDRESS, CONCURRENCY_LIMIT);
const successCount = printResults(results);
console.log('');
console.log(`Summary: ${successCount} out of ${results.length} RPC endpoints succeeded`);
// Exit code: 0 if at least one succeeded, 1 if all failed
process.exit(successCount > 0 ? 0 : 1);
}
main().catch(err => {
console.error('Fatal error:', err);
process.exit(1);
});