From f354fbd6c69a23b9f1b8459e96b7d950265e985f Mon Sep 17 00:00:00 2001 From: MarcoMandar <malicemandar@gmail.com> Date: Tue, 19 Nov 2024 07:28:00 +0200 Subject: [PATCH 1/6] token balance, transactions, data simulation update on buys and sells Signed-off-by: MarcoMandar <malicemandar@gmail.com> --- .../src/providers/trustScoreProvider.ts | 40 ++++++ .../src/adapters/trustScoreDatabase.ts | 120 ++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/packages/plugin-solana/src/providers/trustScoreProvider.ts b/packages/plugin-solana/src/providers/trustScoreProvider.ts index 88c2b97df01..fcb98d394e4 100644 --- a/packages/plugin-solana/src/providers/trustScoreProvider.ts +++ b/packages/plugin-solana/src/providers/trustScoreProvider.ts @@ -351,10 +351,14 @@ export class TrustScoreManager { new PublicKey(Wallet!) ); + let tokensBalance = 0; const prices = await wallet.fetchPrices(runtime); const solPrice = prices.solana.usd; const buySol = data.buy_amount / parseFloat(solPrice); const buy_value_usd = data.buy_amount * processedData.tradeData.price; + const token = await this.tokenProvider.fetchTokenTradeData(); + const tokenPrice = token.price; + tokensBalance = buy_value_usd / tokenPrice; const creationData = { token_address: tokenAddress, @@ -383,6 +387,23 @@ export class TrustScoreManager { rapidDump: false, }; this.trustScoreDb.addTradePerformance(creationData, data.is_simulation); + + if (data.is_simulation) { + // If the trade is a simulation update the balance + this.trustScoreDb.updateTokenBalance(tokenAddress, tokensBalance); + // generate some random hash for simulations + const hash = Math.random().toString(36).substring(7); + const transaction = { + tokenAddress: tokenAddress, + type: "buy", + transactionHash: hash, + amount: data.buy_amount, + price: processedData.tradeData.price, + isSimulation: true, + timestamp: new Date().toISOString(), + }; + this.trustScoreDb.addTransaction(transaction); + } // api call to update trade performance this.createTradeInBe(tokenAddress, recommenderId, data); return creationData; @@ -507,6 +528,25 @@ export class TrustScoreManager { sellDetailsData, isSimulation ); + if (isSimulation) { + // If the trade is a simulation update the balance + const oldBalance = this.trustScoreDb.getTokenBalance(tokenAddress); + const tokenBalance = oldBalance - sellDetails.sell_amount; + this.trustScoreDb.updateTokenBalance(tokenAddress, tokenBalance); + // generate some random hash for simulations + const hash = Math.random().toString(36).substring(7); + const transaction = { + tokenAddress: tokenAddress, + type: "sell", + transactionHash: hash, + amount: sellDetails.sell_amount, + price: processedData.tradeData.price, + isSimulation: true, + timestamp: new Date().toISOString(), + }; + this.trustScoreDb.addTransaction(transaction); + } + return sellDetailsData; } diff --git a/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts b/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts index 51b44b9eb25..5fd6e3d8bb7 100644 --- a/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts +++ b/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts @@ -40,6 +40,7 @@ export interface TokenPerformance { rapidDump: boolean; suspiciousVolume: boolean; validationTrust: number; + balance: number; lastUpdated: Date; } @@ -120,9 +121,20 @@ interface TokenPerformanceRow { rapid_dump: number; suspicious_volume: number; validation_trust: number; + balance: number; last_updated: string; } +interface Transaction { + tokenAddress: string; + transactionHash: string; + type: "buy" | "sell"; + amount: number; + price: number; + isSimulation: boolean; + timestamp: string; +} + export class TrustScoreDatabase { private db: Database; @@ -192,6 +204,7 @@ export class TrustScoreDatabase { rapid_dump BOOLEAN DEFAULT FALSE, suspicious_volume BOOLEAN DEFAULT FALSE, validation_trust REAL DEFAULT 0, + balance REAL DEFAULT 0, last_updated DATETIME DEFAULT CURRENT_TIMESTAMP ); `); @@ -289,6 +302,20 @@ export class TrustScoreDatabase { FOREIGN KEY (recommender_id) REFERENCES recommenders(id) ON DELETE CASCADE ); `); + + // create transactions table + this.db.exec(` + CREATE TABLE IF NOT EXISTS transactions ( + token_address TEXT NOT NULL, + transaction_hash TEXT PRIMARY KEY, + type TEXT NOT NULL, + amount REAL NOT NULL, + price REAL NOT NULL, + timestamp TEXT NOT NULL, + is_simulation BOOLEAN DEFAULT FALSE, + FOREIGN KEY (token_address) REFERENCES token_performance(token_address) ON DELETE CASCADE + ); + `); } /** @@ -749,6 +776,25 @@ export class TrustScoreDatabase { } } + // update token balance + + updateTokenBalance(tokenAddress: string, balance: number): boolean { + const sql = ` + UPDATE token_performance + SET balance = ?, + last_updated = CURRENT_TIMESTAMP + WHERE token_address = ?; + `; + try { + this.db.prepare(sql).run(balance, tokenAddress); + console.log(`Updated token balance for ${tokenAddress}`); + return true; + } catch (error) { + console.error("Error updating token balance:", error); + return false; + } + } + /** * Retrieves token performance metrics. * @param tokenAddress Token's address @@ -776,6 +822,7 @@ export class TrustScoreDatabase { rapidDump: row.rapid_dump === 1, suspiciousVolume: row.suspicious_volume === 1, validationTrust: row.validation_trust, + balance: row.balance, lastUpdated: new Date(row.last_updated), }; } @@ -1247,6 +1294,79 @@ export class TrustScoreDatabase { }; } + // ----- Transactions Methods ----- + /** + * Adds a new transaction to the database. + * @param transaction Transaction object + * @returns boolean indicating success + */ + + addTransaction(transaction: Transaction): boolean { + const sql = ` + INSERT INTO transactions ( + token_address, + transaction_hash, + type, + amount, + price, + is_simulation, + timestamp + ) VALUES (?, ?, ?, ?, ?, ?); + `; + try { + this.db + .prepare(sql) + .run( + transaction.tokenAddress, + transaction.transactionHash, + transaction.type, + transaction.amount, + transaction.price, + transaction.isSimulation, + transaction.timestamp + ); + return true; + } catch (error) { + console.error("Error adding transaction:", error); + return false; + } + } + + /** + * Retrieves all transactions for a specific token. + * @param tokenAddress Token's address + * @returns Array of Transaction objects + */ + getTransactionsByToken(tokenAddress: string): Transaction[] { + const sql = `SELECT * FROM transactions WHERE token_address = ? ORDER BY timestamp DESC;`; + const rows = this.db.prepare(sql).all(tokenAddress) as Array<{ + token_address: string; + transaction_hash: string; + type: string; + amount: number; + price: number; + is_simulation: boolean; + timestamp: string; + }>; + + return rows.map((row) => { + // Validate and cast 'type' to ensure it matches the expected union type + if (row.type !== "buy" && row.type !== "sell") { + throw new Error(`Unexpected transaction type: ${row.type}`); + } + + return { + tokenAddress: row.token_address, + transactionHash: row.transaction_hash, + type: row.type as "buy" | "sell", + amount: row.amount, + price: row.price, + isSimulation: row.is_simulation, + timestamp: new Date(row.timestamp).toISOString(), + }; + }); + } + /** * Close the database connection gracefully. */ From ee1ea3495e4c4d2e509a9227eb33acf032c47cfd Mon Sep 17 00:00:00 2001 From: MarcoMandar <malicemandar@gmail.com> Date: Wed, 20 Nov 2024 02:45:44 +0200 Subject: [PATCH 2/6] include username in createTradePerformance Signed-off-by: MarcoMandar <malicemandar@gmail.com> --- packages/plugin-solana/src/evaluators/trust.ts | 1 + packages/plugin-solana/src/providers/trustScoreProvider.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/plugin-solana/src/evaluators/trust.ts b/packages/plugin-solana/src/evaluators/trust.ts index d4fecefdc94..05e98b75096 100644 --- a/packages/plugin-solana/src/evaluators/trust.ts +++ b/packages/plugin-solana/src/evaluators/trust.ts @@ -253,6 +253,7 @@ async function handler(runtime: IAgentRuntime, message: Memory) { runtime, rec.contractAddress, userId, + account.username, // we need this to create the recommender account in the BE { buy_amount: rec.buyAmount, is_simulation: true, diff --git a/packages/plugin-solana/src/providers/trustScoreProvider.ts b/packages/plugin-solana/src/providers/trustScoreProvider.ts index fcb98d394e4..4478e2c94b6 100644 --- a/packages/plugin-solana/src/providers/trustScoreProvider.ts +++ b/packages/plugin-solana/src/providers/trustScoreProvider.ts @@ -338,6 +338,7 @@ export class TrustScoreManager { runtime: IAgentRuntime, tokenAddress: string, recommenderId: string, + username: string, data: TradeData ): Promise<TradePerformance> { const recommender = @@ -405,7 +406,7 @@ export class TrustScoreManager { this.trustScoreDb.addTransaction(transaction); } // api call to update trade performance - this.createTradeInBe(tokenAddress, recommenderId, data); + this.createTradeInBe(tokenAddress, recommenderId, username, data); return creationData; } @@ -416,6 +417,7 @@ export class TrustScoreManager { async createTradeInBe( tokenAddress: string, recommenderId: string, + username: string, data: TradeData, retries = 3, delayMs = 2000 @@ -434,6 +436,7 @@ export class TrustScoreManager { tokenAddress: tokenAddress, tradeData: data, recommenderId: recommenderId, + username: username, }), } ); From 530e16a25286b57662ecaecba660c767a8879361 Mon Sep 17 00:00:00 2001 From: MarcoMandar <malicemandar@gmail.com> Date: Wed, 20 Nov 2024 12:28:11 +0200 Subject: [PATCH 3/6] simulation selling service initial Signed-off-by: MarcoMandar <malicemandar@gmail.com> --- .../src/providers/simulationSellingService.ts | 255 ++++++++++++++++++ .../src/adapters/trustScoreDatabase.ts | 24 ++ 2 files changed, 279 insertions(+) create mode 100644 packages/plugin-solana/src/providers/simulationSellingService.ts diff --git a/packages/plugin-solana/src/providers/simulationSellingService.ts b/packages/plugin-solana/src/providers/simulationSellingService.ts new file mode 100644 index 00000000000..fc626592538 --- /dev/null +++ b/packages/plugin-solana/src/providers/simulationSellingService.ts @@ -0,0 +1,255 @@ +import { + TrustScoreDatabase, + TokenPerformance, + TradePerformance, + TokenRecommendation, + ProcessedTokenData, +} from "@ai16z/plugin-trustdb"; +import { Connection, PublicKey } from "@solana/web3.js"; +// Assuming TokenProvider and IAgentRuntime are available +import { TokenProvider } from "./token.ts"; +import { settings } from "@ai16z/eliza"; +import { IAgentRuntime, Memory, Provider, State } from "@ai16z/eliza"; +import { WalletProvider } from "./wallet.ts"; + +interface sellDetails { + sell_amount: number; + sell_recommender_id: string | null; +} + +export class simulationSellingService { + private trustScoreDb: TrustScoreDatabase; + private walletProvider: WalletProvider; + private connection: Connection; + private baseMint: PublicKey; + private DECAY_RATE = 0.95; + private MAX_DECAY_DAYS = 30; + private backend: string; + private backendToken: string; + + constructor( + runtime: IAgentRuntime, + trustScoreDb: TrustScoreDatabase, + walletProvider: WalletProvider + ) { + this.trustScoreDb = trustScoreDb; + + this.connection = new Connection(runtime.getSetting("RPC_URL")); + this.walletProvider = new WalletProvider( + this.connection, + new PublicKey(runtime.getSetting("WALLET_PUBLIC_KEY")) + ); + this.baseMint = new PublicKey( + runtime.getSetting("BASE_MINT") || + "So11111111111111111111111111111111111111112" + ); + this.backend = runtime.getSetting("BACKEND_URL"); + this.backendToken = runtime.getSetting("BACKEND_TOKEN"); + } + + public async startService() { + // starting the service + console.log("Starting SellingService..."); + await this.scanAndSell(); + } + + private async scanAndSell() { + // scanning recommendations and selling + console.log("Scanning for token performances..."); + const tokenPerformances = + await this.trustScoreDb.getAllTokenPerformancesWithBalance(); + + const sellDecisions = this.decideWhenToSell(tokenPerformances); + + // Execute sells + await this.executeSells(sellDecisions); + + // Perform stop loss checks + await this.performStopLoss(); + } + + private decideWhenToSell( + tokenPerformances: TokenPerformance[] + ): SellDecision[] { + // To Do: logic when to sell and how much + console.log("Deciding when to sell and how much..."); + const decisions: SellDecision[] = []; + + tokenPerformances.forEach((performance) => { + const amountToSell = performance.balance * 0.1; + decisions.push({ tokenPerformance: performance, amountToSell }); + }); + + return decisions; + } + + private async executeSells(decisions: SellDecision[]) { + console.log("Executing sell orders..."); + for (const decision of decisions) { + console.log( + `Selling ${decision.amountToSell} of token ${decision.tokenPerformance.tokenSymbol}` + ); + // To Do sell logic + } + } + + private async performStopLoss() { + console.log("Performing stop loss checks..."); + // To Do: Implement stop loss logic + } + + async updateSellDetails( + tokenAddress: string, + recommenderId: string, + sellTimeStamp: string, + sellDetails: sellDetails, + isSimulation: boolean, + tokenProvider: TokenProvider + ) { + // To Do: Change the logic after codex update + const recommender = + await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( + recommenderId + ); + const processedData: ProcessedTokenData = + await tokenProvider.getProcessedTokenData(); + const prices = await this.walletProvider.fetchPrices(null); + const solPrice = prices.solana.usd; + const sellSol = sellDetails.sell_amount / parseFloat(solPrice); + const sell_value_usd = + sellDetails.sell_amount * processedData.tradeData.price; + const trade = await this.trustScoreDb.getLatestTradePerformance( + tokenAddress, + recommender.id, + isSimulation + ); + const buyTimeStamp = trade.buy_timeStamp; + const marketCap = + processedData.dexScreenerData.pairs[0]?.marketCap || 0; + const liquidity = + processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0; + const sell_price = processedData.tradeData.price; + const profit_usd = sell_value_usd - trade.buy_value_usd; + const profit_percent = (profit_usd / trade.buy_value_usd) * 100; + + const market_cap_change = marketCap - trade.buy_market_cap; + const liquidity_change = liquidity - trade.buy_liquidity; + + const isRapidDump = await this.isRapidDump(tokenAddress, tokenProvider); + + const sellDetailsData = { + sell_price: sell_price, + sell_timeStamp: sellTimeStamp, + sell_amount: sellDetails.sell_amount, + received_sol: sellSol, + sell_value_usd: sell_value_usd, + profit_usd: profit_usd, + profit_percent: profit_percent, + sell_market_cap: marketCap, + market_cap_change: market_cap_change, + sell_liquidity: liquidity, + liquidity_change: liquidity_change, + rapidDump: isRapidDump, + sell_recommender_id: sellDetails.sell_recommender_id || null, + }; + this.trustScoreDb.updateTradePerformanceOnSell( + tokenAddress, + recommender.id, + buyTimeStamp, + sellDetailsData, + isSimulation + ); + + // If the trade is a simulation update the balance + const oldBalance = this.trustScoreDb.getTokenBalance(tokenAddress); + const tokenBalance = oldBalance - sellDetails.sell_amount; + this.trustScoreDb.updateTokenBalance(tokenAddress, tokenBalance); + // generate some random hash for simulations + const hash = Math.random().toString(36).substring(7); + const transaction = { + tokenAddress: tokenAddress, + type: "sell", + transactionHash: hash, + amount: sellDetails.sell_amount, + price: processedData.tradeData.price, + isSimulation: true, + timestamp: new Date().toISOString(), + }; + this.trustScoreDb.addTransaction(transaction); + this.updateTradeInBe( + tokenAddress, + recommender.id, + recommender.telegramId, + sellDetailsData, + tokenBalance + ); + + return sellDetailsData; + } + async isRapidDump( + tokenAddress: string, + tokenProvider: TokenProvider + ): Promise<boolean> { + const processedData: ProcessedTokenData = + await tokenProvider.getProcessedTokenData(); + console.log(`Fetched processed token data for token: ${tokenAddress}`); + + return processedData.tradeData.trade_24h_change_percent < -50; + } + + async delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + async updateTradeInBe( + tokenAddress: string, + recommenderId: string, + username: string, + data: sellDetails, + balanceLeft: number, + retries = 3, + delayMs = 2000 + ) { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + await fetch( + `${this.backend}/api/updaters/updateTradePerformance`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${this.backendToken}`, + }, + body: JSON.stringify({ + tokenAddress: tokenAddress, + tradeData: data, + recommenderId: recommenderId, + username: username, + isSimulation: true, + balanceLeft: balanceLeft, + }), + } + ); + // If the request is successful, exit the loop + return; + } catch (error) { + console.error( + `Attempt ${attempt} failed: Error creating trade in backend`, + error + ); + if (attempt < retries) { + console.log(`Retrying in ${delayMs} ms...`); + await this.delay(delayMs); // Wait for the specified delay before retrying + } else { + console.error("All attempts failed."); + } + } + } + } +} + +// SellDecision interface +interface SellDecision { + tokenPerformance: TokenPerformance; + amountToSell: number; +} diff --git a/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts b/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts index 5fd6e3d8bb7..302d194d610 100644 --- a/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts +++ b/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts @@ -827,6 +827,30 @@ export class TrustScoreDatabase { }; } + getAllTokenPerformancesWithBalance(): TokenPerformance[] { + const sql = `SELECT * FROM token_performance WHERE balance > 0;`; + const rows = this.db.prepare(sql).all() as TokenPerformanceRow[]; + + return rows.map((row) => ({ + tokenAddress: row.token_address, + priceChange24h: row.price_change_24h, + volumeChange24h: row.volume_change_24h, + trade_24h_change: row.trade_24h_change, + liquidity: row.liquidity, + liquidityChange24h: row.liquidity_change_24h, + holderChange24h: row.holder_change_24h, + rugPull: row.rug_pull === 1, + isScam: row.is_scam === 1, + marketCapChange24h: row.market_cap_change24h, + sustainedGrowth: row.sustained_growth === 1, + rapidDump: row.rapid_dump === 1, + suspiciousVolume: row.suspicious_volume === 1, + validationTrust: row.validation_trust, + balance: row.balance, + lastUpdated: new Date(row.last_updated), + })); + } + // ----- TokenRecommendations Methods ----- /** From e6c34560304fd45a235b21d490de0d17a005c6ce Mon Sep 17 00:00:00 2001 From: MarcoMandar <malicemandar@gmail.com> Date: Thu, 21 Nov 2024 02:27:46 +0200 Subject: [PATCH 4/6] update sell simulation to include stop loss and calculate amount to sell Signed-off-by: MarcoMandar <malicemandar@gmail.com> --- .../src/providers/simulationSellingService.ts | 83 +++++++++++++++++-- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/packages/plugin-solana/src/providers/simulationSellingService.ts b/packages/plugin-solana/src/providers/simulationSellingService.ts index fc626592538..0ab0c76b2b2 100644 --- a/packages/plugin-solana/src/providers/simulationSellingService.ts +++ b/packages/plugin-solana/src/providers/simulationSellingService.ts @@ -65,7 +65,7 @@ export class simulationSellingService { await this.executeSells(sellDecisions); // Perform stop loss checks - await this.performStopLoss(); + await this.performStopLoss(tokenPerformances); } private decideWhenToSell( @@ -75,27 +75,100 @@ export class simulationSellingService { console.log("Deciding when to sell and how much..."); const decisions: SellDecision[] = []; - tokenPerformances.forEach((performance) => { - const amountToSell = performance.balance * 0.1; + tokenPerformances.forEach(async (performance) => { + const tokenProvider = new TokenProvider( + performance.tokenAddress, + this.walletProvider + ); + const sellAmount = await this.amountToSell( + performance.tokenAddress, + tokenProvider + ); + const amountToSell = sellAmount.sellAmount; decisions.push({ tokenPerformance: performance, amountToSell }); }); return decisions; } + async amountToSell(tokenAddress: string, tokenProvider: TokenProvider) { + // To Do: Implement logic to decide how much to sell + //placeholder + const processedData: ProcessedTokenData = + await tokenProvider.getProcessedTokenData(); + const prices = await this.walletProvider.fetchPrices(null); + const solPrice = prices.solana.usd; + const tokenBalance = this.trustScoreDb.getTokenBalance(tokenAddress); + + const sellAmount = tokenBalance * 0.1; + const sellSol = sellAmount / parseFloat(solPrice); + const sellValueUsd = sellAmount * processedData.tradeData.price; + + return { sellAmount, sellSol, sellValueUsd }; + } + private async executeSells(decisions: SellDecision[]) { console.log("Executing sell orders..."); for (const decision of decisions) { console.log( `Selling ${decision.amountToSell} of token ${decision.tokenPerformance.tokenSymbol}` ); - // To Do sell logic + // update the sell details + const sellDetails = { + sell_amount: decision.amountToSell, + sell_recommender_id: null, + }; + const sellTimeStamp = new Date().toISOString(); + const tokenProvider = new TokenProvider( + decision.tokenPerformance.tokenAddress, + this.walletProvider + ); + const sellDetailsData = await this.updateSellDetails( + decision.tokenPerformance.tokenAddress, + decision.tokenPerformance.recommenderId, + sellTimeStamp, + sellDetails, + true, + tokenProvider + ); + console.log("Sell order executed successfully", sellDetailsData); } } - private async performStopLoss() { + private async performStopLoss(tokenPerformances: TokenPerformance[]) { console.log("Performing stop loss checks..."); // To Do: Implement stop loss logic + // check if the token has dropped by more than 50% in the last 24 hours + for (const performance of tokenPerformances) { + const tokenProvider = new TokenProvider( + performance.tokenAddress, + this.walletProvider + ); + const processedData: ProcessedTokenData = + await tokenProvider.getProcessedTokenData(); + if (processedData.tradeData.trade_24h_change_percent < -50) { + const sellAmount = performance.balance; + const sellSol = sellAmount / 100; + const sellValueUsd = sellAmount * processedData.tradeData.price; + const sellDetails = { + sell_amount: sellAmount, + sell_recommender_id: null, + }; + const sellTimeStamp = new Date().toISOString(); + const sellDetailsData = await this.updateSellDetails( + performance.tokenAddress, + performance.recommenderId, + sellTimeStamp, + sellDetails, + true, + tokenProvider + ); + console.log( + "Stop loss triggered. Sell order executed successfully", + sellDetailsData + ); + } + } } async updateSellDetails( From e240a1812ac467fd2670f59cd29c01343f3b68f3 Mon Sep 17 00:00:00 2001 From: MarcoMandar <malicemandar@gmail.com> Date: Fri, 22 Nov 2024 01:31:58 +0200 Subject: [PATCH 5/6] add rabbitMq, executeSellDecision Signed-off-by: MarcoMandar <malicemandar@gmail.com> --- package.json | 1 + .../src/providers/simulationSellingService.ts | 300 ++++++++++++------ .../src/providers/trustScoreProvider.ts | 13 + pnpm-lock.yaml | 39 ++- 4 files changed, 257 insertions(+), 96 deletions(-) diff --git a/package.json b/package.json index 5eccfc6e2fc..2d42e0e9bcd 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "node": ">=22" }, "dependencies": { + "amqplib": "^0.10.4", "ollama-ai-provider": "^0.16.1", "optional": "^0.1.4", "sharp": "^0.33.5" diff --git a/packages/plugin-solana/src/providers/simulationSellingService.ts b/packages/plugin-solana/src/providers/simulationSellingService.ts index 0ab0c76b2b2..cbfc6c7ce5f 100644 --- a/packages/plugin-solana/src/providers/simulationSellingService.ts +++ b/packages/plugin-solana/src/providers/simulationSellingService.ts @@ -11,8 +11,9 @@ import { TokenProvider } from "./token.ts"; import { settings } from "@ai16z/eliza"; import { IAgentRuntime, Memory, Provider, State } from "@ai16z/eliza"; import { WalletProvider } from "./wallet.ts"; +import * as amqp from "amqplib"; -interface sellDetails { +interface SellDetails { sell_amount: number; sell_recommender_id: string | null; } @@ -26,6 +27,12 @@ export class simulationSellingService { private MAX_DECAY_DAYS = 30; private backend: string; private backendToken: string; + private amqpConnection: amqp.Connection; + private amqpChannel: amqp.Channel; + private sonarBe: string; + private sonarBeToken: string; + + private runningProcesses: Set<string> = new Set(); constructor( runtime: IAgentRuntime, @@ -45,129 +52,234 @@ export class simulationSellingService { ); this.backend = runtime.getSetting("BACKEND_URL"); this.backendToken = runtime.getSetting("BACKEND_TOKEN"); + this.initializeRabbitMQ(runtime.getSetting("AMQP_URL")); + this.sonarBe = runtime.getSetting("SONAR_BE"); + this.sonarBeToken = runtime.getSetting("SONAR_BE_TOKEN"); } - - public async startService() { - // starting the service - console.log("Starting SellingService..."); - await this.scanAndSell(); + /** + * Initializes the RabbitMQ connection and starts consuming messages. + * @param amqpUrl The RabbitMQ server URL. + */ + private async initializeRabbitMQ(amqpUrl: string) { + try { + this.amqpConnection = await amqp.connect(amqpUrl); + this.amqpChannel = await this.amqpConnection.createChannel(); + console.log("Connected to RabbitMQ"); + // Start consuming messages + this.consumeMessages(); + } catch (error) { + console.error("Failed to connect to RabbitMQ:", error); + } } - private async scanAndSell() { - // scanning recommendations and selling - console.log("Scanning for token performances..."); - const tokenPerformances = - await this.trustScoreDb.getAllTokenPerformancesWithBalance(); - - const sellDecisions = this.decideWhenToSell(tokenPerformances); - - // Execute sells - await this.executeSells(sellDecisions); - - // Perform stop loss checks - await this.performStopLoss(tokenPerformances); + /** + * Sets up the consumer for the specified RabbitMQ queue. + */ + private async consumeMessages() { + const queue = "process_eliza_simulation"; + await this.amqpChannel.assertQueue(queue, { durable: true }); + this.amqpChannel.consume( + queue, + (msg) => { + if (msg !== null) { + const content = msg.content.toString(); + this.processMessage(content); + this.amqpChannel.ack(msg); + } + }, + { noAck: false } + ); + console.log(`Listening for messages on queue: ${queue}`); } - private decideWhenToSell( - tokenPerformances: TokenPerformance[] - ): SellDecision[] { - // To Do: logic when to sell and how much - console.log("Deciding when to sell and how much..."); - const decisions: SellDecision[] = []; - - tokenPerformances.forEach(async (performance) => { - const tokenProvider = new TokenProvider( - performance.tokenAddress, - this.walletProvider - ); - const sellAmount = await this.amountToSell( - performance.tokenAddress, - tokenProvider + /** + * Processes incoming messages from RabbitMQ. + * @param message The message content as a string. + */ + private async processMessage(message: string) { + try { + const { tokenAddress, amount, sell_recommender_id } = + JSON.parse(message); + console.log( + `Received message for token ${tokenAddress} to sell ${amount}` ); - const amountToSell = sellAmount.sellAmount; - decisions.push({ tokenPerformance: performance, amountToSell }); - }); - - return decisions; - } - async amountToSell(tokenAddress: string, tokenProvider: TokenProvider) { - // To Do: Implement logic to decide how much to sell - //placeholder - const processedData: ProcessedTokenData = - await tokenProvider.getProcessedTokenData(); - const prices = await this.walletProvider.fetchPrices(null); - const solPrice = prices.solana.usd; - const tokenBalance = this.trustScoreDb.getTokenBalance(tokenAddress); + const decision: SellDecision = { + tokenPerformance: + await this.trustScoreDb.getTokenPerformance(tokenAddress), + amountToSell: amount, + sell_recommender_id: sell_recommender_id, + }; - const sellAmount = tokenBalance * 0.1; - const sellSol = sellAmount / parseFloat(solPrice); - const sellValueUsd = sellAmount * processedData.tradeData.price; + // Execute the sell + await this.executeSellDecision(decision); - return { sellAmount, sellSol, sellValueUsd }; + // Remove from running processes after completion + this.runningProcesses.delete(tokenAddress); + } catch (error) { + console.error("Error processing message:", error); + } } - private async executeSells(decisions: SellDecision[]) { - console.log("Executing sell orders..."); - for (const decision of decisions) { + /** + * Executes a single sell decision. + * @param decision The sell decision containing token performance and amount to sell. + */ + private async executeSellDecision(decision: SellDecision) { + const { tokenPerformance, amountToSell, sell_recommender_id } = + decision; + const tokenAddress = tokenPerformance.tokenAddress; + + try { console.log( - `Selling ${decision.amountToSell} of token ${decision.tokenPerformance.tokenSymbol}` + `Executing sell for token ${tokenPerformance.tokenSymbol}: ${amountToSell}` ); - // update the sell details - const sellDetails = { - sell_amount: decision.amountToSell, - sell_recommender_id: null, + + // Update the sell details + const sellDetails: SellDetails = { + sell_amount: amountToSell, + sell_recommender_id: sell_recommender_id, // Adjust if necessary }; const sellTimeStamp = new Date().toISOString(); const tokenProvider = new TokenProvider( - decision.tokenPerformance.tokenAddress, + tokenAddress, this.walletProvider ); + + // Update sell details in the database const sellDetailsData = await this.updateSellDetails( - decision.tokenPerformance.tokenAddress, - decision.tokenPerformance.recommenderId, + tokenAddress, + tokenPerformance.recommenderId, sellTimeStamp, sellDetails, - true, + true, // isSimulation tokenProvider ); + console.log("Sell order executed successfully", sellDetailsData); + + // check if balance is zero and remove token from running processes + const balance = this.trustScoreDb.getTokenBalance(tokenAddress); + if (balance === 0) { + this.runningProcesses.delete(tokenAddress); + } + // stop the process in the sonar backend + await this.stopProcessInTheSonarBackend(tokenAddress); + } catch (error) { + console.error( + `Error executing sell for token ${tokenAddress}:`, + error + ); } } - private async performStopLoss(tokenPerformances: TokenPerformance[]) { - console.log("Performing stop loss checks..."); - // To Do: Implement stop loss logic - // check if the token has dropped by more than 50% in the last 24 hours - for (const performance of tokenPerformances) { + public async startService() { + // starting the service + console.log("Starting SellingService..."); + await this.startListeners(); + } + + private async startListeners() { + // scanning recommendations and selling + console.log("Scanning for token performances..."); + const tokenPerformances = + await this.trustScoreDb.getAllTokenPerformancesWithBalance(); + + await this.processTokenPerformances(tokenPerformances); + } + + private processTokenPerformances(tokenPerformances: TokenPerformance[]) { + // To Do: logic when to sell and how much + console.log("Deciding when to sell and how much..."); + const runningProcesses = this.runningProcesses; + // remove running processes from tokenPerformances + tokenPerformances = tokenPerformances.filter( + (tp) => !runningProcesses.has(tp.tokenAddress) + ); + + // start the process in the sonar backend + tokenPerformances.forEach(async (tokenPerformance) => { const tokenProvider = new TokenProvider( - performance.tokenAddress, + tokenPerformance.tokenAddress, this.walletProvider ); - const processedData: ProcessedTokenData = - await tokenProvider.getProcessedTokenData(); - if (processedData.tradeData.trade_24h_change_percent < -50) { - const sellAmount = performance.balance; - const sellSol = sellAmount / 100; - const sellValueUsd = sellAmount * processedData.tradeData.price; - const sellDetails = { - sell_amount: sellAmount, - sell_recommender_id: null, - }; - const sellTimeStamp = new Date().toISOString(); - const sellDetailsData = await this.updateSellDetails( - performance.tokenAddress, - performance.recommenderId, - sellTimeStamp, - sellDetails, - true, - tokenProvider + const shouldTrade = await tokenProvider.shouldTradeToken(); + if (shouldTrade) { + const balance = tokenPerformance.balance; + const sell_recommender_id = tokenPerformance.recommenderId; + const tokenAddress = tokenPerformance.tokenAddress; + const process = await this.startProcessInTheSonarBackend( + tokenAddress, + balance, + sell_recommender_id ); - console.log( - "Stop loss triggered. Sell order executed successfully", - sellDetailsData + if (process) { + this.runningProcesses.add(tokenAddress); + } + } + }); + } + + private async startProcessInTheSonarBackend( + tokenAddress: string, + balance: number, + sell_recommender_id: string + ) { + try { + const message = JSON.stringify({ + tokenAddress, + balance, + sell_recommender_id, + }); + const response = await fetch( + `${this.sonarBe}/api/simulation/sell`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${this.sonarBeToken}`, + }, + body: message, + } + ); + + if (!response.ok) { + console.error( + `Failed to send message to process token ${tokenAddress}` ); + return; } + + const result = await response.json(); + console.log("Received response:", result); + console.log(`Sent message to process token ${tokenAddress}`); + + return result; + } catch (error) { + console.error( + `Error sending message to process token ${tokenAddress}:`, + error + ); + return null; + } + } + + private stopProcessInTheSonarBackend(tokenAddress: string) { + try { + return fetch( + `${this.sonarBe}/api/simulation/sell/${tokenAddress}`, + { + method: "GET", + headers: { + Authorization: `Bearer ${this.sonarBeToken}`, + }, + } + ); + } catch (error) { + console.error( + `Error stopping process for token ${tokenAddress}:`, + error + ); } } @@ -175,11 +287,10 @@ export class simulationSellingService { tokenAddress: string, recommenderId: string, sellTimeStamp: string, - sellDetails: sellDetails, + sellDetails: SellDetails, isSimulation: boolean, tokenProvider: TokenProvider ) { - // To Do: Change the logic after codex update const recommender = await this.trustScoreDb.getOrCreateRecommenderWithTelegramId( recommenderId @@ -278,7 +389,7 @@ export class simulationSellingService { tokenAddress: string, recommenderId: string, username: string, - data: sellDetails, + data: SellDetails, balanceLeft: number, retries = 3, delayMs = 2000 @@ -325,4 +436,5 @@ export class simulationSellingService { interface SellDecision { tokenPerformance: TokenPerformance; amountToSell: number; + sell_recommender_id: string | null; } diff --git a/packages/plugin-solana/src/providers/trustScoreProvider.ts b/packages/plugin-solana/src/providers/trustScoreProvider.ts index 4478e2c94b6..7e05dfa57e0 100644 --- a/packages/plugin-solana/src/providers/trustScoreProvider.ts +++ b/packages/plugin-solana/src/providers/trustScoreProvider.ts @@ -389,6 +389,19 @@ export class TrustScoreManager { }; this.trustScoreDb.addTradePerformance(creationData, data.is_simulation); + const tokenRecommendation: TokenRecommendation = { + recommenderId: recommenderId, + tokenAddress: tokenAddress, + timestamp: new Date(), + initialMarketCap: + processedData.dexScreenerData.pairs[0]?.marketCap || 0, + initialLiquidity: + processedData.dexScreenerData.pairs[0]?.liquidity || 0, + initialPrice: processedData.tradeData.price, + }; + + this.trustScoreDb.addTokenRecommendation(tokenRecommendation); + if (data.is_simulation) { // If the trade is a simulation update the balance this.trustScoreDb.updateTokenBalance(tokenAddress, tokensBalance); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f85cfec055d..37c212913a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: .: dependencies: + amqplib: + specifier: ^0.10.4 + version: 0.10.4 ollama-ai-provider: specifier: ^0.16.1 version: 0.16.1(zod@3.23.8) @@ -621,7 +624,7 @@ importers: version: 2.79.2 ts-jest: specifier: 29.2.5 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.4)(ts-node@10.9.2(@swc/core@1.9.2(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.0)(jest@29.7.0(@types/node@22.8.4)(ts-node@10.9.2(@swc/core@1.9.2(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)))(typescript@5.6.3) ts-node: specifier: 10.9.2 version: 10.9.2(@swc/core@1.9.2(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3) @@ -933,6 +936,10 @@ importers: packages: + '@acuminous/bitsyntax@0.1.2': + resolution: {integrity: sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==} + engines: {node: '>=0.8'} + '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} @@ -2226,6 +2233,7 @@ packages: resolution: {integrity: sha512-hArn9FF5ZYi1IkxdJEVnJi+OxlwLV0NJYWpKXsmNOojtGtAZHxmsELA+MZlu2KW1F/K1/nt7lFOfcMXNYweq9w==} version: 0.17.0 engines: {node: '>=16.11.0'} + deprecated: This version uses deprecated encryption modes. Please use a newer version. '@discordjs/ws@1.1.1': resolution: {integrity: sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==} @@ -5061,6 +5069,10 @@ packages: amp@0.3.1: resolution: {integrity: sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==} + amqplib@0.10.4: + resolution: {integrity: sha512-DMZ4eCEjAVdX1II2TfIUpJhfKAuoCeDIo/YyETbfAqehHTXxxs7WOOd+N1Xxr4cKhx12y23zk8/os98FxlZHrw==} + engines: {node: '>=10'} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -5476,6 +5488,9 @@ packages: resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} engines: {node: '>=4.5'} + buffer-more-ints@1.0.0: + resolution: {integrity: sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==} + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -12977,6 +12992,14 @@ packages: snapshots: + '@acuminous/bitsyntax@0.1.2': + dependencies: + buffer-more-ints: 1.0.0 + debug: 4.3.7(supports-color@5.5.0) + safe-buffer: 5.1.2 + transitivePeerDependencies: + - supports-color + '@adraffy/ens-normalize@1.10.1': {} '@ai-sdk/anthropic@0.0.53(zod@3.23.8)': @@ -18544,6 +18567,15 @@ snapshots: amp@0.3.1: {} + amqplib@0.10.4: + dependencies: + '@acuminous/bitsyntax': 0.1.2 + buffer-more-ints: 1.0.0 + readable-stream: 1.1.14 + url-parse: 1.5.10 + transitivePeerDependencies: + - supports-color + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -19001,6 +19033,8 @@ snapshots: buffer-layout@1.2.2: {} + buffer-more-ints@1.0.0: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -27140,7 +27174,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.4)(ts-node@10.9.2(@swc/core@1.9.2(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.0)(jest@29.7.0(@types/node@22.8.4)(ts-node@10.9.2(@swc/core@1.9.2(@swc/helpers@0.5.15))(@types/node@22.8.4)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -27158,6 +27192,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.26.0) + esbuild: 0.24.0 ts-mixer@6.0.4: {} From 6d6296ebf8a496ccf563a0561acdc662e082b257 Mon Sep 17 00:00:00 2001 From: MarcoMandar <malicemandar@gmail.com> Date: Mon, 25 Nov 2024 23:20:12 +0200 Subject: [PATCH 6/6] updates for the backend Signed-off-by: MarcoMandar <malicemandar@gmail.com> --- .../src/providers/simulationSellingService.ts | 30 ++++++++++--------- .../src/providers/trustScoreProvider.ts | 24 ++++++++++++++- .../src/adapters/trustScoreDatabase.ts | 20 +++++++++++++ 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/packages/plugin-solana/src/providers/simulationSellingService.ts b/packages/plugin-solana/src/providers/simulationSellingService.ts index cbfc6c7ce5f..d3d7ce267d3 100644 --- a/packages/plugin-solana/src/providers/simulationSellingService.ts +++ b/packages/plugin-solana/src/providers/simulationSellingService.ts @@ -211,7 +211,8 @@ export class simulationSellingService { const process = await this.startProcessInTheSonarBackend( tokenAddress, balance, - sell_recommender_id + sell_recommender_id, + tokenPerformance.initial_mc ); if (process) { this.runningProcesses.add(tokenAddress); @@ -223,21 +224,23 @@ export class simulationSellingService { private async startProcessInTheSonarBackend( tokenAddress: string, balance: number, - sell_recommender_id: string + isSimulation: boolean, + initial_mc: number ) { try { const message = JSON.stringify({ tokenAddress, balance, - sell_recommender_id, + isSimulation, + initial_mc, }); const response = await fetch( - `${this.sonarBe}/api/simulation/sell`, + `${this.sonarBe}/ai16z-sol/startProcess`, { method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${this.sonarBeToken}`, + "x-api-key": `${this.sonarBeToken}`, }, body: message, } @@ -266,15 +269,14 @@ export class simulationSellingService { private stopProcessInTheSonarBackend(tokenAddress: string) { try { - return fetch( - `${this.sonarBe}/api/simulation/sell/${tokenAddress}`, - { - method: "GET", - headers: { - Authorization: `Bearer ${this.sonarBeToken}`, - }, - } - ); + return fetch(`${this.sonarBe}/ai16z-sol/stopProcess`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": `${this.sonarBeToken}`, + }, + body: JSON.stringify({ tokenAddress }), + }); } catch (error) { console.error( `Error stopping process for token ${tokenAddress}:`, diff --git a/packages/plugin-solana/src/providers/trustScoreProvider.ts b/packages/plugin-solana/src/providers/trustScoreProvider.ts index 7e05dfa57e0..73ba858baef 100644 --- a/packages/plugin-solana/src/providers/trustScoreProvider.ts +++ b/packages/plugin-solana/src/providers/trustScoreProvider.ts @@ -399,9 +399,31 @@ export class TrustScoreManager { processedData.dexScreenerData.pairs[0]?.liquidity || 0, initialPrice: processedData.tradeData.price, }; - this.trustScoreDb.addTokenRecommendation(tokenRecommendation); + this.trustScoreDb.upsertTokenPerformance({ + tokenAddress: tokenAddress, + priceChange24h: processedData.tradeData.price_change_24h_percent, + volumeChange24h: processedData.tradeData.volume_24h, + trade_24h_change: processedData.tradeData.trade_24h_change_percent, + liquidity: + processedData.dexScreenerData.pairs[0]?.liquidity.usd || 0, + liquidityChange24h: 0, + holderChange24h: + processedData.tradeData.unique_wallet_24h_change_percent, + rugPull: false, + isScam: false, + marketCapChange24h: 0, + sustainedGrowth: false, + rapidDump: false, + suspiciousVolume: false, + validationTrust: 0, + balance: tokensBalance, + initialMarketCap: + processedData.dexScreenerData.pairs[0]?.marketCap || 0, + lastUpdated: new Date(), + }); + if (data.is_simulation) { // If the trade is a simulation update the balance this.trustScoreDb.updateTokenBalance(tokenAddress, tokensBalance); diff --git a/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts b/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts index 302d194d610..b877d52e8b5 100644 --- a/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts +++ b/packages/plugin-trustdb/src/adapters/trustScoreDatabase.ts @@ -41,6 +41,7 @@ export interface TokenPerformance { suspiciousVolume: boolean; validationTrust: number; balance: number; + initialMarketCap: number; lastUpdated: Date; } @@ -122,6 +123,7 @@ interface TokenPerformanceRow { suspicious_volume: number; validation_trust: number; balance: number; + initial_market_cap: number; last_updated: string; } @@ -205,6 +207,7 @@ export class TrustScoreDatabase { suspicious_volume BOOLEAN DEFAULT FALSE, validation_trust REAL DEFAULT 0, balance REAL DEFAULT 0, + initial_market_cap REAL DEFAULT 0, last_updated DATETIME DEFAULT CURRENT_TIMESTAMP ); `); @@ -731,6 +734,8 @@ export class TrustScoreDatabase { rapid_dump, suspicious_volume, validation_trust, + balance, + initial_market_cap, last_updated ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP) ON CONFLICT(token_address) DO UPDATE SET @@ -747,6 +752,8 @@ export class TrustScoreDatabase { rapid_dump = excluded.rapid_dump, suspicious_volume = excluded.suspicious_volume, validation_trust = excluded.validation_trust, + balance = excluded.balance, + initial_market_cap = excluded.initial_market_cap, last_updated = CURRENT_TIMESTAMP; `; try { @@ -764,6 +771,8 @@ export class TrustScoreDatabase { performance.sustainedGrowth ? 1 : 0, performance.rapidDump ? 1 : 0, performance.suspiciousVolume ? 1 : 0, + performance.balance, + performance.initialMarketCap, validationTrust ); console.log( @@ -823,10 +832,20 @@ export class TrustScoreDatabase { suspiciousVolume: row.suspicious_volume === 1, validationTrust: row.validation_trust, balance: row.balance, + initialMarketCap: row.initial_market_cap, lastUpdated: new Date(row.last_updated), }; } + //getTokenBalance + getTokenBalance(tokenAddress: string): number { + const sql = `SELECT balance FROM token_performance WHERE token_address = ?;`; + const row = this.db.prepare(sql).get(tokenAddress) as { + balance: number; + }; + return row.balance; + } + getAllTokenPerformancesWithBalance(): TokenPerformance[] { const sql = `SELECT * FROM token_performance WHERE balance > 0;`; const rows = this.db.prepare(sql).all() as TokenPerformanceRow[]; @@ -847,6 +866,7 @@ export class TrustScoreDatabase { suspiciousVolume: row.suspicious_volume === 1, validationTrust: row.validation_trust, balance: row.balance, + initialMarketCap: row.initial_market_cap, lastUpdated: new Date(row.last_updated), })); }