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/README.md b/README.md index 7b73eb44..ff5e56ef 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Anyone - whether an SF-based AI researcher or a crypto-native builder - can brin - Perpetuals Trading with Adrena Protocol - **Solana Blinks** - - Lending by Lulo (Best APR for USDC) + - Lending by Lulo (Best APR for USDC and other assets) - Send Arcade Games - JupSOL staking - Solayer SOL (sSOL)staking @@ -357,7 +357,7 @@ Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on how to co Apache-2 License -## Funding +## Funding If you wanna give back any tokens or donations to the OSS community -- The Public Solana Agent Kit Treasury Address: diff --git a/docs/interfaces/JupiterTokenData.html b/docs/interfaces/JupiterTokenData.html index 9cc5ed9e..1ac15d69 100644 --- a/docs/interfaces/JupiterTokenData.html +++ b/docs/interfaces/JupiterTokenData.html @@ -9,8 +9,4 @@ permanent_delegate symbol tags -<<<<<<< HEAD

Properties

address: string
daily_volume: number
decimals: number
extensions: { coingeckoId?: string }
freeze_authority: null | string
logoURI: string
mint_authority: null | string
name: string
permanent_delegate: null | string
symbol: string
tags: string[]
-======= -

Properties

address: string
daily_volume: number
decimals: number
extensions: { coingeckoId?: string }
freeze_authority: null | string
logoURI: string
mint_authority: null | string
name: string
permanent_delegate: null | string
symbol: string
tags: string[]
->>>>>>> main diff --git a/src/actions/index.ts b/src/actions/index.ts index c9742098..8a606032 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -13,6 +13,8 @@ import stakeWithJupAction from "./stakeWithJup"; import stakeWithSolayerAction from "./stakeWithSolayer"; import registerDomainAction from "./registerDomain"; import lendAssetAction from "./lendAsset"; +import luloLendAction from "./luloLend"; +import luloWithdrawAction from "./luloWithdraw"; import createGibworkTaskAction from "./createGibworkTask"; import resolveSolDomainAction from "./resolveSolDomain"; import pythFetchPriceAction from "./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/luloLend.ts b/src/actions/luloLend.ts new file mode 100644 index 00000000..2051fc1d --- /dev/null +++ b/src/actions/luloLend.ts @@ -0,0 +1,54 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { luloLend } from "../tools"; + +const luloLendAction: Action = { + name: "WITHDRAW_ASSET", + similes: [ + "lend usdc", + "lend 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: "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/luloWithdraw.ts b/src/actions/luloWithdraw.ts new file mode 100644 index 00000000..85971077 --- /dev/null +++ b/src/actions/luloWithdraw.ts @@ -0,0 +1,54 @@ +import { Action } from "../types/action"; +import { SolanaAgentKit } from "../agent"; +import { z } from "zod"; +import { luloWithdraw } from "../tools"; + +const luloWithdrawAction: Action = { + name: "WITHDRAW_ASSET", + similes: [ + "withdraw usdc", + "withdraw 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 56517037..5f43e75f 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 e442206a..fbb62bb0 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -1054,6 +1054,78 @@ export class SolanaLendAssetTool extends Tool { } } +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.lendAssets(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", + }); + } + } +} + +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.withdrawAssets(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", + }); + } + } +} + export class SolanaTPSCalculatorTool extends Tool { name = "solana_get_tps"; description = "Get the current TPS of the Solana network"; @@ -2703,6 +2775,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/tools/index.ts b/src/tools/index.ts index 2363e3ab..bed21c3e 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -19,6 +19,8 @@ export * from "./get_wallet_address"; export * from "./launch_pumpfun_token"; export * from "./lend"; export * from "./manifest_trade"; +export * from "./lulo_lend"; +export * from "./lulo_withdraw"; export * from "./mint_nft"; export * from "./openbook_create_market"; export * from "./orca_close_position"; @@ -49,4 +51,4 @@ export * from "./transfer"; export * from "./flash_open_trade"; export * from "./flash_close_trade"; -export * from "./create_3land_collectible"; +export * from "./create_3land_collectible"; \ No newline at end of file diff --git a/src/tools/lend.ts b/src/tools/lend.ts index 732f00c3..53e574f6 100644 --- a/src/tools/lend.ts +++ b/src/tools/lend.ts @@ -56,4 +56,4 @@ export async function lendAsset( } catch (error: any) { throw new Error(`Lending failed: ${error.message}`); } -} +} \ No newline at end of file diff --git a/src/tools/lulo_lend.ts b/src/tools/lulo_lend.ts new file mode 100644 index 00000000..d7ddd02e --- /dev/null +++ b/src/tools/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_withdraw.ts b/src/tools/lulo_withdraw.ts new file mode 100644 index 00000000..e78bea16 --- /dev/null +++ b/src/tools/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 {