From e1869603903f5fc9c098de7c4dfce9e975baaad4 Mon Sep 17 00:00:00 2001 From: zhourunlai Date: Thu, 2 Jan 2025 19:28:56 +0800 Subject: [PATCH] deposit and withdraw with lulo --- .env.example | 3 +- src/actions/depositWithLulo.ts | 63 ++++++++++++++++ src/actions/index.ts | 4 + src/actions/withdrawWithLulo.ts | 63 ++++++++++++++++ src/agent/index.ts | 10 +++ src/langchain/index.ts | 72 ++++++++++++++++++ src/tools/index.ts | 1 + src/tools/stake_with_lulo.ts | 125 ++++++++++++++++++++++++++++++++ 8 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 src/actions/depositWithLulo.ts create mode 100644 src/actions/withdrawWithLulo.ts create mode 100644 src/tools/stake_with_lulo.ts diff --git a/.env.example b/.env.example index e8004fdf..28424225 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,5 @@ OPENAI_API_KEY= RPC_URL= SOLANA_PRIVATE_KEY= JUPITER_REFERRAL_ACCOUNT= -JUPITER_FEE_BPS= \ No newline at end of file +JUPITER_FEE_BPS= +FLEXLEND_API_KEY= \ No newline at end of file diff --git a/src/actions/depositWithLulo.ts b/src/actions/depositWithLulo.ts new file mode 100644 index 00000000..eb45d231 --- /dev/null +++ b/src/actions/depositWithLulo.ts @@ -0,0 +1,63 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { depositWithLulo } from "../tools"; + +const depositWithLuloAction: Action = { + name: "DEPOSIT_WITH_LULO", + similes: [ + "deposit with lulo", + "deposit usdc with lulo", + "deposit pyusc with lulo", + "deposit usds with lulo", + "deposit usdt with lulo", + "deposit SOL with lulo", + "deposit jitoSOL with lulo", + "deposit bSOL with lulo", + "deposit mSOL with lulo", + "deposit BONK with lulo", + "deposit JUP with lulo", + ], + description: + "Deposit tokens with Lulo staking protocol", + examples: [ + [ + { + input: { + mintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + amount: 1.5, + }, + output: { + status: "success", + signature: "5KtPn3...", + message: "Successfully deposited 1.5 SOL with lulo", + }, + explanation: "Deposit 1.5 SOL with lulo", + }, + ], + ], + schema: z.object({ + mintAddress: z.string().describe("Token mint address to deposit"), + amount: z.number().positive().describe("Amount to deposit"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const mintAddress = input.mintAddress as string; + const amount = input.amount as number; + + const res = await depositWithLulo(agent, mintAddress, amount); + return { + status: "success", + res, + message: `Successfully deposited ${amount} of ${mintAddress}`, + }; + } catch (error: any) { + return { + status: "error", + message: `deposit failed: ${error.message}`, + }; + } + }, +}; + +export default depositWithLuloAction; diff --git a/src/actions/index.ts b/src/actions/index.ts index e61ba6ca..69287878 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -11,6 +11,8 @@ import getTPSAction from "./getTPS"; import fetchPriceAction from "./fetchPrice"; import stakeWithJupAction from "./stakeWithJup"; import stakeWithSolayerAction from "./stakeWithSolayer"; +import depositWithLuloAction from "./depositWithLulo"; +import withdrawWithLuloAction from "./withdrawWithLulo"; import registerDomainAction from "./registerDomain"; import lendAssetAction from "./lendAsset"; import createGibworkTaskAction from "./createGibworkTask"; @@ -44,6 +46,8 @@ export const ACTIONS = { FETCH_PRICE_ACTION: fetchPriceAction, STAKE_WITH_JUP_ACTION: stakeWithJupAction, STAKE_WITH_SOLAYER_ACTION : stakeWithSolayerAction, + DEPOSIT_WITH_LULO_ACTION: depositWithLuloAction, + WITHDRAW_WITH_LULO_ACTION: withdrawWithLuloAction, REGISTER_DOMAIN_ACTION: registerDomainAction, LEND_ASSET_ACTION: lendAssetAction, CREATE_GIBWORK_TASK_ACTION: createGibworkTaskAction, diff --git a/src/actions/withdrawWithLulo.ts b/src/actions/withdrawWithLulo.ts new file mode 100644 index 00000000..57192035 --- /dev/null +++ b/src/actions/withdrawWithLulo.ts @@ -0,0 +1,63 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { withdrawWithLulo } from "../tools"; + +const withdrawWithLuloAction: Action = { + name: "WITHDRAW_WITH_LULO", + similes: [ + "withdraw with lulo", + "withdraw usdc with lulo", + "withdraw pyusc with lulo", + "withdraw usds with lulo", + "withdraw usdt with lulo", + "withdraw SOL with lulo", + "withdraw jitoSOL with lulo", + "withdraw bSOL with lulo", + "withdraw mSOL with lulo", + "withdraw BONK with lulo", + "withdraw JUP with lulo", + ], + description: + "Withdraw tokens with Lulo staking protocol", + examples: [ + [ + { + input: { + mintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + amount: 1.5, + }, + output: { + status: "success", + signature: "5KtPn3...", + message: "Successfully withdrawed 1.5 SOL with lulo", + }, + explanation: "Withdraw 1.5 SOL with lulo", + }, + ], + ], + schema: z.object({ + mintAddress: z.string().describe("Token mint address to withdraw"), + amount: z.number().positive().describe("Amount to withdraw"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const mintAddress = input.mintAddress as string; + const amount = input.amount as number; + + const res = await withdrawWithLulo(agent, mintAddress, amount); + return { + status: "success", + res, + message: `Successfully withdrawed ${amount} of ${mintAddress}`, + }; + } catch (error: any) { + return { + status: "error", + message: `withdraw failed: ${error.message}`, + }; + } + }, +}; + +export default withdrawWithLuloAction; diff --git a/src/agent/index.ts b/src/agent/index.ts index 19a8492c..6e560ff0 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -31,6 +31,8 @@ import { getTokenDataByTicker, stakeWithJup, stakeWithSolayer, + depositWithLulo, + withdrawWithLulo, sendCompressedAirdrop, orcaCreateSingleSidedLiquidityPool, orcaCreateCLMM, @@ -259,6 +261,14 @@ export class SolanaAgentKit { return stakeWithSolayer(this, amount); } + async deposit(mintAddress: string, amount: number): Promise { + return depositWithLulo(this, mintAddress, amount); + } + + async withdraw(mintAddress: string, amount: number): Promise { + return withdrawWithLulo(this, mintAddress, amount); + } + async sendCompressedAirdrop( mintAddress: string, amount: number, diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 72a1f5ba..5ab8c7cd 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -895,6 +895,76 @@ export class SolanaRestakeTool extends Tool { } } +export class SolanaDepositWithLuloTool extends Tool { + name = "solana_deposit_with_lulo"; + description = `This tool can be used to deposit your token with lulo. + + Inputs ( input is a JSON string ): + mintAddress: string, eg "So11111111111111111111111111111111111111112" (required) + amount: number, eg 1 or 0.01 (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + + const tx = await this.solanaKit.deposit(parsedInput.mintAddress, parsedInput.amount); + + return JSON.stringify({ + status: "success", + message: "Deposit successfully", + transaction: tx, + mintAddress: parsedInput.mintAddress, + amount: parsedInput.amount, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} + +export class SolanaWithdrawWithLuloTool extends Tool { + name = "solana_withdraw_with_lulo"; + description = `This tool can be used to withdraw your token with lulo. + + Inputs ( input is a JSON string ): + mintAddress: string, eg "So11111111111111111111111111111111111111112" (required) + amount: number, eg 1 or 0.01 (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + protected async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + + const tx = await this.solanaKit.withdraw(parsedInput.mintAddress, parsedInput.amount); + + return JSON.stringify({ + status: "success", + message: "Withdraw successfully", + transaction: tx, + mintAddress: parsedInput.mintAddress, + amount: parsedInput.amount, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} + /** * Tool to fetch the price of a token in USDC */ @@ -2031,6 +2101,8 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaTPSCalculatorTool(solanaKit), new SolanaStakeTool(solanaKit), new SolanaRestakeTool(solanaKit), + new SolanaDepositWithLuloTool(solanaKit), + new SolanaWithdrawWithLuloTool(solanaKit), new SolanaFetchPriceTool(solanaKit), new SolanaGetDomainTool(solanaKit), new SolanaTokenDataTool(solanaKit), diff --git a/src/tools/index.ts b/src/tools/index.ts index 825dff8d..3648449d 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -22,6 +22,7 @@ export * from "./get_tps"; export * from "./get_token_data"; export * from "./stake_with_jup"; export * from "./stake_with_solayer"; +export * from "./stake_with_lulo"; export * from "./fetch_price"; export * from "./send_compressed_airdrop"; export * from "./orca_close_position"; diff --git a/src/tools/stake_with_lulo.ts b/src/tools/stake_with_lulo.ts new file mode 100644 index 00000000..d8e865b5 --- /dev/null +++ b/src/tools/stake_with_lulo.ts @@ -0,0 +1,125 @@ +import { VersionedTransaction } from "@solana/web3.js"; +import { SolanaAgentKit } from "../index"; + +/** + * Deposit tokens for yields using Lulo + * @param agent SolanaAgentKit instance + * @param mintAddress Mint address to deposit + * @param amount Amount to deposit + * @returns Transaction signature + */ +export async function depositWithLulo( + agent: SolanaAgentKit, + mintAddress: string, + amount: number, +): Promise { + try { + const response = await fetch( + `https://api.flexlend.fi/generate/account/deposit`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-wallet-pubkey": agent.wallet.publicKey.toBase58(), + "x-api-key": process.env.FLEXLEND_API_KEY! + }, + body: JSON.stringify({ + owner: agent.wallet.publicKey.toBase58(), + mintAddress: mintAddress, + depositAmount: amount, + }), + }, + ); + + const data = await response.json(); + + // Deserialize the transaction + const luloTxn = VersionedTransaction.deserialize( + Buffer.from(data.transaction, "base64"), + ); + + // Get a recent blockhash and set it + const { blockhash } = await agent.connection.getLatestBlockhash(); + luloTxn.message.recentBlockhash = blockhash; + + // Sign and send transaction + luloTxn.sign([agent.wallet]); + + const signature = await agent.connection.sendTransaction(luloTxn, { + preflightCommitment: "confirmed", + maxRetries: 3, + }); + + // Wait for confirmation using the latest strategy + const latestBlockhash = await agent.connection.getLatestBlockhash(); + await agent.connection.confirmTransaction({ + signature, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }); + + return signature; + } catch (error: any) { + throw new Error(`Deposit failed: ${error.message}`); + } +} + +/** + * Withdraw tokens for yields using Lulo + * @param agent SolanaAgentKit instance + * @param mintAddress Mint address to deposit + * @param amount Amount to deposit + * @returns Transaction signature + */ +export async function withdrawWithLulo( + agent: SolanaAgentKit, + mintAddress: string, + amount: number, + ): Promise { + try { + const response = await fetch( + `https://api.flexlend.fi/generate/account/withdraw`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-wallet-pubkey": agent.wallet.publicKey.toBase58(), + "x-api-key": process.env.FLEXLEND_API_KEY! + }, + body: JSON.stringify({ + owner: agent.wallet.publicKey.toBase58(), + mintAddress: mintAddress, + depositAmount: amount, + }), + }, + ); + + const data = await response.json(); + + const luloTxn = VersionedTransaction.deserialize( + Buffer.from(data.transaction, "base64"), + ); + + const { blockhash } = await agent.connection.getLatestBlockhash(); + luloTxn.message.recentBlockhash = blockhash; + + luloTxn.sign([agent.wallet]); + + const signature = await agent.connection.sendTransaction(luloTxn, { + preflightCommitment: "confirmed", + maxRetries: 3, + }); + + const latestBlockhash = await agent.connection.getLatestBlockhash(); + await agent.connection.confirmTransaction({ + signature, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }); + + return signature; + } catch (error: any) { + throw new Error(`Withdraw failed: ${error.message}`); + } + } + \ No newline at end of file