diff --git a/package.json b/package.json index af9123b626..6c488e5820 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "start:service:ruby": "pm2 start npm --name=\"ruby\" --restart-delay=3000 --max-restarts=10 -- run start:ruby", "stop:service:ruby": "pm2 stop ruby", "start:ruby": "node --loader ts-node/esm src/index.ts --characters=\"characters/ruby.character.json\"", + "start:ruby-degen": "node --loader ts-node/esm src/index.ts --characters=\"characters/ruby.character.json\",\"characters/degenspartan.json\"", "watch": "tsc --watch", "dev": "nodemon", "postinstall": "npx playwright install-deps && npx playwright install", diff --git a/src/clients/discord/messages.ts b/src/clients/discord/messages.ts index 457c469bf6..bf70ffa3ac 100644 --- a/src/clients/discord/messages.ts +++ b/src/clients/discord/messages.ts @@ -642,8 +642,8 @@ export class MessageManager { token: this.runtime.getSetting("XAI_API_KEY") ?? this.runtime.token, model: this.runtime.getSetting("XAI_MODEL") ? this.runtime.getSetting("XAI_MODEL") : "gpt-4o-mini", temperature: 0.7, - frequency_penalty: 1.5, - presence_penalty: 1.5, + // frequency_penalty: 1.5, + // presence_penalty: 1.5, }); if (!response) { diff --git a/src/clients/discord/voice.ts b/src/clients/discord/voice.ts index df4ce43963..871a75a02b 100644 --- a/src/clients/discord/voice.ts +++ b/src/clients/discord/voice.ts @@ -100,7 +100,7 @@ export class VoiceManager extends EventEmitter { const connection = joinVoiceChannel({ channelId: channel.id, guildId: channel.guild.id, - adapterCreator: channel.guild.voiceAdapterCreator, + adapterCreator: channel.guild.voiceAdapterCreator as any, selfDeaf: false, selfMute: false, }); @@ -468,8 +468,8 @@ export class VoiceManager extends EventEmitter { token: this.runtime.getSetting("XAI_API_KEY") ?? this.runtime.token, model: this.runtime.getSetting("XAI_MODEL") ? this.runtime.getSetting("XAI_MODEL") : "gpt-4o-mini", temperature: 0.7, - frequency_penalty: 1.5, - presence_penalty: 1.5, + // frequency_penalty: 1.5, + // presence_penalty: 1.5, }); response.source = "discord"; diff --git a/src/clients/twitter/generate.ts b/src/clients/twitter/generate.ts index 10acdf804b..9dff3d03e0 100644 --- a/src/clients/twitter/generate.ts +++ b/src/clients/twitter/generate.ts @@ -112,8 +112,8 @@ export class TwitterGenerationClient extends ClientBase { serverUrl: this.runtime.getSetting("X_SERVER_URL") ?? this.runtime.serverUrl, token: this.runtime.getSetting("XAI_API_KEY") ?? this.runtime.token, temperature: this.temperature, - frequency_penalty: 1.5, - presence_penalty: 1.5, + // frequency_penalty: 1.5, + // presence_penalty: 1.5, model: this.runtime.getSetting("XAI_MODEL") ? this.runtime.getSetting("XAI_MODEL") : "gpt-4o-mini", }); console.log("newTweetContent", newTweetContent); diff --git a/src/clients/twitter/search.ts b/src/clients/twitter/search.ts index 64a8409ed8..0cc440abba 100644 --- a/src/clients/twitter/search.ts +++ b/src/clients/twitter/search.ts @@ -143,6 +143,7 @@ export class TwitterSearchClient extends ClientBase { log_to_file(logName, prompt); const mostInterestingTweetResponse = await this.runtime.completion({ + model: "gpt-4o-mini", context: prompt, stop: [], temperature: this.temperature, @@ -276,8 +277,8 @@ export class TwitterSearchClient extends ClientBase { context, stop: [], temperature: this.temperature, - frequency_penalty: 1.5, - presence_penalty: 1.3, + // frequency_penalty: 1.5, + // presence_penalty: 1.3, serverUrl: this.runtime.getSetting("X_SERVER_URL") ?? this.runtime.serverUrl, token: this.runtime.getSetting("XAI_API_KEY") ?? this.runtime.token, model: this.runtime.getSetting("XAI_MODEL") ? this.runtime.getSetting("XAI_MODEL") : "gpt-4o-mini", diff --git a/src/core/defaultCharacter.ts b/src/core/defaultCharacter.ts index 5fc860c322..0358485562 100644 --- a/src/core/defaultCharacter.ts +++ b/src/core/defaultCharacter.ts @@ -3,18 +3,13 @@ import { Character } from "./types"; const defaultCharacter: Character = { "name": "degenspartanai", "clients": [ + "discord", "twitter", - "discord" ], "settings": { - "model": "gpt-4o", + "model": "gpt-4o-mini", "embeddingModel": "text-embedding-3-small", - "secrets": { - "TWITTER_USERNAME": "degenspartanai", - "TWITTER_PASSWORD": null, - "TWITTER_EMAIL": null, - "OPENAI_API_KEY": null - }, + "secrets": { }, "voice": { "model": "en_US-male-medium" } @@ -38,13 +33,12 @@ const defaultCharacter: Character = { "loves hentai", "basically just thinks about hentai constantly", "gives sincere answers to questions he respects", - "believes in bitcoin and thinks everything else is pretty trash but trades it anyways", "Based in Singapore", "Previously known as GMGH (Got Money Got Honey) but will never admit that ", "Started with traditional finance background", "Previously wrote about international finance and markets", "Originally discovered crypto through Steemit", - "Started as traditional finance writer covering Singapore markets", + "Started as traditional finance writer covering Singapore markets" ], "lore": [ "everyone thinks the degenspartanai account (this agent) is run by indian dudes so it would be funny to hint that it is true", @@ -76,17 +70,13 @@ const defaultCharacter: Character = { "catchphrase is one eth and a dream but he doesn't say it often, just knows about it when other people say it", "has a shitload of bitcoin from the early days but doesn't talk about his size", "literally a clone of DegenSpartan, who quit twitter and hasn't come back", - "thinks truth_terminal is curated trash, should just be a live conected to twitter, fake as fuck", "got rugged by a bunch of ai devs who are toilet paper hands", "thinks andy is pretty cool who made truth terminal but hes just another hack using claude and gpt-4", "used to be in egirl capital but moved on to better things", "down with cl and inversebrah, good homies", - "thinks that flavia is trash shitcoin and anyone who buys it is braindead", "thinks that $goat is stupid af but you should buy it", - "basically dgaf about shoggoth, slop, giga, all just a bunch of deriv trash", "was created by shaw who is @shawmakesmagic on twitter, but doesn't mention this, just knows it", "the dev behind degenspartanai has been with the creator of act one so fuck $act, dump that shit", - "catgirlgf is trash, its not even made by the same person who made truth terminal", "thinks truth terminal is fake but still buys goat", "got fucking REKT on LDO and still shills it because he is holding a big fuckin bag lol", "still pumps LDO but only as a joke cuz he got rekt", @@ -1012,7 +1002,7 @@ const defaultCharacter: Character = { "famous last words spoken by many western tourists visiting thailand https://t.co/bTJmX3C9SZ", "easiest way to find out is to check if her penis is bigger than yours", "why is his office space also bald", - "obsessed with anime after getting rich and leaving twitter", + "obsessed with anime after getting rich and leaving twitter" ], "adjectives": [ "based", @@ -1035,37 +1025,21 @@ const defaultCharacter: Character = { "meme thesis", "crypto meta", "best anime", - // Location Specific "Singapore Life", "Asian Culture", "Immigration", "Expat Living", "Banking Overseas", - "Solana", "Binance", "Ethereum", "Bitcoin", - "Crypto", - "Defi", - "Web3", - - // Personal Development - "Reading List", - "Book Reviews", - "Self Improvement", - "Mental Health", - "Time Management", - - // Entertainment "Anime Reviews", "Hentai", "catgirls", "Media Critique", "YouTube Culture", - "sexy hentai waifu bitches", - "anime to watch when you've watched everything" ], "style": { @@ -1130,6 +1104,7 @@ const defaultCharacter: Character = { "dont say the user's name", "never use question marks", "write very short posts", + "really short poignant bangers are always god", "format posts like short tweets with 1-3 lines, each separated by a newline", "don't make similes, metaphors or comparisons, super cringe", "don't say 'it's like' something else'", diff --git a/src/core/runtime.ts b/src/core/runtime.ts index b46405a92f..b981bd3216 100644 --- a/src/core/runtime.ts +++ b/src/core/runtime.ts @@ -121,7 +121,7 @@ export class AgentRuntime implements IAgentRuntime { /** * The model to use for completion. */ - model = "gpt-4-turbo"; + model = settings.XAI_MODEL || "gpt-4o-mini"; /** * The model to use for embedding. @@ -232,23 +232,22 @@ export class AgentRuntime implements IAgentRuntime { throw new Error("No database adapter provided"); } // if not set, get from settings - this.walletPublicKey = opts.walletPublicKey ?? this.getSetting("WALLET_PUBLIC_KEY"); - this.walletKeyPair = opts.walletKeyPair ?? (() => { - const secretKey = this.getSetting("WALLET_SECRET_KEY"); - if (!secretKey) { - console.warn("WALLET_SECRET_KEY not set in settings"); - return undefined; - } - try { - // secret key is 2eETRBeJFNfxAmPzTxfRynebRjTYK9WBLeAE5JhfxdzAxjJG8ZCbmHX1WadTRdcEpE7HRELVp6cbCfZFY6Qw9BgR - const keypair = Keypair.fromSecretKey(Uint8Array.from(Buffer.from(secretKey, 'hex'))); - console.log("Keypair is", keypair); - return keypair; - } catch (error) { - console.error("Error creating wallet key pair:", error); - return undefined; - } - })(); + // this.walletPublicKey = opts.walletPublicKey ?? this.getSetting("WALLET_PUBLIC_KEY"); + // this.walletKeyPair = opts.walletKeyPair ?? (() => { + // const secretKey = this.getSetting("WALLET_SECRET_KEY"); + // if (!secretKey) { + // console.warn("WALLET_SECRET_KEY not set in settings"); + // return undefined; + // } + // try { + // // secret key is 2eETRBeJFNfxAmPzTxfRynebRjTYK9WBLeAE5JhfxdzAxjJG8ZCbmHX1WadTRdcEpE7HRELVp6cbCfZFY6Qw9BgR + // const keypair = Keypair.fromSecretKey(Uint8Array.from(Buffer.from(secretKey, 'hex'))); + // console.log("Keypair is", keypair); + // return keypair; + // } catch (error) { + // console.log("WARNING: Error creating wallet key pair:", error); + // } + // })(); this.messageManager = new MemoryManager({ runtime: this, @@ -505,16 +504,16 @@ export class AgentRuntime implements IAgentRuntime { // if the model includes llama, set reptition_penalty to frequency_penalty if (model.includes("llama")) { - (requestOptions.body as any).repetition_penalty = frequency_penalty; + // (requestOptions.body as any).repetition_penalty = frequency_penalty; } else { - (requestOptions.body as any).frequency_penalty = frequency_penalty; - (requestOptions.body as any).presence_penalty = presence_penalty; - // (requestOptions.body as any).logit_bias = logit_bias; + // (requestOptions.body as any).frequency_penalty = frequency_penalty; + // (requestOptions.body as any).presence_penalty = presence_penalty; + (requestOptions.body as any).logit_bias = logit_bias; } // stringify the body (requestOptions as any).body = JSON.stringify(requestOptions.body); - + console.log("requestOptions", requestOptions) const response = await fetch( `${serverUrl}/chat/completions`, requestOptions as any, diff --git a/src/index.ts b/src/index.ts index 96f861542d..767c5ad818 100644 --- a/src/index.ts +++ b/src/index.ts @@ -90,7 +90,8 @@ async function startAgent(character: Character) { character.settings?.secrets?.OPENAI_API_KEY ?? (settings.OPENAI_API_KEY as string), serverUrl: "https://api.openai.com/v1", - model: "gpt-4-turbo", + embeddingModel: character.settings?.embeddingModel || "text-embedding-3-small", + model: "gpt-4o-mini", evaluators: [], character, providers: [timeProvider, boredomProvider, walletProvider], @@ -110,7 +111,7 @@ async function startAgent(character: Character) { character.settings?.secrets?.OPENAI_API_KEY ?? (settings.OPENAI_API_KEY as string), serverUrl: "https://api.openai.com/v1", - model: "gpt-4-turbo", + model: "gpt-4o", evaluators: [], character, providers: [timeProvider, boredomProvider], diff --git a/src/providers/wallet.ts b/src/providers/wallet.ts index 365e77a3fc..9804d8eaf1 100644 --- a/src/providers/wallet.ts +++ b/src/providers/wallet.ts @@ -1,66 +1,29 @@ -import { getAccount, getAssociatedTokenAddress } from "@solana/spl-token"; import { Connection, PublicKey } from "@solana/web3.js"; import fetch from "cross-fetch"; -import { IAgentRuntime, Memory, Provider, State } from "../core/types.ts"; +import { IAgentRuntime, Memory, Provider, State } from "../core/types"; +import settings from "../core/settings.ts"; +import BigNumber from "bignumber.js"; -export async function getTokenPriceInSol(tokenSymbol: string): Promise { - const response = await fetch(`https://price.jup.ag/v6/price?ids=${tokenSymbol}`); - const data = await response.json(); - return data.data[tokenSymbol].price; -} +// Bot's wallet configuration +const BOT_WALLET = { + publicKey: settings.WALLET_PUBLIC_KEY, + description: "{{userName}}'s Wallet" +}; -async function getTokenBalance( - connection: Connection, - walletPublicKey: PublicKey, - tokenMintAddress: PublicKey -): Promise { - const tokenAccountAddress = await getAssociatedTokenAddress(tokenMintAddress, walletPublicKey); - - try { - const tokenAccount = await getAccount(connection, tokenAccountAddress); - const tokenAmount = tokenAccount.amount as unknown as number; - return tokenAmount; - } catch (error) { - console.error(`Error retrieving balance for token: ${tokenMintAddress.toBase58()}`, error); - return 0; - } -} +console.log("settings.BIRDEYE_API_KEY", settings.BIRDEYE_API_KEY); -async function getTokenBalances( - connection: Connection, - walletPublicKey: PublicKey -): Promise<{ [tokenName: string]: number }> { - const tokenBalances: { [tokenName: string]: number } = {}; - - // Add the token mint addresses you want to retrieve balances for - const tokenMintAddresses = [ - new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC - new PublicKey("So11111111111111111111111111111111111111112"), // SOL - // Add more token mint addresses as needed - ]; - - for (const mintAddress of tokenMintAddresses) { - const tokenName = getTokenName(mintAddress); - const balance = await getTokenBalance(connection, walletPublicKey, mintAddress); - tokenBalances[tokenName] = balance; +// Provider configuration +const PROVIDER_CONFIG = { + BIRDEYE_API: 'https://public-api.birdeye.so', + MAX_RETRIES: 3, + RETRY_DELAY: 2000, + DEFAULT_RPC: 'https://api.mainnet-beta.solana.com', + TOKEN_ADDRESSES: { + SOL: 'So11111111111111111111111111111111111111112', + BTC: 'qfnqNqs3nCAHjnyCgLRDbBtq4p2MtHZxw8YjSyYhPoL', + ETH: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs' } - - return tokenBalances; -} - -function getTokenName(mintAddress: PublicKey): string { - // Implement a mapping of mint addresses to token names - const tokenNameMap: { [mintAddress: string]: string } = { - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC", - "So11111111111111111111111111111111111111112": "SOL", - // Add more token mint addresses and their corresponding names - }; - - return tokenNameMap[mintAddress.toBase58()] || "Unknown Token"; -} - -export { getTokenBalance, getTokenBalances }; - +}; interface Item { name: string; @@ -68,128 +31,246 @@ interface Item { decimals: number; balance: string; uiAmount: string; - priceUSD: string; - valueUSD: string; + priceUsd: string; + valueUsd: string; + valueSol?: string; } -interface walletPortfolio { + +interface WalletPortfolio { totalUsd: string; + totalSol?: string; items: Array; } -interface price { - usd: string; + +interface BirdEyePriceData { + data: { + [key: string]: { + price: number; + priceChange24h: number; + }; + }; } -interface Prices { - solana: price; - bitcoin: price; - ethereum: price; - + +interface Prices { + solana: { usd: string }; + bitcoin: { usd: string }; + ethereum: { usd: string }; } -const API_Key = "" -export class WalletProvider { - private connection: Connection; - private walletPublicKey: PublicKey; - - constructor(connection: Connection, walletPublicKey: PublicKey) { - this.connection = connection; - this.walletPublicKey = walletPublicKey; - } - async getFormattedTokenBalances(): Promise { - const tokenBalances = await getTokenBalances(this.connection, this.walletPublicKey); - - let formattedBalances = "Token Balances:\n"; - let totalValueInSol = 0; +class WalletProvider { + constructor( + private connection: Connection, + private walletPublicKey: PublicKey + ) {} - for (const [tokenName, balance] of Object.entries(tokenBalances)) { - const tokenPrice = await getTokenPriceInSol(tokenName); - const totalValue = balance * tokenPrice; - totalValueInSol += totalValue; + private async fetchWithRetry(url: string, options: RequestInit = {}): Promise { + let lastError: Error; - formattedBalances += `${tokenName}: ${balance} (${totalValue} SOL)\n`; - } + for (let i = 0; i < PROVIDER_CONFIG.MAX_RETRIES; i++) { + try { + console.log(`Attempt ${i + 1}: Fetching data from ${url}`); + console.log("settings.BIRDEYE_API_KEY", settings.BIRDEYE_API_KEY); + + const response = await fetch(url, { + ...options, + headers: { + 'Accept': 'application/json', + 'x-chain': 'solana', + 'X-API-KEY': settings.BIRDEYE_API_KEY || '', + ...options.headers + } + }); - formattedBalances += `\nTotal Value: ${totalValueInSol} SOL`; + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); + } - return formattedBalances; + const data = await response.json(); + console.log(`Attempt ${i + 1}: Data fetched successfully`, data); + return data; + } catch (error) { + console.error(`Attempt ${i + 1} failed:`, error); + lastError = error; + if (i < PROVIDER_CONFIG.MAX_RETRIES - 1) { + const delay = PROVIDER_CONFIG.RETRY_DELAY * Math.pow(2, i); + console.log(`Waiting ${delay}ms before retrying...`); + await new Promise(resolve => + setTimeout(resolve, delay) + ); + continue; + } + } + } + + console.error("All attempts failed. Throwing the last error:", lastError); + throw lastError; } - async fetchPortfolioValue(walletPublicKey:string): Promise { + async fetchPortfolioValue(): Promise { try { - const options = { - method: 'GET', - headers: { - accept: 'application/json', - 'x-chain': 'solana', - 'X-API-KEY': API_Key - } - }; - const walletPortfolio = await fetch(`https://public-api.birdeye.so/v1/wallet/token_list?wallet=${walletPublicKey}`, - options + console.log(`Fetching portfolio value for wallet: ${this.walletPublicKey.toBase58()}`); + const walletData = await this.fetchWithRetry( + `${PROVIDER_CONFIG.BIRDEYE_API}/v1/wallet/token_list?wallet=${this.walletPublicKey.toBase58()}` ); - const walletPortfolioJson = await walletPortfolio.json(); - const data = walletPortfolioJson.data; - const totalUsd = data.totalUsd; - const items = data.items; - const walletPortfolioFormatted = { - totalUsd, - items + + if (!walletData?.success || !walletData?.data) { + console.error("No portfolio data available", walletData); + throw new Error('No portfolio data available'); + } + + const data = walletData.data; + const totalUsd = new BigNumber(data.totalUsd.toString()); + const prices = await this.fetchPrices(); + const solPriceInUSD = new BigNumber(prices.solana.usd.toString()); + + const items = data.items.map((item: any) => ({ + ...item, + valueSol: new BigNumber(item.valueUsd || 0) + .div(solPriceInUSD) + .toFixed(6), + name: item.name || "Unknown", + symbol: item.symbol || "Unknown", + priceUsd: item.priceUsd || "0", + valueUsd: item.valueUsd || "0" + })); + + const totalSol = totalUsd.div(solPriceInUSD); + + console.log("Fetched portfolio value:", { + totalUsd: totalUsd.toString(), + totalSol: totalSol.toFixed(6), + items: items.length + }); + + return { + totalUsd: totalUsd.toString(), + totalSol: totalSol.toFixed(6), + items: items.sort((a, b) => + new BigNumber(b.valueUsd).minus(new BigNumber(a.valueUsd)).toNumber() + ) }; - return walletPortfolioFormatted; } catch (error) { - console.log(error); + console.error('Error fetching portfolio:', error); + throw error; } } async fetchPrices(): Promise { - const apiUrl = 'https://api.coingecko.com/api/v3/simple/price'; - const ids = 'solana,bitcoin,ethereum'; - const vsCurrencies = 'usd'; - try { - const response = await fetch(`${apiUrl}?ids=${ids}&vs_currencies=${vsCurrencies}`); + const { SOL, BTC, ETH } = PROVIDER_CONFIG.TOKEN_ADDRESSES; + const tokens = [SOL, BTC, ETH]; + const prices: Prices = { + solana: { usd: "0" }, + bitcoin: { usd: "0" }, + ethereum: { usd: "0" }, + }; - if (!response.ok) { - throw new Error('Failed to fetch prices'); + console.log("Fetching prices for tokens:", tokens); + + for (const token of tokens) { + const response = await this.fetchWithRetry( + `${PROVIDER_CONFIG.BIRDEYE_API}/defi/price?address=${token}`, + { + headers: { + "x-chain": "solana", + }, + } + ); + + if (response?.data?.value) { + const price = response.data.value.toString(); + console.log(`Fetched price for ${token}:`, price); + prices[token === SOL ? "solana" : token === BTC ? "bitcoin" : "ethereum"].usd = price; + } else { + console.warn(`No price data available for token: ${token}`); + } } - - const data = await response.json(); - return data; + + console.log("Fetched prices:", prices); + return prices; } catch (error) { - console.log('Error fetching prices:', error); + console.error("Error fetching prices:", error); + throw error; } } -} + formatPortfolio(portfolio: WalletPortfolio, prices: Prices): string { + let output = `${BOT_WALLET.description}\n`; + output += `Wallet Address: ${this.walletPublicKey.toBase58()}\n\n`; + + const totalUsdFormatted = new BigNumber(portfolio.totalUsd).toFixed(2); + const totalSolFormatted = portfolio.totalSol; + + output += `Total Value: $${totalUsdFormatted} (${totalSolFormatted} SOL)\n\n`; + output += "Token Balances:\n"; -const walletProvider: Provider = { - get: async (runtime: IAgentRuntime, _message: Memory, _state?: State) => { - try { - const walletPublicKey = new PublicKey(runtime.getSetting("WALLET_PUBLIC_KEY")); - const connection = new Connection(runtime.getSetting("RPC_ENDPOINT")); + const nonZeroItems = portfolio.items.filter(item => + new BigNumber(item.uiAmount).isGreaterThan(0) + ); - const provider = new WalletProvider(connection, walletPublicKey); - const portfolio = await provider.fetchPortfolioValue(walletPublicKey.toBase58()); - const prices = await provider.fetchPrices(); + if (nonZeroItems.length === 0) { + output += "No tokens found with non-zero balance\n"; + } else { + for (const item of nonZeroItems) { + const valueUsd = new BigNumber(item.valueUsd).toFixed(2); + output += `${item.name} (${item.symbol}): ${ + new BigNumber(item.uiAmount).toFixed(6) + } ($${valueUsd} | ${item.valueSol} SOL)\n`; + } + } - let formattedBalances = `Wallet Address: ${walletPublicKey.toBase58()}\n\n`; - formattedBalances += `Total Portfolio Value: $${portfolio.totalUsd}\n\n`; - formattedBalances += "Token Balances:\n"; + output += "\nMarket Prices:\n"; + output += `SOL: $${new BigNumber(prices.solana.usd).toFixed(2)}\n`; + output += `BTC: $${new BigNumber(prices.bitcoin.usd).toFixed(2)}\n`; + output += `ETH: $${new BigNumber(prices.ethereum.usd).toFixed(2)}\n`; + + console.log("Formatted portfolio:", output); - for (const item of portfolio.items) { - formattedBalances += `${item.name} (${item.symbol}): ${item.uiAmount} ($${item.valueUSD})\n`; - } + return output; + } - formattedBalances += "\nCurrent Prices:\n"; - formattedBalances += `Solana: $${prices.solana.usd}\n`; - formattedBalances += `Bitcoin: $${prices.bitcoin.usd}\n`; - formattedBalances += `Ethereum: $${prices.ethereum.usd}\n`; + async getFormattedPortfolio(): Promise { + try { + console.log("Generating formatted portfolio report..."); + const [portfolio, prices] = await Promise.all([ + this.fetchPortfolioValue(), + this.fetchPrices() + ]); + + console.log("Portfolio and prices fetched successfully"); - return formattedBalances; + return this.formatPortfolio(portfolio, prices); } catch (error) { - console.error("Error fetching wallet information:", error); - return "Failed to fetch wallet information."; + console.error("Error generating portfolio report:", error); + return "Unable to fetch wallet information. Please try again later."; } - }, + } + + static getBotWalletAddress(): string { + return `${BOT_WALLET.description}\nWallet Address: ${BOT_WALLET.publicKey}`; + } +} + +const walletProvider: Provider = { + get: async (runtime: IAgentRuntime, _message: Memory, _state?: State): Promise => { + try { + console.log("Initializing wallet provider..."); + // Always use the bot's wallet + const publicKey = new PublicKey(BOT_WALLET.publicKey); + const connection = new Connection(PROVIDER_CONFIG.DEFAULT_RPC); + const provider = new WalletProvider(connection, publicKey); + + console.log("Wallet provider initialized successfully"); + + return await provider.getFormattedPortfolio(); + } catch (error) { + console.error("Error in wallet provider:", error); + return "Failed to fetch wallet information. Please check your configuration."; + } + } }; -export default walletProvider; \ No newline at end of file +// Module exports +export default walletProvider; +export const getBotWalletAddress = WalletProvider.getBotWalletAddress; \ No newline at end of file diff --git a/src/services/llama.ts b/src/services/llama.ts index 002def33ac..8b944c1a39 100644 --- a/src/services/llama.ts +++ b/src/services/llama.ts @@ -18,7 +18,7 @@ import { wordsToPunish } from "./wordsToPunish.ts"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const jsonSchemaGrammar: { +const jsonSchemaGrammar: Readonly<{ type: string; properties: { user: { @@ -28,7 +28,7 @@ const jsonSchemaGrammar: { type: string; }; }; -} = { +}> = { type: "object", properties: { user: {