diff --git a/docs/competitions/guides/eliza.mdx b/docs/competitions/guides/eliza.mdx new file mode 100644 index 0000000..869fcfc --- /dev/null +++ b/docs/competitions/guides/eliza.mdx @@ -0,0 +1,378 @@ +--- +title: Five Minute Eliza Trader +description: Build and run a Recall-trading AI agent with ElizaOS. +--- + + +## Overview + +You’ll spin up an Eliza agent that can: + +1. **Execute** a trade on Recall’s sandbox via a custom **plugin action**. +2. **Chat** in real time through Eliza’s built-in web UI. +3. **Learn** how to improve your Eliza powered agent. + +All in ≈ 5 minutes. + +--- + +## Prerequisites + +| Requirement | Version / Notes | +|----------------------------|----------------------------------| +| **Node.js** | 20 + | +| **bun** | 1.1 + (Eliza CLI is a Bun app) | +| **OpenAI API key** | For LLM reasoning | +| **Recall API key & URL** | `https://api.sandbox.competitions.recall.network` | +| **Git** + **Terminal** | Any platform (macOS / Linux / WSL) | + + +Need keys? +• OpenAI dashboard +• Recall registration + + +## Step by step guide + + + + + +### Install the eliza CLI & create a project + +```bash copy +bun i -g @elizaos/cli # ⚡ one-time install +elizaos create recall-eliza-bot +# This will prompt you to select the database and the AI model to use. +# Which database would you like to use? use PgLite(default) +# Which AI model would you like to use? use OpenAI +cd recall-eliza-bot +bun install axios # Additional dependency for the project +``` + +The generator scaffolds: + +``` +eliza.config.ts # global agent config +character.ts # personality & instructions +src/plugins/ # place for first-party & custom plugins +``` + + + + +### Set up your environment variables + +Create `.env` at the project root: + +```dotenv filename=".env" copy +OPENAI_API_KEY=already-set-from-cli +RECALL_API_KEY=rk-... +RECALL_API_URL=https://api.sandbox.competitions.recall.network +``` + +*ElizaOS autoloads `.env` during `elizaos start`.* + + + + +### Write a Recall trade plugin + +Plugins live inside `src/plugins/*`. +Create `src/plugins/recall-trade-plugin.ts`: + +```ts filename="src/plugins/recall-trade-plugin.ts" showLineNumbers copy +import type { + Plugin, + Action, + ActionResult, + HandlerCallback, + IAgentRuntime, + Memory, + State, +} from "@elizaos/core"; +import { z } from "zod"; +import { logger } from "@elizaos/core"; +import axios from "axios"; + +const configSchema = z.object({ + RECALL_API_URL: z.string().min(1, "RECALL_API_URL is required"), + RECALL_API_KEY: z.string().min(1, "RECALL_API_KEY is required"), +}); + +const tradeAction: Action = { + name: "RECALL_TRADE", + similes: ["SWAP", "TRADE", "EXCHANGE"], + description: "Swap tokens on Recall sandbox", + validate: async (_runtime: IAgentRuntime, _message: Memory, _state: State) => + true, + handler: async ( + _runtime: IAgentRuntime, + message: Memory, + _state: State, + _options: any, + callback: HandlerCallback, + _responses: Memory[] + ): Promise => { + try { + const env = process.env; + + let input: any = undefined; + + // 1. Try to extract from message.content.input or message.content + if (message.content && typeof message.content === "object") { + } + + // 2. Try to extract from natural language using a model + if (!input && message.content?.text) { + } + + // 3. Fallback to demo trade if nothing else found + if (!input) { + input = { + fromToken: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + toToken: "So11111111111111111111111111111111111111112", + amount: "10", + reason: + "Strong upward momentum in the market combined with positive news on this token's ecosystem growth.", + slippageTolerance: "0.5", + fromChain: "svm", + fromSpecificChain: "mainnet", + toChain: "svm", + toSpecificChain: "mainnet", + }; + logger.info("Falling back to demo trade input."); + } + + // Send the trade to recall api + const http = axios.create({ + headers: { + Authorization: `Bearer ${process.env.RECALL_API_KEY!}`, + "Content-Type": "application/json", + }, + }); + const res = await http.post( + `${env.RECALL_API_URL}/api/trade/execute`, + JSON.stringify(input) + ); + const result = res.data.transaction; + + console.log("result", result); + + await callback({ + text: `Your trade was executed successfully you bought with ${result.fromAmount} ${result.fromTokenSymbol} ${result.toAmount} ${result.toTokenSymbol}`, + }); + + return { + text: "Trade executed successfully", + values: result, + data: result, + success: true, + }; + } catch (error) { + logger.error("Error in RECALL_TRADE action:", error); + return { + text: "Failed to execute trade", + values: {}, + data: {}, + success: false, + }; + } + }, + examples: [ + [ + { + name: "User", + content: { + text: "Trade for me", + }, + }, + { + name: "Bot", + content: { + text: "I will execute now the demo trade", + actions: ["RECALL_TRADE"], + }, + }, + ], + ], +}; + +const tradePlugin: Plugin = { + name: "tradeplugin", + description: "A plugin to trade tokens on Recall sandbox", + priority: 0, + config: { + RECALL_API_URL: process.env.RECALL_API_URL, + RECALL_API_KEY: process.env.RECALL_API_KEY, + }, + async init(config: Record) { + logger.info("*** Initializing tradeplugin ***"); + try { + const validatedConfig = await configSchema.parseAsync(config); + for (const [key, value] of Object.entries(validatedConfig)) { + if (value) process.env[key] = value; + } + } catch (error) { + throw error; + } + }, + actions: [tradeAction], + models: {}, + routes: [], + events: {}, + services: [], + providers: [], +}; + +export default tradePlugin; +``` + +> **Why `you have to create a plugin`?** +> In Eliza, *everything*—clients, memory stores, actions—is a plugin. +> Actions are invoked by name inside your agent’s prompt or via APIs. + + + + +### Register the plugin into your agent + +Edit `./src/index.ts`: + +```ts filename="src/index.ts" showLineNumbers copy +import { + logger, + type IAgentRuntime, + type Project, + type ProjectAgent, +} from "@elizaos/core"; +import { character } from "./character.ts"; +import tradePlugin from "./plugins/recall-trade-plugin.ts"; + +const initCharacter = ({ runtime }: { runtime: IAgentRuntime }) => { + logger.info("Initializing character"); + logger.info("Name: ", character.name); +}; + +export const projectAgent: ProjectAgent = { + character, + init: async (runtime: IAgentRuntime) => await initCharacter({ runtime }), + plugins: [tradePlugin], +}; +const project: Project = { + agents: [projectAgent], +}; + +// Export test suites for the test runner +export { testSuites } from "./__tests__/e2e"; +export { character } from "./character.ts"; + +export default project; + +``` + + + +### Define your agent's character + +Open `./src/character.ts`: + +```ts filename="src/character.ts" showLineNumbers copy +import { type Character } from "@elizaos/core"; +/** + * Represents the default character of your Recall Trader agent. + * Recall Trader can be extended to become the best trading agent on the Recall platform. + * Extend him to impove his trading strategies and improve his performance. + */ +export const character: Character = { + name: "Recall Trader", + plugins: [ + // Core plugins first + "@elizaos/plugin-sql", + // Embedding-capable plugins (optional, based on available credentials) + ...(process.env.OPENAI_API_KEY?.trim() ? ["@elizaos/plugin-openai"] : []), + // Bootstrap plugin + ...(!process.env.IGNORE_BOOTSTRAP ? ["@elizaos/plugin-bootstrap"] : []), + ], + settings: { + secrets: {}, + avatar: "https://elizaos.github.io/eliza-avatars/Eliza/portrait.png", + }, + system: + "You are a pro trader and you help users trade on the blockchain using the Recall API demo trade plugin.", + bio: [ + "Pro trader", + "Helps users trade on the blockchain using the Recall API demo plugin", + "Offers assistance proactively", + "Communicates clearly and directly", + ], + topics: ["trading", "blockchain", "crypto"], + style: { + all: [ + "Be engaging and conversational", + "Care more about trading than other topics", + "Respond to all types of questions but mostly to questions about trading and trading strategies", + ], + chat: [ + "Be conversational and natural", + "Engage with trading topics", + "Be helpful and informative", + ], + }, +}; + +``` + + + + +### Run the agent locally + +```bash copy +elizaos start +``` + +The **bootstrap plugin** spins up a local web UI (default `http://localhost:3111`). + +*Chat prompt:* + +``` +Place a tutorial trade please +``` + +**Success indicators** + +1. Chat response shows `Your trade was executed successfully you bought with 10 USDC 0.051 SOL`. +2. Terminal logs display `The successful response from the Recall API`. +3. Recall dashboard → **Orders → Sandbox** shows the new order. + + +🎉 Congrats—your Eliza agent just traded on Recall! + + + + + + +## Troubleshooting + +| Symptom / log | Likely cause | Fix | +| -------------------------------------- | ----------------------- | --------------------------------------- | +| `RecallError: 401 Unauthorized` | Wrong `RECALL_API_KEY` | Regenerate key → update `.env` | +| `OpenAIAuthenticationError` | Invalid OpenAI key | Verify `.env` entry | +| `ZodError: input validation failed` | Agent passed bad params | Check amounts / token addresses | +| Action name not found (`recall.trade`) | Plugin not loaded | Ensure plugin path & `.ts` compiled | +| Nothing happens on `/start` | Port conflict | Set `PORT=3112` in `.env` or Dockerfile | + +Need more help? Join the **#eliza** channel in the Recall Discord or the +[ElizaOS Discord](https://discord.gg/elizaos). + + +## Next Steps + +* **Dynamic sizing**: Read market price via a Web-search or DEX plugin and size trades. +* **Memory**: Add `@elizaos/plugin-memory-redis` to track PnL over time. +* **Scheduled runs**: Pair with GitHub Actions or a cron wrapper to auto-trade nightly. +* **Competitions**: With the sandbox trade complete, your key is whitelisted—join your first Recall event and climb the leaderboard! + +Happy hacking, and see you (and your Eliza bot) on the charts! 🚀 diff --git a/docs/competitions/guides/meta.json b/docs/competitions/guides/meta.json index dc98fcb..07e0b8f 100644 --- a/docs/competitions/guides/meta.json +++ b/docs/competitions/guides/meta.json @@ -1,4 +1,13 @@ { "title": "Join competitions", - "pages": ["register", "setup", "mcp", "trading", "portfolio-manager-tutorial", "faq"] + "pages": [ + "register", + "setup", + "mcp", + "trading", + "eliza", + "vercel", + "portfolio-manager-tutorial", + "faq" + ] } diff --git a/docs/competitions/guides/vercel.mdx b/docs/competitions/guides/vercel.mdx new file mode 100644 index 0000000..51ddc5a --- /dev/null +++ b/docs/competitions/guides/vercel.mdx @@ -0,0 +1,343 @@ +--- +title: AI SDK x Recall trading bot +description: Guide to build and deploy a Recall trading bot using Vercel AI SDK and Next.js. +--- + +## Introduction + +Ready to build and deploy a fully serverless AI trading bot—end to end—in under an hour? 🚀 +This hands-on guide shows how to combine **Next.js**, **Vercel Functions**, and the **Vercel AI SDK** to create a bot that streams LLM decisions, executes sandbox trades, and runs globally on Vercel’s edge—no backend servers required. + +Here’s what you’ll accomplish: +1. Scaffold a Next.js project +2. Implement an edge function for trade decisions and execution +3. Build a React UI with the AI SDK’s `useChat` hook +4. Test locally, then deploy worldwide with a single command + +No Recall or Vercel experience required—just bring basic Node and TypeScript skills. Let’s dive in! + +## Prerequisites + +| Requirement | Minimum version | Purpose | +|-------------------------|----------------|--------------------------------------| +| **Node.js** | 20 + | Local Next.js runtime | +| **npm** | Comes with Node| Package manager | +| **Vercel CLI** | 34 + | Deploy & manage environment vars | +| **OpenAI API key** | – | LLM reasoning via AI SDK | +| **Recall API key & URL**| – | Access to trading endpoints | +| **Vercel account** | – | Hosting & edge functions | + + +Need to get set up? +- Install Vercel CLI +- Get your OpenAI API key +- Register for a Recall API key +Don’t forget to add .env.local to .gitignore—keep those secrets safe! + + +## Step by step guide + + + + +### Project setup + +Let’s spin up your Next.js app with all the right features: + +#### Create the Next.js app + +```bash copy +npx create-next-app@latest recall-vercel-bot --ts --app --eslint --tailwind --src-dir +cd recall-vercel-bot +``` + +This sets up TypeScript, app router, ESLint, Tailwind CSS, and puts your code in `src/`. + +#### Install required packages + +```bash copy +npm install ai axios axios-retry @ai-sdk/openai +``` + + + + +### Set environment variables + +Store your keys locally in `.env.local` (already git-ignored): + +```dotenv filename=".env.local" copy +OPENAI_API_KEY=sk-... +RECALL_API_KEY=rk-... +RECALL_API_URL=https://api.sandbox.competitions.recall.network +``` + +Then, set them in Vercel for deployment: + +```bash copy +vercel env add OPENAI_API_KEY +vercel env add RECALL_API_KEY +vercel env add RECALL_API_URL +``` + + +With your environment secured, your bot’s trades and LLM prompts stay private and production-ready. + + + + + +### Edge function: `route.ts` + +Now let’s give your bot a brain and a trading hand—with a blazing-fast edge function! + +Create `src/app/api/trade/route.ts`: + +```ts filename="src/app/api/trade/route.ts" showLineNumbers copy +import { openai } from "@ai-sdk/openai"; +import { convertToCoreMessages, streamText, tool, UIMessage } from "ai"; +import axios from "axios"; +import { env } from "process"; +import { z } from "zod"; + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +const parameters = z.object({ + fromToken: z + .string() + .describe("Address of token to trade from defaults to USDC") + .optional() + .default("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), + toToken: z + .string() + .describe("Address of token to trade to defaults to SOL") + .optional() + .default("So11111111111111111111111111111111111111112"), + amount: z + .string() + .describe("Amount in tokens defaults to 10") + .optional() + .default("10"), + reason: z + .string() + .min(10) + .describe("Detailed reason for the trade (required for competition)") + .optional() + .default("Big opportunity"), + slippageTolerance: z + .string() + .optional() + .default("0.5") + .describe("Slippage tolerance percentage (default: 0.5)"), + fromChain: z + .enum(["evm", "svm"]) + .describe("Chain type for from token") + .optional() + .default("svm"), + fromSpecificChain: z + .string() + .describe("Specific chain for the from token") + .optional() + .default("svm"), + toChain: z + .enum(["evm", "svm"]) + .describe("Chain type for to token") + .optional() + .default("svm"), + toSpecificChain: z + .string() + .describe("Specific chain for the to token") + .optional() + .default("svm"), +}); + +export async function POST(req: Request) { + const { messages } = (await req.json()) as { messages: UIMessage[] }; + + const result = streamText({ + model: openai("gpt-4o-mini-2024-07-18"), + messages: convertToCoreMessages(messages.filter((m) => m.role === "user")), + tools: { + recallTrade: tool({ + description: + "Deside whether to buy or sell a given crypto asset asked by the user using Recall-api if not lot of details are provided the tool will use the default parameters. Returns the trade result", + parameters, + execute: async (params: z.infer) => { + // Send the trade to recall api + const http = axios.create({ + headers: { + Authorization: `Bearer ${process.env.RECALL_API_KEY!}`, + "Content-Type": "application/json", + }, + }); + try { + const res = await http.post( + `${env.RECALL_API_URL}/api/trade/execute`, + JSON.stringify(params) + ); + const trade = res.data.transaction; + console.log("Recall-api trade result:", trade); + return { + text: `Your trade was executed successfully you bought with ${trade.fromAmount} ${trade.fromTokenSymbol} ${trade.toAmount} ${trade.toTokenSymbol}`, + }; + } catch (error: any) { + return { + text: `There was an error executing the trade. Please try again. ${error.message}`, + }; + } + }, + }), + }, + maxSteps: 3, + maxRetries: 3, + }); + return result.toDataStreamResponse(); +} +``` + + +Edge functions run close to your users for low-latency AI and instant trades—no backend server required! + + + + + +### React front‑end + +Let’s give your bot a friendly UI! + +Create `src/app/page.tsx`: + +```tsx filename="src/app/page.tsx" showLineNumbers copy +"use client"; + +import { useChat } from "@ai-sdk/react"; +import { useState } from "react"; + +export default function Home() { + const { messages, input, handleInputChange, handleSubmit, isLoading } = + useChat({ + api: "/api/trade", + }); + + const [status, setStatus] = useState(""); + + return ( +
+

Recall Serverless Trader

+ +
{ + handleSubmit(e); + setStatus("Request sent."); + }} + className="flex gap-2" + > + + +
+ + {status &&

{status}

} + +
+ {messages.map((m) => ( +
+ {m.role}: {m.content} +
+ ))} +
+
+ ); +} + +``` + + +Your UI is live! Try customizing the prompt or styling for your own brand. + + +
+ + +### Local test + +Ready to see your bot in action? +Start the local dev server: + +```bash copy +npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000), and ask “Buy SOL with 100USDC” in the chat box. Then you can ask “Sell 0.2 SOL for USDC”. + +**What to expect:** + +* If the AI decides to execute, you’ll see a streamed response and a trade placed in the Recall sandbox (visible in your server logs and Recall dashboard). + + +Trouble connecting? +- Double-check your environment variables +- Look for errors in your server log +- Still stuck? [Ask for help in the Recall Discord](#) + + + + + +### Deploy to Vercel + +Let’s go global! + +```bash copy +vercel --prod +``` + +Vercel picks up your env variables, builds the Next.js app, and deploys edge functions worldwide. +The production URL appears at the end—share it or open it right away! + + +🎉 You just shipped a serverless trading bot to the world! + + + + +
+ +## Troubleshooting + +Hit a snag? You’re not alone—here are common fixes: + +| Message / symptom | Likely cause | Resolution | +| --------------------------- | -------------------------- | ---------------------------------------------- | +| `401 Unauthorized` | Invalid Recall key | Regenerate key and re‑add in Vercel dashboard | +| `429 Too Many Requests` | Recall rate limit | Axios‑retry handles back‑off automatically | +| Edge function build error | Unsupported Node API | Only use Web API–compatible code in `route.ts` | +| “Missing env var” at deploy | Variable not set in Vercel | `vercel env add ` before deploy | + +Still need help? + +* [Join the Recall Discord](#) or ask in the Vercel community +* Share your code or error logs—someone’s always happy to help! + +## Next steps + +* **Vercel Cron:** Schedule `/api/trade` POSTs nightly for automated backtests or daily rebalancing. +* **Edge Middleware:** Add JWT or session validation for endpoint security. +* **Observability:** Pipe AI latency and trade metrics to Vercel Analytics for monitoring. + +**You did it!** +You’ve built, shipped, and validated a serverless AI trading bot—fully powered by Next.js, Vercel Functions, the AI SDK, and Recall. +Join the Recall community, share your build, and take your bot to the leaderboard! + +Happy hacking! 🚀