From b943dbb23e04968819d4c458c77481047dacc413 Mon Sep 17 00:00:00 2001 From: Arihant Bansal <17180950+arihantbansal@users.noreply.github.com> Date: Wed, 11 Dec 2024 02:03:52 +0530 Subject: [PATCH 1/3] refactor: lulo blink --- src/constants/index.ts | 5 ----- src/tools/lend.ts | 42 +++++++++--------------------------------- src/types/index.ts | 6 ------ 3 files changed, 9 insertions(+), 44 deletions(-) diff --git a/src/constants/index.ts b/src/constants/index.ts index dfcc9f6..7c1d9c5 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -28,8 +28,3 @@ export const DEFAULT_OPTIONS = { * Jupiter API URL */ export const JUP_API = "https://quote-api.jup.ag/v6"; - -/** - * LULO (fka Flexlend) API URL - */ -export const LULO_API = "https://api.flexlend.fi"; diff --git a/src/tools/lend.ts b/src/tools/lend.ts index 5fd85b7..d31283d 100644 --- a/src/tools/lend.ts +++ b/src/tools/lend.ts @@ -1,56 +1,32 @@ import { VersionedTransaction } from "@solana/web3.js"; -import { SolanaAgentKit } from "../index"; -import { LuloAccountDetailsResponse, LuloDepositAssetMint } from "../types"; -import { getPriorityFees } from "../utils/send_tx"; -import { LULO_API } from "../constants"; +import { LuloAccountDetailsResponse } from "../types"; +import { SolanaAgentKit } from "../agent"; /** * Lend tokens for yields using Lulo * @param agent SolanaAgentKit instance - * @param asset Mint address of the token to lend (as supported by Lulo) - * @param amount Amount to lend (in token decimals) - * @param LULO_API_KEY Valid API key for Lulo + * @param amount Amount of USDC to lend * @returns Transaction signature */ export async function lendAsset( agent: SolanaAgentKit, - asset: LuloDepositAssetMint, amount: number, - LULO_API_KEY = "", ): Promise { try { - if (!LULO_API_KEY) { - throw new Error("Missing Lulo API key"); - } - - const request = { - owner: agent.wallet.publicKey.toBase58(), - mintAddress: asset.toBase58(), - depositAmount: amount.toString(), - }; - - const priorityFees = await getPriorityFees(agent.connection); - const priority = `?priorityFee=${priorityFees.median}`; - const response = await fetch( - `${LULO_API}/generate/account/deposit${priority}`, + `https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`, { method: "POST", - headers: { - "Content-Type": "application/json", - "x-wallet-pubkey": agent.wallet.publicKey.toBase58(), - "x-api-key": LULO_API_KEY, - }, - body: JSON.stringify(request), + body: JSON.stringify({ + account: agent.wallet.publicKey.toBase58(), + }), }, ); - const { - data: { transactionMeta }, - } = await response.json(); + const data = await response.json(); const luloTxn = VersionedTransaction.deserialize( - Buffer.from(transactionMeta[0].transaction, "base64"), + Buffer.from(data.transaction, "base64"), ); // Sign and send transaction diff --git a/src/types/index.ts b/src/types/index.ts index 16d2c4c..62c6991 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,4 @@ import { PublicKey } from "@solana/web3.js"; -import { TOKENS } from "../constants"; export interface Creator { address: string; @@ -24,11 +23,6 @@ export interface MintCollectionNFTResponse { metadata: PublicKey; } -/** - * Mint addresses of supported tokens for lending on Lulo - */ -export type LuloDepositAssetMint = (typeof TOKENS)[keyof typeof TOKENS]; - /** * Lulo Account Details response format */ From 12af3c434f3b3869e2c1e554c27d1426266067f9 Mon Sep 17 00:00:00 2001 From: Arihant Bansal <17180950+arihantbansal@users.noreply.github.com> Date: Wed, 11 Dec 2024 02:05:22 +0530 Subject: [PATCH 2/3] fix --- README.md | 2 -- src/agent/index.ts | 10 +++------- src/langchain/index.ts | 10 +++------- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 2dd01d8..8b7ae7d 100644 --- a/README.md +++ b/README.md @@ -104,9 +104,7 @@ import { PublicKey } from '@solana/web3.js'; const signature = await lendAsset( agent, - new PublicKey('asset-mint'), 100, // amount - "lulo-api-key" ); ``` diff --git a/src/agent/index.ts b/src/agent/index.ts index b165ccc..59ee62d 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -13,7 +13,7 @@ import { getLendingDetails, getTPS, } from "../tools"; -import { CollectionOptions, LuloDepositAssetMint } from "../types"; +import { CollectionOptions } from "../types"; import { DEFAULT_OPTIONS } from "../constants"; /** @@ -96,12 +96,8 @@ export class SolanaAgentKit { return trade(this, outputMint, inputAmount, inputMint, slippageBps); } - async lendAssets( - asset: LuloDepositAssetMint, - amount: number, - LULO_API_KEY: string, - ) { - return lendAsset(this, asset, amount, LULO_API_KEY); + async lendAssets(amount: number) { + return lendAsset(this, amount); } async fetchLendingDetails(LULO_API_KEY: string) { diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 3819a5c..b52a8b5 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -193,7 +193,7 @@ export class SolanaGetWalletAddressTool extends Tool { export class SolanaLendAssetTool extends Tool { name = "solana_lend_asset"; description = - "Lend idle assets for yield using Lulo. Input should be JSON with: {asset: string, amount: number, luloApiKey: string}"; + "Lend idle assets for yield using Lulo. Input should be JSON with: {amount: number}"; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -201,13 +201,9 @@ export class SolanaLendAssetTool extends Tool { async _call(input: string): Promise { try { - const { asset, amount, luloApiKey } = JSON.parse(input); + const { amount } = JSON.parse(input); - const tx = await this.solanaKit.lendAssets( - new PublicKey(asset), - amount, - luloApiKey, - ); + const tx = await this.solanaKit.lendAssets(amount); return `Asset lent successfully. Transaction: ${tx}`; } catch (error: any) { From eb7746ec50fd9859c2ffc9c3d5f3e451dc28392e Mon Sep 17 00:00:00 2001 From: aryan Date: Fri, 13 Dec 2024 15:01:25 +0530 Subject: [PATCH 3/3] fix: lend using lulo --- src/agent/index.ts | 5 ---- src/langchain/index.ts | 65 ++++++++++++++++-------------------------- src/tools/lend.ts | 58 ++++++++++++++++--------------------- 3 files changed, 49 insertions(+), 79 deletions(-) diff --git a/src/agent/index.ts b/src/agent/index.ts index 9564695..0d7a4ef 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -10,7 +10,6 @@ import { trade, registerDomain, launchPumpFunToken, - getLendingDetails, lendAsset, getTPS, } from "../tools"; @@ -92,10 +91,6 @@ export class SolanaAgentKit { return lendAsset(this, amount); } - async fetchLendingDetails(LULO_API_KEY: string) { - return getLendingDetails(this, LULO_API_KEY); - } - async getTPS() { return getTPS(this); } diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 972a360..2ba3b23 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -64,7 +64,7 @@ export class SolanaTransferTool extends Tool { const tx = await this.solanaKit.transfer( recipient, parsedInput.amount, - mintAddress, + mintAddress ); return JSON.stringify({ @@ -102,7 +102,7 @@ export class SolanaDeployTokenTool extends Tool { input.decimals > 9) ) { throw new Error( - "decimals must be a number between 0 and 9 when provided", + "decimals must be a number between 0 and 9 when provided" ); } if ( @@ -159,7 +159,7 @@ export class SolanaDeployCollectionTool extends Tool { input.royaltyBasisPoints > 10000) ) { throw new Error( - "royaltyBasisPoints must be a number between 0 and 10000 when provided", + "royaltyBasisPoints must be a number between 0 and 10000 when provided" ); } if (input.creators) { @@ -169,7 +169,7 @@ export class SolanaDeployCollectionTool extends Tool { input.creators.forEach((creator: any, index: number) => { if (!creator.address || typeof creator.address !== "string") { throw new Error( - `creator[${index}].address is required and must be a string`, + `creator[${index}].address is required and must be a string` ); } if ( @@ -178,7 +178,7 @@ export class SolanaDeployCollectionTool extends Tool { creator.percentage > 100 ) { throw new Error( - `creator[${index}].percentage must be a number between 0 and 100`, + `creator[${index}].percentage must be a number between 0 and 100` ); } }); @@ -246,9 +246,7 @@ export class SolanaMintNFTTool extends Tool { const result = await this.solanaKit.mintNFT( new PublicKey(parsedInput.collectionMint), parsedInput.metadata, - parsedInput.recipient - ? new PublicKey(parsedInput.recipient) - : undefined, + parsedInput.recipient ? new PublicKey(parsedInput.recipient) : undefined ); return JSON.stringify({ @@ -292,7 +290,7 @@ export class SolanaTradeTool extends Tool { parsedInput.inputMint ? new PublicKey(parsedInput.inputMint) : new PublicKey("So11111111111111111111111111111111111111112"), - parsedInput.slippageBps, + parsedInput.slippageBps ); return JSON.stringify({ @@ -373,7 +371,7 @@ export class SolanaRegisterDomainTool extends Tool { const tx = await this.solanaKit.registerDomain( parsedInput.name, - parsedInput.spaceKB || 1, + parsedInput.spaceKB || 1 ); return JSON.stringify({ @@ -465,7 +463,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool { telegram: parsedInput.telegram, website: parsedInput.website, initialLiquiditySOL: parsedInput.initialLiquiditySOL, - }, + } ); return JSON.stringify({ @@ -521,8 +519,10 @@ export class SolanaCreateImageTool extends Tool { export class SolanaLendAssetTool extends Tool { name = "solana_lend_asset"; - description = - "Lend idle assets for yield using Lulo. Input should be JSON with: {amount: number}"; + description = `Lend idle USDC for yield using Lulo. ( only USDC is supported ) + + Inputs (input is a json string): + amount: number, eg 1, 0.01 (required)`; constructor(private solanaKit: SolanaAgentKit) { super(); @@ -530,36 +530,22 @@ export class SolanaLendAssetTool extends Tool { async _call(input: string): Promise { try { - const { amount } = JSON.parse(input); + let amount = JSON.parse(input).amount || input; const tx = await this.solanaKit.lendAssets(amount); - return `Asset lent successfully. Transaction: ${tx}`; + return JSON.stringify({ + status: "success", + message: "Asset lent successfully", + transaction: tx, + amount: amount, + }); } catch (error: any) { - return `Error lending asset: ${error.message}`; - } - } -} - -export class SolanaFetchLendingDetailsTool extends Tool { - name = "solana_get_lending_details"; - description = - "Get details of assets lent on Lulo. Input should be JSON with: {luloApiKey: string}"; - - constructor(private solanaKit: SolanaAgentKit) { - super(); - } - - async _call(input: string): Promise { - try { - const { luloApiKey } = JSON.parse(input); - - const lendingDetails = - await this.solanaKit.fetchLendingDetails(luloApiKey); - - return `Lending details: ${lendingDetails}`; - } catch (error: any) { - return `Error fetching lending details: ${error.message}`; + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); } } } @@ -596,7 +582,6 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaPumpfunTokenLaunchTool(solanaKit), new SolanaCreateImageTool(solanaKit), new SolanaLendAssetTool(solanaKit), - new SolanaFetchLendingDetailsTool(solanaKit), new SolanaTPSCalculatorTool(solanaKit), ]; } diff --git a/src/tools/lend.ts b/src/tools/lend.ts index d31283d..7889343 100644 --- a/src/tools/lend.ts +++ b/src/tools/lend.ts @@ -10,61 +10,51 @@ import { SolanaAgentKit } from "../agent"; */ export async function lendAsset( agent: SolanaAgentKit, - amount: number, + amount: number ): Promise { try { const response = await fetch( `https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`, { method: "POST", + headers: { + "Content-Type": "application/json", + }, body: JSON.stringify({ account: agent.wallet.publicKey.toBase58(), }), - }, + } ); const data = await response.json(); + // Deserialize the transaction const luloTxn = VersionedTransaction.deserialize( - Buffer.from(data.transaction, "base64"), + 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); + + 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}`); } } - -/** - * Fetch lending details for agent - * @param agent SolanaAgentKit instance - * @param LULO_API_KEY Valid API key for Lulo - * @returns Lending account details - */ -export async function getLendingDetails( - agent: SolanaAgentKit, - LULO_API_KEY = "", -): Promise { - try { - if (!LULO_API_KEY) { - throw new Error("Missing Lulo API key"); - } - - const response = await fetch(`${LULO_API}/account`, { - headers: { - "x-wallet-pubkey": agent.wallet.publicKey.toBase58(), - "x-api-key": LULO_API_KEY, - }, - }); - - const { data } = await response.json(); - - return data as LuloAccountDetailsResponse; - } catch (error: any) { - throw new Error(`Failed to fetch lending details: ${error.message}`); - } -}