@chainreactionom/nano-mcp
Version:
NANO cryptocurrency wallet implementation for MCP with comprehensive testing
159 lines (133 loc) • 5.14 kB
text/typescript
import dotenv from 'dotenv';
import path from 'path';
import fs from 'fs';
// Load environment variables from .env file
dotenv.config();
/**
* Default RPC URL from nano.to
* High-performance public enterprise node for the Nano blockchain
* Visit https://rpc.nano.to/ to get your free API key
*/
const DEFAULT_RPC_URL = 'https://rpc.nano.to/';
/**
* Default representative address
* This address is used when no representative is specified
*
* To choose a different representative:
* 1. Visit https://nanexplorer.com/nano/representatives
* 2. Select a representative with good uptime and reasonable voting weight
* 3. Copy their address and use it in your configuration
*
* Choosing a good representative helps maintain network decentralization
*/
const DEFAULT_REPRESENTATIVE = 'nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4';
const DEFAULT_RPC_KEY = 'RPC-KEY-BAB822FCCDAE42ECB7A331CCAAAA23';
export interface NanoConfig {
rpcUrl: string;
rpcKey: string;
gpuKey: string;
defaultRepresentative: string;
}
export interface ConfigValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
}
class GlobalConfig {
private static instance: GlobalConfig;
private config: NanoConfig;
private initialized: boolean = false;
private constructor() {
this.config = {
rpcUrl: process.env.NANO_RPC_URL || DEFAULT_RPC_URL,
rpcKey: process.env.NANO_RPC_KEY || DEFAULT_RPC_KEY,
gpuKey: process.env.NANO_GPU_KEY || DEFAULT_RPC_KEY,
defaultRepresentative: process.env.NANO_DEFAULT_REPRESENTATIVE || DEFAULT_REPRESENTATIVE
};
}
public static getInstance(): GlobalConfig {
if (!GlobalConfig.instance) {
GlobalConfig.instance = new GlobalConfig();
}
return GlobalConfig.instance;
}
public getNanoConfig(): NanoConfig {
if (!this.initialized) {
throw new Error('Configuration not initialized. Call initializeConfig() first.');
}
return { ...this.config };
}
public initializeConfig(config?: Partial<NanoConfig>): ConfigValidationResult {
if (config) {
this.config = {
...this.config,
...config
};
}
const validation = this.validateConfig();
if (validation.isValid) {
this.initialized = true;
}
return validation;
}
public validateConfig(): ConfigValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
// Required parameters
if (!this.config.rpcUrl) {
errors.push('NANO_RPC_URL is required but not set');
}
if (!this.config.rpcKey) {
errors.push('NANO_RPC_KEY is required but not set');
}
if (!this.config.gpuKey) {
errors.push('NANO_GPU_KEY is required but not set');
}
// Optional parameters with defaults
if (!this.config.defaultRepresentative) {
this.config.defaultRepresentative = DEFAULT_REPRESENTATIVE;
warnings.push('Using default representative address');
}
// URL format validation
try {
new URL(this.config.rpcUrl);
} catch {
errors.push('NANO_RPC_URL must be a valid URL');
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
public static generateEnvTemplate(): void {
const template = `
NANO_RPC_URL=https://rpc.nano.to/ # Required: URL of the NANO RPC server
NANO_RPC_KEY=your-rpc-key-here
NANO_GPU_KEY=your-gpu-key-here
NANO_DEFAULT_REPRESENTATIVE=${DEFAULT_REPRESENTATIVE}
`;
const envPath = path.join(process.cwd(), '.env.example');
fs.writeFileSync(envPath, template);
}
public getConfigStatus(): string {
const validation = this.validateConfig();
let status = 'Configuration Status:\n';
if (validation.errors.length > 0) {
status += '\nErrors:\n' + validation.errors.map(err => `- ${err}`).join('\n');
}
if (validation.warnings.length > 0) {
status += '\nWarnings:\n' + validation.warnings.map(warn => `- ${warn}`).join('\n');
}
if (validation.isValid) {
status += '\nConfiguration is valid and ready to use.';
}
return status;
}
}
export const config = GlobalConfig.getInstance();
export default GlobalConfig;