From 33737f8bef36abd1d6e3de1dd5368f6e8f25dea4 Mon Sep 17 00:00:00 2001 From: zhourunlai Date: Thu, 2 Jan 2025 19:28:56 +0800 Subject: [PATCH] lend and withdraw asset with lulo, not only usdc --- .env.example | 3 +- src/actions/index.ts | 4 ++ src/actions/lulo/luloLend.ts | 62 ++++++++++++++++++++++++++ src/actions/lulo/luloWithdraw.ts | 62 ++++++++++++++++++++++++++ src/agent/index.ts | 10 +++++ src/langchain/index.ts | 4 ++ src/langchain/lulo/index.ts | 2 + src/langchain/lulo/lulo_lend.ts | 37 ++++++++++++++++ src/langchain/lulo/lulo_withdraw.ts | 37 ++++++++++++++++ src/tools/lulo/index.ts | 2 + src/tools/lulo/lulo_lend.ts | 64 ++++++++++++++++++++++++++ src/tools/lulo/lulo_withdraw.ts | 69 +++++++++++++++++++++++++++++ src/types/index.ts | 1 + 13 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 src/actions/lulo/luloLend.ts create mode 100644 src/actions/lulo/luloWithdraw.ts create mode 100644 src/langchain/lulo/lulo_lend.ts create mode 100644 src/langchain/lulo/lulo_withdraw.ts create mode 100644 src/tools/lulo/lulo_lend.ts create mode 100644 src/tools/lulo/lulo_withdraw.ts diff --git a/.env.example b/.env.example index 52fdf995..b37c8d3f 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,5 @@ RPC_URL= SOLANA_PRIVATE_KEY= JUPITER_REFERRAL_ACCOUNT= JUPITER_FEE_BPS= -FLASH_PRIVILEGE= referral | nft | none \ No newline at end of file +FLASH_PRIVILEGE= referral | nft | none +FLEXLEND_API_KEY= diff --git a/src/actions/index.ts b/src/actions/index.ts index e878aa1c..c7616db2 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -13,6 +13,8 @@ import stakeWithJupAction from "./jupiter/stakeWithJup"; import stakeWithSolayerAction from "./solayer/stakeWithSolayer"; import registerDomainAction from "./sns/registerDomain"; import lendAssetAction from "./lulo/lendAsset"; +import luloLendAction from "./lulo/luloLend"; +import luloWithdrawAction from "./lulo/luloWithdraw"; import createGibworkTaskAction from "./gibwork/createGibworkTask"; import resolveSolDomainAction from "./sns/resolveSolDomain"; import pythFetchPriceAction from "./pyth/pythFetchPrice"; @@ -48,6 +50,8 @@ export const ACTIONS = { STAKE_WITH_SOLAYER_ACTION: stakeWithSolayerAction, REGISTER_DOMAIN_ACTION: registerDomainAction, LEND_ASSET_ACTION: lendAssetAction, + LULO_LEND_ACTION: luloLendAction, + LULO_WITHDRAW_ACTION: luloWithdrawAction, CREATE_GIBWORK_TASK_ACTION: createGibworkTaskAction, RESOLVE_SOL_DOMAIN_ACTION: resolveSolDomainAction, PYTH_FETCH_PRICE_ACTION: pythFetchPriceAction, diff --git a/src/actions/lulo/luloLend.ts b/src/actions/lulo/luloLend.ts new file mode 100644 index 00000000..937930c2 --- /dev/null +++ b/src/actions/lulo/luloLend.ts @@ -0,0 +1,62 @@ +import { Action } from "../../types/action"; +import { SolanaAgentKit } from "../../agent"; +import { z } from "zod"; +import { luloLend } from "../../tools/lulo"; + +const luloLendAction: Action = { + name: "LULO_LEND", + similes: [ + "lend USDC with lulo", + "lend PYUSD with lulo", + "lend USDS with lulo", + "lend USDT with lulo", + "lend SQL with lulo", + "lend jitoSQL with lulo", + "lend bSQL with lulo", + "lend mSQL with lulo", + "lend BONK with lulo", + "lend JUP with lulo", + ], + description: "Lend SPL tokens using Lulo protocol", + examples: [ + [ + { + input: { + mintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + amount: 100, + }, + output: { + status: "success", + signature: "4xKpN2...", + message: "Successfully lend 100 USDC", + }, + explanation: "Lend 100 USDC on Lulo", + }, + ], + ], + schema: z.object({ + mintAddress: z.string().describe("SPL Mint address"), + amount: z.number().positive().describe("Amount to lend"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const mintAddress = input.mintAddress as string; + const amount = input.amount as number; + + const response = await luloLend(agent, mintAddress, amount); + + return { + status: "success", + signature: response, + message: `Successfully lend ${amount} of token ${mintAddress}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Lend failed: ${error.message}`, + }; + } + }, +}; + +export default luloLendAction; diff --git a/src/actions/lulo/luloWithdraw.ts b/src/actions/lulo/luloWithdraw.ts new file mode 100644 index 00000000..6c709335 --- /dev/null +++ b/src/actions/lulo/luloWithdraw.ts @@ -0,0 +1,62 @@ +import { Action } from "../../types/action"; +import { SolanaAgentKit } from "../../agent"; +import { z } from "zod"; +import { luloWithdraw } from "../../tools/lulo"; + +const luloWithdrawAction: Action = { + name: "LULO_WITHDRAW", + similes: [ + "withdraw USDC with lulo", + "withdraw PYUSD with lulo", + "withdraw USDS with lulo", + "withdraw USDT with lulo", + "withdraw SQL with lulo", + "withdraw jitoSQL with lulo", + "withdraw bSQL with lulo", + "withdraw mSQL with lulo", + "withdraw BONK with lulo", + "withdraw JUP with lulo", + ], + description: "Withdraw SPL tokens using Lulo protocol", + examples: [ + [ + { + input: { + mintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + amount: 100, + }, + output: { + status: "success", + signature: "4xKpN2...", + message: "Successfully withdraw 100 USDC", + }, + explanation: "Withdraw 100 USDC on Lulo", + }, + ], + ], + schema: z.object({ + mintAddress: z.string().describe("SPL Mint address"), + amount: z.number().positive().describe("Amount to lend"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const mintAddress = input.mintAddress as string; + const amount = input.amount as number; + + const response = await luloWithdraw(agent, mintAddress, amount); + + return { + status: "success", + signature: response, + message: `Successfully withdraw ${amount} of token ${mintAddress}`, + }; + } catch (error: any) { + return { + status: "error", + message: `Withdraw failed: ${error.message}`, + }; + } + }, +}; + +export default luloWithdrawAction; diff --git a/src/agent/index.ts b/src/agent/index.ts index 25037c99..c9bb575c 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -14,6 +14,8 @@ import { getPrimaryDomain, launchPumpFunToken, lendAsset, + luloLend, + luloWithdraw, mintCollectionNFT, openbookCreateMarket, manifestCreateMarket, @@ -275,6 +277,14 @@ export class SolanaAgentKit { return lendAsset(this, amount); } + async luloLend(mintAddress: string, amount: number): Promise { + return luloLend(this, mintAddress, amount); + } + + async luloWithdraw(mintAddress: string, amount: number): Promise { + return luloWithdraw(this, mintAddress, amount); + } + async getTPS(): Promise { return getTPS(this); } diff --git a/src/langchain/index.ts b/src/langchain/index.ts index c2e5aff5..7d9f96bf 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -39,6 +39,8 @@ import { SolanaPumpfunTokenLaunchTool, SolanaCreateImageTool, SolanaLendAssetTool, + SolanaLuloLendTool, + SolanaLuloWithdrawTool, SolanaTPSCalculatorTool, SolanaStakeTool, SolanaRestakeTool, @@ -100,6 +102,8 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaPumpfunTokenLaunchTool(solanaKit), new SolanaCreateImageTool(solanaKit), new SolanaLendAssetTool(solanaKit), + new SolanaLuloLendTool(solanaKit), + new SolanaLuloWithdrawTool(solanaKit), new SolanaTPSCalculatorTool(solanaKit), new SolanaStakeTool(solanaKit), new SolanaRestakeTool(solanaKit), diff --git a/src/langchain/lulo/index.ts b/src/langchain/lulo/index.ts index f4ecf393..e31add17 100644 --- a/src/langchain/lulo/index.ts +++ b/src/langchain/lulo/index.ts @@ -1 +1,3 @@ export * from "./lend_asset"; +export * from "./lulo_lend"; +export * from "./lulo_withdraw"; diff --git a/src/langchain/lulo/lulo_lend.ts b/src/langchain/lulo/lulo_lend.ts new file mode 100644 index 00000000..0d5371e6 --- /dev/null +++ b/src/langchain/lulo/lulo_lend.ts @@ -0,0 +1,37 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaLuloLendTool extends Tool { + name = "solana_lulo_lend"; + description = `Lend token for yield using Lulo. (support USDC/PYUSD/USDS/USDT/SOL/jitoSOL/bSOL/mSOL/BONK/JUP) + Inputs: + mintAddress: string, eg "So11111111111111111111111111111111111111112" (required) + amount: number, eg 1, 0.01 (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const mintAddress = parsedInput.mintAddress + const amount = parsedInput.amount; + + const tx = await this.solanaKit.luloLend(mintAddress, amount); + + return JSON.stringify({ + status: "success", + message: "Asset lent successfully", + transaction: tx, + amount, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } + } \ No newline at end of file diff --git a/src/langchain/lulo/lulo_withdraw.ts b/src/langchain/lulo/lulo_withdraw.ts new file mode 100644 index 00000000..fe2671d2 --- /dev/null +++ b/src/langchain/lulo/lulo_withdraw.ts @@ -0,0 +1,37 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; + +export class SolanaLuloWithdrawTool extends Tool { + name = "solana_lulo_withdraw"; + description = `Withdraw token USDC using Lulo. (support USDC/PYUSD/USDS/USDT/SOL/jitoSOL/bSOL/mSOL/BONK/JUP) + Inputs (input is a json string): + mintAddress: string, eg "So11111111111111111111111111111111111111112" (required) + amount: number, eg 1, 0.01 (required)`; + + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + + async _call(input: string): Promise { + try { + const parsedInput = JSON.parse(input); + const mintAddress = parsedInput.mintAddress + const amount = parsedInput.amount; + + const tx = await this.solanaKit.luloWithdraw(mintAddress, amount); + + return JSON.stringify({ + status: "success", + message: "Asset withdraw successfully", + transaction: tx, + amount, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } + } \ No newline at end of file diff --git a/src/tools/lulo/index.ts b/src/tools/lulo/index.ts index a28ed36e..2a9ad206 100644 --- a/src/tools/lulo/index.ts +++ b/src/tools/lulo/index.ts @@ -1 +1,3 @@ export * from "./lend"; +export * from "./lulo_lend"; +export * from "./lulo_withdraw"; diff --git a/src/tools/lulo/lulo_lend.ts b/src/tools/lulo/lulo_lend.ts new file mode 100644 index 00000000..ab4610c7 --- /dev/null +++ b/src/tools/lulo/lulo_lend.ts @@ -0,0 +1,64 @@ +import { VersionedTransaction } from "@solana/web3.js"; +import { SolanaAgentKit } from "../../index"; + +/** + * Lend tokens for yields using Lulo + * @param agent SolanaAgentKit instance + * @param mintAddress SPL Mint address + * @param amount Amount to lend + * @returns Transaction signature + */ +export async function luloLend( + agent: SolanaAgentKit, + mintAddress: string, + amount: number, +): Promise { + try { + const response = await fetch( + `https://api.flexlend.fi/generate/account/deposit?priorityFee=50000`, + { + 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.toString(), + }), + }, + ); + const { data: { transactionMeta } } = await response.json() + + // Deserialize the transaction + const luloTxn = VersionedTransaction.deserialize( + Buffer.from(transactionMeta[0].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(`Lending failed: ${error.message}`); + } +} diff --git a/src/tools/lulo/lulo_withdraw.ts b/src/tools/lulo/lulo_withdraw.ts new file mode 100644 index 00000000..cfc88b22 --- /dev/null +++ b/src/tools/lulo/lulo_withdraw.ts @@ -0,0 +1,69 @@ +import { VersionedTransaction } from "@solana/web3.js"; +import { SolanaAgentKit } from "../../index"; + +/** + * Withdraw tokens for yields using Lulo + * @param agent SolanaAgentKit instance + * @param mintAddress SPL Mint address + * @param amount Amount to withdraw + * @returns Transaction signature + */ +export async function luloWithdraw( + agent: SolanaAgentKit, + mintAddress: string, + amount: number, +): Promise { + try { + if (!agent.config.FLEXLEND_API_KEY) { + throw new Error("Lulo API key not found in agent configuration"); + } + + const response = await fetch( + `https://api.flexlend.fi/generate/account/withdraw?priorityFee=50000`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-wallet-pubkey": agent.wallet.publicKey.toBase58(), + "x-api-key": agent.config.FLEXLEND_API_KEY + }, + body: JSON.stringify({ + owner: agent.wallet.publicKey.toBase58(), + mintAddress: mintAddress, + depositAmount: amount, + }), + }, + ); + + const { data: { transactionMeta } } = await response.json() + + // Deserialize the transaction + const luloTxn = VersionedTransaction.deserialize( + Buffer.from(transactionMeta[0].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(`Lending failed: ${error.message}`); + } +} diff --git a/src/types/index.ts b/src/types/index.ts index 01ac152a..1fbec8f5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -7,6 +7,7 @@ export interface Config { JUPITER_REFERRAL_ACCOUNT?: string; JUPITER_FEE_BPS?: number; FLASH_PRIVILEGE?: string; + FLEXLEND_API_KEY?: string; } export interface Creator {