diff --git a/test/index.ts b/test/index.ts index 00f9976..d28b626 100644 --- a/test/index.ts +++ b/test/index.ts @@ -165,7 +165,139 @@ async function runChatMode(agent: any, config: any) { } } -async function chooseMode(): Promise<"chat" | "auto"> { +interface MarketMakerConfig { + marketId: string; + baseToken: string; + quoteToken: string; + quoteParams: { + number: number; // Number of quotes on each side + minDepth: number; // Minimum distance from mid (%) + maxDepth: number; // Maximum distance from mid (%) + }; + allowance: { + base: number; + quote: number; + }; + intervalSeconds: number; +} + +function createReadlineInterface() { + return readline.createInterface({ + input: process.stdin, + output: process.stdout + }); +} + +async function askQuestion(rl: readline.Interface, question: string): Promise { + return new Promise((resolve) => { + rl.question(question, (answer) => { + resolve(answer); + }); + }); +} + +async function configureMarketMaker(): Promise { + const rl = createReadlineInterface(); + + try { + console.log("\n=== Market Maker Configuration ===\n"); + + // Basic market information + const marketId = await askQuestion(rl, "Enter the market ID: "); + const baseToken = await askQuestion(rl, "Enter the base token symbol (e.g., SEND): "); + const quoteToken = await askQuestion(rl, "Enter the quote token symbol (e.g., USDC): "); + + // Quote parameters + console.log("\n=== Quote Parameters (applies to both buy and sell sides) ==="); + const quoteNumber = parseInt(await askQuestion(rl, "Enter number of quotes to place on each side: ")); + const minDepth = parseFloat(await askQuestion(rl, "Enter minimum quote depth (% distance from mid price): ")); + const maxDepth = parseFloat(await askQuestion(rl, "Enter maximum quote depth (% distance from mid price): ")); + + // Token allowances + console.log("\n=== Token Allowances ==="); + const baseAllowance = parseFloat(await askQuestion(rl, `Enter total ${baseToken} allowance: `)); + const quoteAllowance = parseFloat(await askQuestion(rl, `Enter total ${quoteToken} allowance: `)); + + // Update interval + const interval = parseInt(await askQuestion(rl, "\nEnter update interval in seconds: ")); + + const config: MarketMakerConfig = { + marketId, + baseToken, + quoteToken, + quoteParams: { + number: quoteNumber, + minDepth: minDepth, + maxDepth: maxDepth + }, + allowance: { + base: baseAllowance, + quote: quoteAllowance + }, + intervalSeconds: interval + }; + + // Display summary + console.log("\n=== Configuration Summary ==="); + console.log(JSON.stringify(config, null, 2)); + + const confirm = await askQuestion(rl, "\nIs this configuration correct? (yes/no): "); + if (confirm.toLowerCase() !== 'yes') { + throw new Error("Configuration cancelled by user"); + } + + return config; + + } finally { + rl.close(); + } +} + +async function runMarketMakerMode(agent: any, config: any) { + try { + const marketMakerConfig = await configureMarketMaker(); + console.log(`\nStarting market maker mode for ${marketMakerConfig.baseToken}/${marketMakerConfig.quoteToken}...`); + + while (true) { + try { + const thought = + `You are an on-chain Solana market maker for the ${marketMakerConfig.baseToken}/${marketMakerConfig.quoteToken} Manifest market, ${marketMakerConfig.marketId}. + Find the ${marketMakerConfig.baseToken}/${marketMakerConfig.quoteToken} live price by checking Jupiter. + Use solana_batch_order to provide ${marketMakerConfig.quoteParams.number} buys at different prices between -${marketMakerConfig.quoteParams.minDepth}% to -${marketMakerConfig.quoteParams.maxDepth}% and ${marketMakerConfig.quoteParams.number} sells at different prices between +${marketMakerConfig.quoteParams.minDepth}% to +${marketMakerConfig.quoteParams.maxDepth}% with increasing quantities further from the live price. + You have an allowance of ${marketMakerConfig.allowance.base} ${marketMakerConfig.baseToken} and ${marketMakerConfig.allowance.quote} ${marketMakerConfig.quoteToken}. + Important! Only send 1 transaction, buy and sells can be combined into a single solana_batch_order.`; + + const stream = await agent.stream( + { messages: [new HumanMessage(thought)] }, + config, + ); + + for await (const chunk of stream) { + if ("agent" in chunk) { + console.log(chunk.agent.messages[0].content); + } else if ("tools" in chunk) { + console.log(chunk.tools.messages[0].content); + } + console.log("-------------------"); + } + + await new Promise((resolve) => + setTimeout(resolve, marketMakerConfig.intervalSeconds * 1000) + ); + } catch (error) { + if (error instanceof Error) { + console.error("Error:", error); + } + process.exit(1); + } + } + } catch (error) { + console.error("Configuration error:", error); + process.exit(1); + } +} + +async function chooseMode(): Promise<"chat" | "auto" | "mm"> { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, @@ -178,6 +310,7 @@ async function chooseMode(): Promise<"chat" | "auto"> { console.log("\nAvailable modes:"); console.log("1. chat - Interactive chat mode"); console.log("2. auto - Autonomous action mode"); + console.log("3. mm - AI guided market making") const choice = (await question("\nChoose a mode (enter number or name): ")) .toLowerCase() @@ -189,6 +322,8 @@ async function chooseMode(): Promise<"chat" | "auto"> { return "chat"; } else if (choice === "2" || choice === "auto") { return "auto"; + } else if (choice === "3" || choice === "mm") { + return "mm"; } console.log("Invalid choice. Please try again."); } @@ -202,8 +337,10 @@ async function main() { if (mode === "chat") { await runChatMode(agent, config); - } else { + } else if( mode === "auto") { await runAutonomousMode(agent, config); + } else { + await runMarketMakerMode(agent, config); } } catch (error) { if (error instanceof Error) {