lemora-bot
Version:
Advanced AI-Powered Trading Signals & Wallet Monitoring for Solana
266 lines (221 loc) • 8.48 kB
text/typescript
import { WebSocketService } from '../services/websocket.service';
import { SolanaWalletService } from '../services/solana-wallet.service';
class BackgroundService {
private websocketService: WebSocketService;
private walletService: SolanaWalletService;
private watchedWallets: Set<string> = new Set();
private notificationQueue: any[] = [];
constructor() {
this.websocketService = new WebSocketService(process.env.WEBSOCKET_URL || 'wss://api.helius.xyz/v0/websocket');
this.walletService = new SolanaWalletService(
process.env.SOLANA_RPC_URL || 'https://api.mainnet-beta.solana.com',
process.env.HELIUS_API_KEY || ''
);
this.setupEventListeners();
this.setupWebSocketHandlers();
}
private setupEventListeners(): void {
// Listen for extension installation
chrome.runtime.onInstalled.addListener((details) => {
console.log('Extension installed:', details.reason);
this.initialize();
});
// Listen for messages from popup/content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
this.handleMessage(message, sender, sendResponse);
return true; // Keep message channel open for async response
});
// Listen for alarm events (for periodic tasks)
chrome.alarms.onAlarm.addListener((alarm) => {
this.handleAlarm(alarm);
});
// Listen for tab updates to inject content scripts
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete' && tab.url) {
this.handleTabUpdate(tabId, tab);
}
});
}
private setupWebSocketHandlers(): void {
this.websocketService.on('connected', () => {
console.log('WebSocket connected in background');
this.resubscribeWallets();
});
this.websocketService.on('transaction', (data) => {
this.handleTransaction(data);
});
this.websocketService.on('balanceUpdate', (data) => {
this.handleBalanceUpdate(data);
});
this.websocketService.on('error', (error) => {
console.error('WebSocket error in background:', error);
});
}
private async initialize(): void {
try {
// Load watched wallets from storage
const result = await chrome.storage.sync.get(['watchedWallets']);
if (result.watchedWallets) {
this.watchedWallets = new Set(result.watchedWallets);
}
// Connect to WebSocket
await this.websocketService.connect();
// Setup periodic tasks
chrome.alarms.create('refreshData', { periodInMinutes: 5 });
chrome.alarms.create('cleanupNotifications', { periodInMinutes: 60 });
console.log('Background service initialized');
} catch (error) {
console.error('Failed to initialize background service:', error);
}
}
private async handleMessage(message: any, sender: chrome.runtime.MessageSender, sendResponse: Function): Promise<void> {
try {
switch (message.type) {
case 'ADD_WALLET':
await this.addWallet(message.address);
sendResponse({ success: true });
break;
case 'REMOVE_WALLET':
await this.removeWallet(message.address);
sendResponse({ success: true });
break;
case 'GET_WALLET_INFO':
const walletInfo = await this.walletService.getWalletInfo(message.address);
sendResponse({ success: true, data: walletInfo });
break;
case 'GET_TRANSACTIONS':
const transactions = await this.walletService.getRecentTransactions(message.address, message.limit);
sendResponse({ success: true, data: transactions });
break;
case 'GET_WATCHED_WALLETS':
sendResponse({ success: true, data: Array.from(this.watchedWallets) });
break;
default:
sendResponse({ success: false, error: 'Unknown message type' });
}
} catch (error) {
console.error('Error handling message:', error);
sendResponse({ success: false, error: error.message });
}
}
private async addWallet(address: string): Promise<void> {
this.watchedWallets.add(address);
await this.saveWatchedWallets();
this.websocketService.subscribe(address);
// Send notification
this.createNotification(`Started tracking wallet: ${address.slice(0, 8)}...`);
}
private async removeWallet(address: string): Promise<void> {
this.watchedWallets.delete(address);
await this.saveWatchedWallets();
this.websocketService.unsubscribe(address);
// Send notification
this.createNotification(`Stopped tracking wallet: ${address.slice(0, 8)}...`);
}
private async saveWatchedWallets(): Promise<void> {
await chrome.storage.sync.set({
watchedWallets: Array.from(this.watchedWallets)
});
}
private resubscribeWallets(): void {
this.watchedWallets.forEach(address => {
this.websocketService.subscribe(address);
});
}
private handleTransaction(data: any): void {
console.log('New transaction:', data);
// Analyze transaction for signals
if (this.isSignificantTransaction(data)) {
this.createNotification(`Significant transaction detected on ${data.address.slice(0, 8)}...`);
// Send to popup if open
chrome.runtime.sendMessage({
type: 'TRANSACTION_UPDATE',
data: data
}).catch(() => {
// Popup might not be open, ignore error
});
}
}
private handleBalanceUpdate(data: any): void {
console.log('Balance update:', data);
// Send to popup if open
chrome.runtime.sendMessage({
type: 'BALANCE_UPDATE',
data: data
}).catch(() => {
// Popup might not be open, ignore error
});
}
private isSignificantTransaction(data: any): boolean {
// Define criteria for significant transactions
return data.amount > 100 || // Large amount
data.type === 'swap' || // Swap transactions
data.riskScore > 0.7; // High risk score
}
private createNotification(message: string): void {
const notificationId = `notification_${Date.now()}`;
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: '../assets/icons/icon48.png',
title: 'Lemora Wallet Tracker',
message: message
});
// Auto-clear notification after 5 seconds
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 5000);
}
private handleAlarm(alarm: chrome.alarms.Alarm): void {
switch (alarm.name) {
case 'refreshData':
this.refreshWalletData();
break;
case 'cleanupNotifications':
this.cleanupNotifications();
break;
}
}
private async refreshWalletData(): Promise<void> {
// Refresh data for all watched wallets
for (const address of this.watchedWallets) {
try {
const walletInfo = await this.walletService.getWalletInfo(address);
// Cache the data
await chrome.storage.local.set({
[`wallet_${address}`]: {
data: walletInfo,
timestamp: Date.now()
}
});
} catch (error) {
console.error(`Failed to refresh data for wallet ${address}:`, error);
}
}
}
private cleanupNotifications(): void {
// Clear old notifications from queue
this.notificationQueue = this.notificationQueue.filter(
notification => Date.now() - notification.timestamp < 24 * 60 * 60 * 1000 // 24 hours
);
}
private handleTabUpdate(tabId: number, tab: chrome.tabs.Tab): void {
// Inject content script if on supported sites
const supportedSites = [
'solscan.io',
'solanabeach.io',
'raydium.io',
'jupiter.exchange'
];
if (tab.url && supportedSites.some(site => tab.url!.includes(site))) {
chrome.scripting.executeScript({
target: { tabId },
files: ['src/content/content-script.js']
}).catch(error => {
console.log('Content script already injected or failed:', error);
});
}
}
}
// Initialize the background service
const backgroundService = new BackgroundService();
export default backgroundService;