From e4d3ff5604324c8e110bbe35a5948d539cdbaaaa Mon Sep 17 00:00:00 2001 From: "@somewheresy" Date: Tue, 3 Dec 2024 18:36:17 -0500 Subject: [PATCH] init commit --- packages/core/src/actions/index.ts | 5 +- packages/core/src/actions/pumpfun.ts | 329 ---------------- packages/core/src/actions/swap.ts | 500 ------------------------- packages/core/src/actions/swapDao.ts | 157 -------- packages/core/src/actions/swapUtils.ts | 327 ---------------- packages/core/src/actions/takeOrder.ts | 134 ------- packages/core/src/core/models.ts | 2 +- 7 files changed, 2 insertions(+), 1452 deletions(-) delete mode 100644 packages/core/src/actions/pumpfun.ts delete mode 100644 packages/core/src/actions/swap.ts delete mode 100644 packages/core/src/actions/swapDao.ts delete mode 100644 packages/core/src/actions/swapUtils.ts delete mode 100644 packages/core/src/actions/takeOrder.ts diff --git a/packages/core/src/actions/index.ts b/packages/core/src/actions/index.ts index 2348d0f..86c8ff5 100644 --- a/packages/core/src/actions/index.ts +++ b/packages/core/src/actions/index.ts @@ -1,7 +1,6 @@ export * from "./followRoom.ts"; export * from "./imageGeneration.ts"; export * from "./muteRoom.ts"; -export * from "./swap.ts"; export * from "./unfollowRoom.ts"; export * from "./unmuteRoom.ts"; export * from "./muteRoom.ts"; @@ -9,8 +8,6 @@ export * from "./continue.ts"; export * from "./followRoom.ts"; export * from "./ignore.ts"; export * from "./imageGenerationUtils.ts"; -export * from "./pumpfun.ts"; -export * from "./swap.ts"; -export * from "./takeOrder.ts"; export * from "./none.ts"; +export * from "./storyGeneration.ts"; export * from "./webgen/index.ts"; \ No newline at end of file diff --git a/packages/core/src/actions/pumpfun.ts b/packages/core/src/actions/pumpfun.ts deleted file mode 100644 index 48ef541..0000000 --- a/packages/core/src/actions/pumpfun.ts +++ /dev/null @@ -1,329 +0,0 @@ -import { AnchorProvider } from "@coral-xyz/anchor"; -import { Wallet } from "@coral-xyz/anchor"; -import { Connection, Keypair, PublicKey } from "@solana/web3.js"; -import { - CreateTokenMetadata, - DEFAULT_DECIMALS, - PriorityFee, - PumpFunSDK, -} from "pumpdotfun-sdk"; - -import { getAssociatedTokenAddressSync } from "@solana/spl-token"; -import settings from "../core/settings.ts"; - -import { - ActionExample, - Content, - IAgentRuntime, - Memory, - type Action, -} from "../core/types.ts"; - -export interface CreateAndBuyContent extends Content { - deployerPrivateKey: string; - tokenMetadata: CreateTokenMetadata; - buyAmountSol: string | number; - priorityFee: { - unitLimit: number; - unitPrice: number; - }; - allowOffCurve: boolean; -} - -export function isCreateAndBuyContent( - runtime: IAgentRuntime, - content: any -): content is CreateAndBuyContent { - return ( - typeof content.deployerPrivateKey === "string" && - typeof content.tokenMetadata === "object" && - content.tokenMetadata !== null && - (typeof content.buyAmountSol === "string" || - typeof content.buyAmountSol === "number") && - typeof content.priorityFee === "object" && - content.priorityFee !== null && - typeof content.priorityFee.unitLimit === "number" && - typeof content.priorityFee.unitPrice === "number" && - typeof content.allowOffCurve === "boolean" - ); -} - -export const createAndBuyToken = async ({ - deployer, - mint, - tokenMetadata, - buyAmountSol, - priorityFee, - allowOffCurve, - commitment = "finalized", - sdk, - connection, - slippage, -}: { - deployer: Keypair; - mint: Keypair; - tokenMetadata: CreateTokenMetadata; - buyAmountSol: bigint; - priorityFee: PriorityFee; - allowOffCurve: boolean; - commitment?: - | "processed" - | "confirmed" - | "finalized" - | "recent" - | "single" - | "singleGossip" - | "root" - | "max"; - sdk: PumpFunSDK; - connection: Connection; - slippage: string; -}) => { - const createResults = await sdk.createAndBuy( - deployer, - mint, - tokenMetadata, - buyAmountSol, - BigInt(slippage), - priorityFee, - commitment - ); - if (createResults.success) { - console.log( - "Success:", - `https://pump.fun/${mint.publicKey.toBase58()}` - ); - const ata = getAssociatedTokenAddressSync( - mint.publicKey, - deployer.publicKey, - allowOffCurve - ); - const balance = await connection.getTokenAccountBalance( - ata, - "processed" - ); - const amount = balance.value.uiAmount; - if (amount === null) { - console.log( - `${deployer.publicKey.toBase58()}:`, - "No Account Found" - ); - } else { - console.log(`${deployer.publicKey.toBase58()}:`, amount); - } - } else { - console.log("Create and Buy failed"); - } -}; - -export const buyToken = async ({ - sdk, - buyer, - mint, - amount, - priorityFee, - allowOffCurve, - slippage, - connection, -}: { - sdk: PumpFunSDK; - buyer: Keypair; - mint: PublicKey; - amount: bigint; - priorityFee: PriorityFee; - allowOffCurve: boolean; - slippage: string; - connection: Connection; -}) => { - const buyResults = await sdk.buy( - buyer, - mint, - amount, - BigInt(slippage), - priorityFee - ); - if (buyResults.success) { - console.log("Success:", `https://pump.fun/${mint.toBase58()}`); - const ata = getAssociatedTokenAddressSync( - mint, - buyer.publicKey, - allowOffCurve - ); - const balance = await connection.getTokenAccountBalance( - ata, - "processed" - ); - const amount = balance.value.uiAmount; - if (amount === null) { - console.log(`${buyer.publicKey.toBase58()}:`, "No Account Found"); - } else { - console.log(`${buyer.publicKey.toBase58()}:`, amount); - } - } else { - console.log("Buy failed"); - } -}; - -export const sellToken = async ({ - sdk, - seller, - mint, - amount, - priorityFee, - allowOffCurve, - slippage, - connection, -}: { - sdk: PumpFunSDK; - seller: Keypair; - mint: PublicKey; - amount: bigint; - priorityFee: PriorityFee; - allowOffCurve: boolean; - slippage: string; - connection: Connection; -}) => { - const sellResults = await sdk.sell( - seller, - mint, - amount, - BigInt(slippage), - priorityFee - ); - if (sellResults.success) { - console.log("Success:", `https://pump.fun/${mint.toBase58()}`); - const ata = getAssociatedTokenAddressSync( - mint, - seller.publicKey, - allowOffCurve - ); - const balance = await connection.getTokenAccountBalance( - ata, - "processed" - ); - const amount = balance.value.uiAmount; - if (amount === null) { - console.log(`${seller.publicKey.toBase58()}:`, "No Account Found"); - } else { - console.log(`${seller.publicKey.toBase58()}:`, amount); - } - } else { - console.log("Sell failed"); - } -}; - -const promptConfirmation = async (): Promise => { - if (typeof window !== "undefined" && typeof window.confirm === "function") { - return window.confirm( - "Confirm the creation and purchase of the token?" - ); - } - return true; -}; - -export default { - name: "CREATE_AND_BUY_TOKEN", - similes: ["CREATE_AND_PURCHASE_TOKEN", "DEPLOY_AND_BUY_TOKEN"], - validate: async (runtime: IAgentRuntime, message: Memory) => { - return isCreateAndBuyContent(runtime, message.content); - }, - description: - "Create a new token and buy a specified amount using SOL. Requires deployer private key, token metadata, buy amount in SOL, priority fee, and allowOffCurve flag.", - handler: async ( - runtime: IAgentRuntime, - message: Memory - ): Promise => { - const content = message.content; - if (!isCreateAndBuyContent(runtime, content)) { - console.error("Invalid content for CREATE_AND_BUY_TOKEN action."); - return false; - } - const { - deployerPrivateKey, - tokenMetadata, - buyAmountSol, - priorityFee, - allowOffCurve, - } = content; - - const privateKey = runtime.getSetting("WALLET_PRIVATE_KEY")!; - const wallet = new Wallet( - Keypair.fromSecretKey(new Uint8Array(JSON.parse(privateKey))) - ); - const connection = new Connection(settings.RPC_URL!); - const provider = new AnchorProvider(connection, wallet, { - commitment: "finalized", - }); - const sdk = new PumpFunSDK(provider); - const slippage = runtime.getSetting("SLIPPAGE"); - - try { - const deployerKeypair = Keypair.fromSecretKey( - Uint8Array.from(Buffer.from(deployerPrivateKey, "base64")) - ); - - const mintKeypair = Keypair.generate(); - - const createAndBuyConfirmation = await promptConfirmation(); - if (!createAndBuyConfirmation) { - console.log("Create and buy token canceled by user"); - return false; - } - - // Execute Create and Buy - await createAndBuyToken({ - deployer: deployerKeypair, - mint: mintKeypair, - tokenMetadata: tokenMetadata as CreateTokenMetadata, - buyAmountSol: BigInt(buyAmountSol), - priorityFee: priorityFee as PriorityFee, - allowOffCurve: allowOffCurve as boolean, - sdk, - connection, - slippage, - }); - - console.log( - `Token created and purchased successfully! View at: https://pump.fun/${mintKeypair.publicKey.toBase58()}` - ); - return true; - } catch (error) { - console.error("Error during create and buy token:", error); - return false; - } - }, - - examples: [ - [ - { - user: "{{user1}}", - content: { - deployerPrivateKey: "Base64EncodedPrivateKey", - tokenMetadata: { - name: "MyToken", - symbol: "MTK", - description: "My first token", - file: "Base64EncodedFile", // blob file of the image - decimals: DEFAULT_DECIMALS, - }, - buyAmountSol: "1000000000", // 1 SOL in lamports - priorityFee: 1000, - allowOffCurve: false, - }, - }, - { - user: "{{user2}}", - content: { - text: "Creating and buying 1 SOL worth of MyToken...", - action: "CREATE_AND_BUY_TOKEN", - }, - }, - { - user: "{{user2}}", - content: { - text: "Token created and purchased successfully! View at: https://pump.fun/MintPublicKey", - }, - }, - ], - ] as ActionExample[][], -} as Action; diff --git a/packages/core/src/actions/swap.ts b/packages/core/src/actions/swap.ts deleted file mode 100644 index 463ed44..0000000 --- a/packages/core/src/actions/swap.ts +++ /dev/null @@ -1,500 +0,0 @@ -import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes/index.js"; -import { - Connection, - Keypair, - PublicKey, - VersionedTransaction, -} from "@solana/web3.js"; -import BigNumber from "bignumber.js"; -import fetch from "cross-fetch"; -import { v4 as uuidv4 } from "uuid"; -import { TrustScoreDatabase } from "../adapters/trustScoreDatabase.ts"; -import { composeContext } from "../core/context.ts"; -import { generateObject } from "../core/generation.ts"; -import settings from "../core/settings.ts"; -import { - ActionExample, - HandlerCallback, - IAgentRuntime, - Memory, - ModelClass, - State, - type Action, -} from "../core/types.ts"; -import { TokenProvider } from "../providers/token.ts"; -import { TrustScoreProvider } from "../providers/trustScoreProvider.ts"; -import { walletProvider, WalletProvider } from "../providers/wallet.ts"; -import { getTokenDecimals } from "./swapUtils.ts"; - -async function swapToken( - connection: Connection, - walletPublicKey: PublicKey, - inputTokenCA: string, - outputTokenCA: string, - amount: number -): Promise { - try { - // Get the decimals for the input token - const decimals = - inputTokenCA === settings.SOL_ADDRESS - ? new BigNumber(9) - : new BigNumber( - await getTokenDecimals(connection, inputTokenCA) - ); - - console.log("Decimals:", decimals.toString()); - - // Use BigNumber for adjustedAmount: amount * (10 ** decimals) - const amountBN = new BigNumber(amount); - const adjustedAmount = amountBN.multipliedBy( - new BigNumber(10).pow(decimals) - ); - - console.log("Fetching quote with params:", { - inputMint: inputTokenCA, - outputMint: outputTokenCA, - amount: adjustedAmount, - }); - - const quoteResponse = await fetch( - `https://quote-api.jup.ag/v6/quote?inputMint=${inputTokenCA}&outputMint=${outputTokenCA}&amount=${adjustedAmount}&slippageBps=50` - ); - const quoteData = await quoteResponse.json(); - - if (!quoteData || quoteData.error) { - console.error("Quote error:", quoteData); - throw new Error( - `Failed to get quote: ${quoteData?.error || "Unknown error"}` - ); - } - - console.log("Quote received:", quoteData); - - const swapRequestBody = { - quoteResponse: quoteData, - userPublicKey: walletPublicKey.toString(), - wrapAndUnwrapSol: true, - computeUnitPriceMicroLamports: 1000, - dynamicComputeUnitLimit: true, - }; - - console.log("Requesting swap with body:", swapRequestBody); - - const swapResponse = await fetch("https://quote-api.jup.ag/v6/swap", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(swapRequestBody), - }); - - const swapData = await swapResponse.json(); - - if (!swapData || !swapData.swapTransaction) { - console.error("Swap error:", swapData); - throw new Error( - `Failed to get swap transaction: ${swapData?.error || "No swap transaction returned"}` - ); - } - - console.log("Swap transaction received"); - return swapData; - } catch (error) { - console.error("Error in swapToken:", error); - throw error; - } -} - -const swapTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. - -Example response: -\`\`\`json -{ - "inputTokenSymbol": "SOL", - "outputTokenSymbol": "USDC", - "inputTokenCA": "So11111111111111111111111111111111111111112", - "outputTokenCA": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - "amount": 1.5 -} -\`\`\` - -{{recentMessages}} - -Given the recent messages and wallet information below: - -{{walletInfo}} - -Extract the following information about the requested token swap: -- Input token symbol (the token being sold) -- Output token symbol (the token being bought) -- Input token contract address if provided -- Output token contract address if provided -- Amount to swap - -Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined. The result should be a valid JSON object with the following schema: -\`\`\`json -{ - "inputTokenSymbol": string | null, - "outputTokenSymbol": string | null, - "inputTokenCA": string | null, - "outputTokenCA": string | null, - "amount": number | string | null -} -\`\`\``; - -// if we get the token symbol but not the CA, check walet for matching token, and if we have, get the CA for it - -// get all the tokens in the wallet using the wallet provider -async function getTokensInWallet(runtime: IAgentRuntime) { - const walletProvider = new WalletProvider( - new Connection("https://api.mainnet-beta.solana.com"), - new PublicKey(runtime.getSetting("WALLET_PUBLIC_KEY")) - ); - - const walletInfo = await walletProvider.fetchPortfolioValue(runtime); - const items = walletInfo.items; - return items; -} - -// check if the token symbol is in the wallet -async function getTokenFromWallet(runtime: IAgentRuntime, tokenSymbol: string) { - try { - const items = await getTokensInWallet(runtime); - const token = items.find((item) => item.symbol === tokenSymbol); - - if (token) { - return token.address; - } else { - return null; - } - } catch (error) { - console.error("Error checking token in wallet:", error); - return null; - } -} - -// swapToken should took CA, not symbol - -export const executeSwap: Action = { - name: "EXECUTE_SWAP", - similes: ["SWAP_TOKENS", "TOKEN_SWAP", "TRADE_TOKENS", "EXCHANGE_TOKENS"], - validate: async (runtime: IAgentRuntime, message: Memory) => { - // Check if the necessary parameters are provided in the message - console.log("Message:", message); - return true; - }, - description: "Perform a token swap.", - handler: async ( - runtime: IAgentRuntime, - message: Memory, - state: State, - _options: { [key: string]: unknown }, - callback?: HandlerCallback - ): Promise => { - // composeState - if (!state) { - state = (await runtime.composeState(message)) as State; - } else { - state = await runtime.updateRecentMessageState(state); - } - - const walletInfo = await walletProvider.get(runtime, message, state); - - state.walletInfo = walletInfo; - - const swapContext = composeContext({ - state, - template: swapTemplate, - }); - - const response = await generateObject({ - runtime, - context: swapContext, - modelClass: ModelClass.LARGE, - }); - - console.log("Response:", response); - const type = - response.inputTokenSymbol?.toUpperCase() === "SOL" ? "buy" : "sell"; - - // Add SOL handling logic - if (response.inputTokenSymbol?.toUpperCase() === "SOL") { - response.inputTokenCA = settings.SOL_ADDRESS; - } - if (response.outputTokenSymbol?.toUpperCase() === "SOL") { - response.outputTokenCA = settings.SOL_ADDRESS; - } - - // if both contract addresses are set, lets execute the swap - // TODO: try to resolve CA from symbol based on existing symbol in wallet - if (!response.inputTokenCA && response.inputTokenSymbol) { - console.log( - `Attempting to resolve CA for input token symbol: ${response.inputTokenSymbol}` - ); - response.inputTokenCA = await getTokenFromWallet( - runtime, - response.inputTokenSymbol - ); - if (response.inputTokenCA) { - console.log(`Resolved inputTokenCA: ${response.inputTokenCA}`); - } else { - console.log("No contract addresses provided, skipping swap"); - const responseMsg = { - text: "I need the contract addresses to perform the swap", - }; - callback?.(responseMsg); - return true; - } - } - - if (!response.outputTokenCA && response.outputTokenSymbol) { - console.log( - `Attempting to resolve CA for output token symbol: ${response.outputTokenSymbol}` - ); - response.outputTokenCA = await getTokenFromWallet( - runtime, - response.outputTokenSymbol - ); - if (response.outputTokenCA) { - console.log( - `Resolved outputTokenCA: ${response.outputTokenCA}` - ); - } else { - console.log("No contract addresses provided, skipping swap"); - const responseMsg = { - text: "I need the contract addresses to perform the swap", - }; - callback?.(responseMsg); - return true; - } - } - - if (!response.amount) { - console.log("No amount provided, skipping swap"); - const responseMsg = { - text: "I need the amount to perform the swap", - }; - callback?.(responseMsg); - return true; - } - - // TODO: if response amount is half, all, etc, semantically retrieve amount and return as number - if (!response.amount) { - console.log("Amount is not a number, skipping swap"); - const responseMsg = { - text: "The amount must be a number", - }; - callback?.(responseMsg); - return true; - } - try { - const connection = new Connection( - "https://api.mainnet-beta.solana.com" - ); - const walletPublicKey = new PublicKey( - runtime.getSetting("WALLET_PUBLIC_KEY") - ); - - console.log("Wallet Public Key:", walletPublicKey); - console.log("inputTokenSymbol:", response.inputTokenCA); - console.log("outputTokenSymbol:", response.outputTokenCA); - console.log("amount:", response.amount); - - const swapResult = await swapToken( - connection, - walletPublicKey, - response.inputTokenCA as string, - response.outputTokenCA as string, - response.amount as number - ); - - console.log("Deserializing transaction..."); - const transactionBuf = Buffer.from( - swapResult.swapTransaction, - "base64" - ); - const transaction = - VersionedTransaction.deserialize(transactionBuf); - - console.log("Preparing to sign transaction..."); - const privateKeyString = runtime.getSetting("WALLET_PRIVATE_KEY"); - - // Handle different private key formats - let secretKey: Uint8Array; - try { - // First try to decode as base58 - secretKey = bs58.decode(privateKeyString); - } catch (e) { - try { - // If that fails, try base64 - secretKey = Uint8Array.from( - Buffer.from(privateKeyString, "base64") - ); - } catch (e2) { - throw new Error("Invalid private key format"); - } - } - - // Verify the key length - if (secretKey.length !== 64) { - console.error("Invalid key length:", secretKey.length); - throw new Error( - `Invalid private key length: ${secretKey.length}. Expected 64 bytes.` - ); - } - - console.log("Creating keypair..."); - const keypair = Keypair.fromSecretKey(secretKey); - - // Verify the public key matches what we expect - const expectedPublicKey = runtime.getSetting("WALLET_PUBLIC_KEY"); - if (keypair.publicKey.toBase58() !== expectedPublicKey) { - throw new Error( - "Generated public key doesn't match expected public key" - ); - } - - console.log("Signing transaction..."); - transaction.sign([keypair]); - - console.log("Sending transaction..."); - - const latestBlockhash = await connection.getLatestBlockhash(); - - const txid = await connection.sendTransaction(transaction, { - skipPreflight: false, - maxRetries: 3, - preflightCommitment: "confirmed", - }); - - console.log("Transaction sent:", txid); - - // Confirm transaction using the blockhash - const confirmation = await connection.confirmTransaction( - { - signature: txid, - blockhash: latestBlockhash.blockhash, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - }, - "confirmed" - ); - - if (confirmation.value.err) { - throw new Error( - `Transaction failed: ${confirmation.value.err}` - ); - } - - if (confirmation.value.err) { - throw new Error( - `Transaction failed: ${confirmation.value.err}` - ); - } - - if (type === "buy") { - const tokenProvider = new TokenProvider(response.outputTokenCA); - const module = await import("better-sqlite3"); - const Database = module.default; - const trustScoreDb = new TrustScoreDatabase( - new Database(":memory:") - ); - // add or get recommender - const uuid = uuidv4(); - const recommender = await trustScoreDb.getOrCreateRecommender({ - id: uuid, - address: walletPublicKey.toString(), - solanaPubkey: walletPublicKey.toString(), - }); - - const trustScoreDatabase = new TrustScoreProvider( - tokenProvider, - trustScoreDb - ); - // save the trade - const tradeData = { - buy_amount: response.amount, - is_simulation: false, - }; - await trustScoreDatabase.createTradePerformance( - runtime, - response.outputTokenCA, - recommender.id, - tradeData - ); - } else if (type === "sell") { - const tokenProvider = new TokenProvider(response.inputTokenCA); - const module = await import("better-sqlite3"); - const Database = module.default; - const trustScoreDb = new TrustScoreDatabase( - new Database(":memory:") - ); - // add or get recommender - const uuid = uuidv4(); - const recommender = await trustScoreDb.getOrCreateRecommender({ - id: uuid, - address: walletPublicKey.toString(), - solanaPubkey: walletPublicKey.toString(), - }); - - const trustScoreDatabase = new TrustScoreProvider( - tokenProvider, - trustScoreDb - ); - // save the trade - const sellDetails = { - sell_amount: response.amount, - sell_recommender_id: recommender.id, - }; - const sellTimeStamp = new Date().getTime().toString(); - await trustScoreDatabase.updateSellDetails( - runtime, - response.inputTokenCA, - recommender.id, - sellTimeStamp, - sellDetails, - false - ); - } - - console.log("Swap completed successfully!"); - console.log(`Transaction ID: ${txid}`); - - const responseMsg = { - text: `Swap completed successfully! Transaction ID: ${txid}`, - }; - - callback?.(responseMsg); - - return true; - } catch (error) { - console.error("Error during token swap:", error); - return false; - } - }, - examples: [ - [ - { - user: "{{user1}}", - content: { - inputTokenSymbol: "SOL", - outputTokenSymbol: "USDC", - amount: 0.1, - }, - }, - { - user: "{{user2}}", - content: { - text: "Swapping 0.1 SOL for USDC...", - action: "TOKEN_SWAP", - }, - }, - { - user: "{{user2}}", - content: { - text: "Swap completed successfully! Transaction ID: ...", - }, - }, - ], - // Add more examples as needed - ] as ActionExample[][], -} as Action; diff --git a/packages/core/src/actions/swapDao.ts b/packages/core/src/actions/swapDao.ts deleted file mode 100644 index 18d5818..0000000 --- a/packages/core/src/actions/swapDao.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js"; -import fetch from "cross-fetch"; -import { - ActionExample, - IAgentRuntime, - Memory, - type Action, -} from "../core/types.ts"; -import { getQuote } from "./swapUtils.ts"; - -async function invokeSwapDao( - connection: Connection, - authority: Keypair, - statePDA: PublicKey, - walletPDA: PublicKey, - instructionData: Buffer -): Promise { - const discriminator = new Uint8Array([ - 25, 143, 207, 190, 174, 228, 130, 107, - ]); - - // Combine discriminator and instructionData into a single Uint8Array - const combinedData = new Uint8Array( - discriminator.length + instructionData.length - ); - combinedData.set(discriminator, 0); - combinedData.set(instructionData, discriminator.length); - - const transaction = new Transaction().add({ - programId: new PublicKey("PROGRAM_ID"), - keys: [ - { pubkey: authority.publicKey, isSigner: true, isWritable: true }, - { pubkey: statePDA, isSigner: false, isWritable: true }, - { pubkey: walletPDA, isSigner: false, isWritable: true }, - ], - data: Buffer.from(combinedData), - }); - - const signature = await connection.sendTransaction(transaction, [ - authority, - ]); - await connection.confirmTransaction(signature); - return signature; -} - -async function promptConfirmation(): Promise { - // confirmation logic here - const confirmSwap = window.confirm("Confirm the token swap?"); - return confirmSwap; -} - -export const executeSwap: Action = { - name: "EXECUTE_SWAP_DAO", - similes: ["SWAP_TOKENS_DAO", "TOKEN_SWAP_DAO"], - validate: async (runtime: IAgentRuntime, message: Memory) => { - console.log("Message:", message); - return true; - }, - description: "Perform a DAO token swap using execute_invoke.", - handler: async ( - runtime: IAgentRuntime, - message: Memory - ): Promise => { - const { inputToken, outputToken, amount } = message.content; - - try { - const connection = new Connection( - "https://api.mainnet-beta.solana.com" // better if we use a better rpc - ); - const authority = Keypair.fromSecretKey( - Uint8Array.from( - Buffer.from( - runtime.getSetting("WALLET_PRIVATE_KEY"), // should be the authority private key - "base64" - ) - ) - ); - const daoMint = new PublicKey(runtime.getSetting("DAO_MINT")); // DAO mint address - - // Derive PDAs - const [statePDA] = await PublicKey.findProgramAddress( - [Buffer.from("state"), daoMint.toBuffer()], - authority.publicKey - ); - const [walletPDA] = await PublicKey.findProgramAddress( - [Buffer.from("wallet"), daoMint.toBuffer()], - authority.publicKey - ); - - const quoteData = await getQuote( - connection as Connection, - inputToken as string, - outputToken as string, - amount as number - ); - console.log("Swap Quote:", quoteData); - - const confirmSwap = await promptConfirmation(); - if (!confirmSwap) { - console.log("Swap canceled by user"); - return false; - } - - // Prepare instruction data for swap - const instructionData = Buffer.from( - JSON.stringify({ - quote: quoteData.data, - userPublicKey: authority.publicKey.toString(), - wrapAndUnwrapSol: true, - }) - ); - - const txid = await invokeSwapDao( - connection, - authority, - statePDA, - walletPDA, - instructionData - ); - - console.log("DAO Swap completed successfully!"); - console.log(`Transaction ID: ${txid}`); - - return true; - } catch (error) { - console.error("Error during DAO token swap:", error); - return false; - } - }, - examples: [ - [ - { - user: "{{user1}}", - content: { - inputTokenSymbol: "SOL", - outputTokenSymbol: "USDC", - inputToken: "So11111111111111111111111111111111111111112", - outputToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", - amount: 0.1, - }, - }, - { - user: "{{user2}}", - content: { - text: "Swapping 0.1 SOL for USDC using DAO...", - action: "TOKEN_SWAP_DAO", - }, - }, - { - user: "{{user2}}", - content: { - text: "DAO Swap completed successfully! Transaction ID: ...", - }, - }, - ], - ] as ActionExample[][], -} as Action; diff --git a/packages/core/src/actions/swapUtils.ts b/packages/core/src/actions/swapUtils.ts deleted file mode 100644 index 1a583eb..0000000 --- a/packages/core/src/actions/swapUtils.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { getAssociatedTokenAddress } from "@solana/spl-token"; -import { - BlockhashWithExpiryBlockHeight, - Connection, - Keypair, - PublicKey, - RpcResponseAndContext, - SimulatedTransactionResponse, - TokenAmount, - VersionedTransaction, -} from "@solana/web3.js"; -import settings from "../core/settings.ts"; - -const solAddress = settings.SOL_ADDRESS; -const SLIPPAGE = settings.SLIPPAGE; -const connection = new Connection( - settings.RPC_URL || "https://api.mainnet-beta.solana.com" -); -const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -export async function delayedCall( - method: (...args: any[]) => Promise, - ...args: any[] -): Promise { - await delay(150); - return method(...args); -} - -export async function getTokenDecimals( - connection: Connection, - mintAddress: string -): Promise { - const mintPublicKey = new PublicKey(mintAddress); - const tokenAccountInfo = - await connection.getParsedAccountInfo(mintPublicKey); - - // Check if the data is parsed and contains the expected structure - if ( - tokenAccountInfo.value && - typeof tokenAccountInfo.value.data === "object" && - "parsed" in tokenAccountInfo.value.data - ) { - const parsedInfo = tokenAccountInfo.value.data.parsed?.info; - if (parsedInfo && typeof parsedInfo.decimals === "number") { - return parsedInfo.decimals; - } - } - - throw new Error("Unable to fetch token decimals"); -} - -export async function getQuote( - connection: Connection, - baseToken: string, - outputToken: string, - amount: number -): Promise { - const decimals = await getTokenDecimals(connection, baseToken); - const adjustedAmount = amount * 10 ** decimals; - - const quoteResponse = await fetch( - `https://quote-api.jup.ag/v6/quote?inputMint=${baseToken}&outputMint=${outputToken}&amount=${adjustedAmount}&slippageBps=50` - ); - const swapTransaction = await quoteResponse.json(); - const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); - return new Uint8Array(swapTransactionBuf); -} - -export const executeSwap = async ( - transaction: VersionedTransaction, - type: "buy" | "sell" -) => { - try { - const latestBlockhash: BlockhashWithExpiryBlockHeight = - await delayedCall(connection.getLatestBlockhash.bind(connection)); - const signature = await connection.sendTransaction(transaction, { - skipPreflight: false, - }); - const confirmation = await connection.confirmTransaction( - { - signature, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - blockhash: latestBlockhash.blockhash, - }, - "finalized" - ); - if (confirmation.value.err) { - console.log("Confirmation error", confirmation.value.err); - - throw new Error("Confirmation error"); - } else { - if (type === "buy") { - console.log( - "Buy successful: https://solscan.io/tx/${signature}" - ); - } else { - console.log( - "Sell successful: https://solscan.io/tx/${signature}" - ); - } - } - - return signature; - } catch (error) { - console.log(error); - } -}; - -export const Sell = async (baseMint: PublicKey, wallet: Keypair) => { - try { - const tokenAta = await delayedCall( - getAssociatedTokenAddress, - baseMint, - wallet.publicKey - ); - const tokenBalInfo: RpcResponseAndContext = - await delayedCall( - connection.getTokenAccountBalance.bind(connection), - tokenAta - ); - - if (!tokenBalInfo) { - console.log("Balance incorrect"); - return null; - } - - const tokenBalance = tokenBalInfo.value.amount; - if (tokenBalance === "0") { - console.warn( - `No token balance to sell with wallet ${wallet.publicKey}` - ); - } - - const sellTransaction = await getSwapTxWithWithJupiter( - wallet, - baseMint, - tokenBalance, - "sell" - ); - // simulate the transaction - if (!sellTransaction) { - console.log("Failed to get sell transaction"); - return null; - } - - const simulateResult: RpcResponseAndContext = - await delayedCall( - connection.simulateTransaction.bind(connection), - sellTransaction - ); - if (simulateResult.value.err) { - console.log("Sell Simulation failed", simulateResult.value.err); - return null; - } - - // execute the transaction - return executeSwap(sellTransaction, "sell"); - } catch (error) { - console.log(error); - } -}; - -export const Buy = async (baseMint: PublicKey, wallet: Keypair) => { - try { - const tokenAta = await delayedCall( - getAssociatedTokenAddress, - baseMint, - wallet.publicKey - ); - const tokenBalInfo: RpcResponseAndContext = - await delayedCall( - connection.getTokenAccountBalance.bind(connection), - tokenAta - ); - - if (!tokenBalInfo) { - console.log("Balance incorrect"); - return null; - } - - const tokenBalance = tokenBalInfo.value.amount; - if (tokenBalance === "0") { - console.warn( - `No token balance to sell with wallet ${wallet.publicKey}` - ); - } - - const buyTransaction = await getSwapTxWithWithJupiter( - wallet, - baseMint, - tokenBalance, - "buy" - ); - // simulate the transaction - if (!buyTransaction) { - console.log("Failed to get buy transaction"); - return null; - } - - const simulateResult: RpcResponseAndContext = - await delayedCall( - connection.simulateTransaction.bind(connection), - buyTransaction - ); - if (simulateResult.value.err) { - console.log("Buy Simulation failed", simulateResult.value.err); - return null; - } - - // execute the transaction - return executeSwap(buyTransaction, "buy"); - } catch (error) { - console.log(error); - } -}; - -export const getSwapTxWithWithJupiter = async ( - wallet: Keypair, - baseMint: PublicKey, - amount: string, - type: "buy" | "sell" -) => { - try { - switch (type) { - case "buy": - return fetchBuyTransaction(wallet, baseMint, amount); - case "sell": - return fetchSellTransaction(wallet, baseMint, amount); - default: - return fetchSellTransaction(wallet, baseMint, amount); - } - } catch (error) { - console.log(error); - } -}; - -export const fetchBuyTransaction = async ( - wallet: Keypair, - baseMint: PublicKey, - amount: string -) => { - try { - const quoteResponse = await ( - await fetch( - `https://quote-api.jup.ag/v6/quote?inputMint=${solAddress}&outputMint=${baseMint.toBase58()}&amount=${amount}&slippageBps=${SLIPPAGE}` - ) - ).json(); - const { swapTransaction } = await ( - await fetch("https://quote-api.jup.ag/v6/swap", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - quoteResponse, - userPublicKey: wallet.publicKey.toString(), - wrapAndUnwrapSol: true, - dynamicComputeUnitLimit: true, - prioritizationFeeLamports: 100000, - }), - }) - ).json(); - if (!swapTransaction) { - console.log("Failed to get buy transaction"); - return null; - } - - // deserialize the transaction - const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); - const transaction = - VersionedTransaction.deserialize(swapTransactionBuf); - - // sign the transaction - transaction.sign([wallet]); - return transaction; - } catch (error) { - console.log("Failed to get buy transaction", error); - return null; - } -}; - -export const fetchSellTransaction = async ( - wallet: Keypair, - baseMint: PublicKey, - amount: string -) => { - try { - const quoteResponse = await ( - await fetch( - `https://quote-api.jup.ag/v6/quote?inputMint=${baseMint.toBase58()}&outputMint=${solAddress}&amount=${amount}&slippageBps=${SLIPPAGE}` - ) - ).json(); - - // get serialized transactions for the swap - const { swapTransaction } = await ( - await fetch("https://quote-api.jup.ag/v6/swap", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - quoteResponse, - userPublicKey: wallet.publicKey.toString(), - wrapAndUnwrapSol: true, - dynamicComputeUnitLimit: true, - prioritizationFeeLamports: 52000, - }), - }) - ).json(); - if (!swapTransaction) { - console.log("Failed to get sell transaction"); - return null; - } - - // deserialize the transaction - const swapTransactionBuf = Buffer.from(swapTransaction, "base64"); - const transaction = - VersionedTransaction.deserialize(swapTransactionBuf); - - // sign the transaction - transaction.sign([wallet]); - return transaction; - } catch (error) { - console.log("Failed to get sell transaction", error); - return null; - } -}; diff --git a/packages/core/src/actions/takeOrder.ts b/packages/core/src/actions/takeOrder.ts deleted file mode 100644 index 6f0f9f2..0000000 --- a/packages/core/src/actions/takeOrder.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { - Action, - IAgentRuntime, - Memory, - Content, - ActionExample, - ModelClass, -} from "../core/types.ts"; -import * as fs from "fs"; -import settings from "../core/settings.ts"; -import { composeContext } from "../core/context.ts"; -import { generateText } from "../core/generation.ts"; - -interface Order { - userId: string; - ticker: string; - contractAddress: string; - timestamp: string; - buyAmount: number; - price: number; -} - -const take_order: Action = { - name: "TAKE_ORDER", - similes: ["BUY_ORDER", "PLACE_ORDER"], - description: "Records a buy order based on the user's conviction level.", - examples: [], - validate: async (runtime: IAgentRuntime, message: Memory) => { - const text = (message.content as Content).text; - // Check if the message contains a ticker symbol - const tickerRegex = /\b[A-Z]{1,5}\b/g; - return tickerRegex.test(text); - }, - handler: async (runtime: IAgentRuntime, message: Memory) => { - const text = (message.content as Content).text; - const userId = message.userId; - - const template = ` - -// CLAUDE TODO: Put the usual conversation context here - -Ticker is: {{ticker}} -Contract address is: {{contractAddress}} - -Determine if the user is trying to shill the ticker. if they are, respond with empty conviction, ticker and contractAddress. - -// CLAUDE TODO: output a JSON block with the following fields: -// - reasoning: string -// - conviction: negative, low, medium, high -// - ticker: string (extract from CA so we have context) -// - contractAddress: string -`; - - let ticker, contractAddress; - - // TODO: - - // 1. create state object with runtime.composeState - // 2. compose context with template and state - // 3. get generateText - // 4. validate generateText - - // if ticker or contractAddress are empty, return a message asking for them - if (!ticker || !contractAddress) { - return { - text: "Ticker and CA?", - }; - } - - const state = await runtime.composeState(message); - // TODO: compose context properly - const context = composeContext({ - state: { - ...state, - ticker, - contractAddress, - }, - template, - }); - - const convictionResponse = await generateText({ - runtime, - context: context, - modelClass: ModelClass.SMALL, - }); - - // TODOL parse and validate the JSON - const convictionResponseJson = JSON.parse(convictionResponse); // TODO: replace with validate like other actions - - // get the conviction - const conviction = convictionResponseJson.conviction; - - let buyAmount = 0; - if (conviction === "low") { - buyAmount = 20; - } else if (conviction === "medium") { - buyAmount = 50; - } else if (conviction === "high") { - buyAmount = 100; - } - - // Get the current price of the asset (replace with actual price fetching logic) - const currentPrice = 100; - - const order: Order = { - userId, - ticker: ticker || "", - contractAddress, - timestamp: new Date().toISOString(), - buyAmount, - price: currentPrice, - }; - - // Read the existing order book from the JSON file - const orderBookPath = settings.orderBookPath; - let orderBook: Order[] = []; - if (fs.existsSync(orderBookPath)) { - const orderBookData = fs.readFileSync(orderBookPath, "utf-8"); - orderBook = JSON.parse(orderBookData); - } - - // Add the new order to the order book - orderBook.push(order); - - // Write the updated order book back to the JSON file - fs.writeFileSync(orderBookPath, JSON.stringify(orderBook, null, 2)); - - return { - text: `Recorded a ${conviction} conviction buy order for ${ticker} (${contractAddress}) with an amount of ${buyAmount} at the price of ${currentPrice}.`, - }; - }, -}; - -export default take_order; diff --git a/packages/core/src/core/models.ts b/packages/core/src/core/models.ts index e354d13..324b2c9 100644 --- a/packages/core/src/core/models.ts +++ b/packages/core/src/core/models.ts @@ -96,7 +96,7 @@ const models: Models = { endpoint: "https://api.together.ai/v1", model: { [ModelClass.SMALL]: "meta-llama/Llama-3.2-3B-Instruct-Turbo", - [ModelClass.MEDIUM]: "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", + [ModelClass.MEDIUM]: "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", [ModelClass.LARGE]: "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", [ModelClass.EMBEDDING]: "togethercomputer/m2-bert-80M-32k-retrieval",