Skip to content

Commit

Permalink
lend and withdraw asset with lulo, not only usdc
Browse files Browse the repository at this point in the history
  • Loading branch information
zhourunlai committed Jan 4, 2025
1 parent 06ad5ba commit cc00661
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ OPENAI_API_KEY=
RPC_URL=
SOLANA_PRIVATE_KEY=
JUPITER_REFERRAL_ACCOUNT=
JUPITER_FEE_BPS=
JUPITER_FEE_BPS=
FLEXLEND_API_KEY=
4 changes: 3 additions & 1 deletion src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import stakeWithJupAction from "./stakeWithJup";
import stakeWithSolayerAction from "./stakeWithSolayer";
import registerDomainAction from "./registerDomain";
import lendAssetAction from "./lendAsset";
import withdrawAssetAction from "./withdrawAsset";
import createGibworkTaskAction from "./createGibworkTask";
import resolveSolDomainAction from "./resolveSolDomain";
import pythFetchPriceAction from "./pythFetchPrice";
Expand Down Expand Up @@ -43,9 +44,10 @@ export const ACTIONS = {
GET_TPS_ACTION: getTPSAction,
FETCH_PRICE_ACTION: fetchPriceAction,
STAKE_WITH_JUP_ACTION: stakeWithJupAction,
STAKE_WITH_SOLAYER_ACTION: stakeWithSolayerAction,
STAKE_WITH_SOLAYER_ACTION : stakeWithSolayerAction,
REGISTER_DOMAIN_ACTION: registerDomainAction,
LEND_ASSET_ACTION: lendAssetAction,
WITHDRAW_ASSET_ACTION: withdrawAssetAction,
CREATE_GIBWORK_TASK_ACTION: createGibworkTaskAction,
RESOLVE_SOL_DOMAIN_ACTION: resolveSolDomainAction,
PYTH_FETCH_PRICE_ACTION: pythFetchPriceAction,
Expand Down
11 changes: 7 additions & 4 deletions src/actions/lendAsset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ const lendAssetAction: Action = {
"deposit usdc",
"lending",
],
description: "Lend USDC tokens to earn yield using Lulo protocol",
description: "Lend SPL tokens to earn yield using Lulo protocol",
examples: [
[
{
input: {
mintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
amount: 100,
},
output: {
Expand All @@ -30,18 +31,20 @@ const lendAssetAction: Action = {
],
],
schema: z.object({
amount: z.number().positive().describe("Amount of USDC to lend"),
mintAddress: z.string().describe("SPL Mint address"),
amount: z.number().positive().describe("Amount to lend"),
}),
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
try {
const mintAddress = input.mintAddress as string;
const amount = input.amount as number;

const response = await lendAsset(agent, amount);
const response = await lendAsset(agent, mintAddress, amount);

return {
status: "success",
signature: response,
message: `Successfully lent ${amount} USDC`,
message: `Successfully lent ${amount} of token ${mintAddress}`,
};
} catch (error: any) {
return {
Expand Down
54 changes: 54 additions & 0 deletions src/actions/withdrawAsset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Action } from "../types/action";
import { SolanaAgentKit } from "../agent";
import { z } from "zod";
import { withdrawAsset } from "../tools";

const withdrawAssetAction: 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<string, any>) => {
try {
const mintAddress = input.mintAddress as string;
const amount = input.amount as number;

const response = await withdrawAsset(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 withdrawAssetAction;
9 changes: 7 additions & 2 deletions src/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getPrimaryDomain,
launchPumpFunToken,
lendAsset,
withdrawAsset,
mintCollectionNFT,
openbookCreateMarket,
manifestCreateMarket,
Expand Down Expand Up @@ -250,8 +251,12 @@ export class SolanaAgentKit {
});
}

async lendAssets(amount: number): Promise<string> {
return lendAsset(this, amount);
async lendAssets(mintAddress: string, amount: number): Promise<string> {
return lendAsset(this, mintAddress, amount);
}

async withdrawAssets(mintAddress: string, amount: number): Promise<string> {
return withdrawAsset(this, mintAddress, amount);
}

async getTPS(): Promise<number> {
Expand Down
46 changes: 43 additions & 3 deletions src/langchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,9 +888,10 @@ export class SolanaCreateImageTool extends Tool {

export class SolanaLendAssetTool extends Tool {
name = "solana_lend_asset";
description = `Lend idle USDC for yield using Lulo. ( only USDC is supported )
description = `Lend token for yield 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) {
Expand All @@ -899,9 +900,11 @@ export class SolanaLendAssetTool extends Tool {

async _call(input: string): Promise<string> {
try {
const amount = JSON.parse(input).amount || input;
const parsedInput = JSON.parse(input);
const mintAddress = parsedInput.mintAddress
const amount = parsedInput.amount;

const tx = await this.solanaKit.lendAssets(amount);
const tx = await this.solanaKit.lendAssets(mintAddress, amount);

return JSON.stringify({
status: "success",
Expand All @@ -919,6 +922,42 @@ export class SolanaLendAssetTool extends Tool {
}
}

export class SolanaWithdrawAssetTool extends Tool {
name = "solana_withdraw_asset";
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<string> {
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";
Expand Down Expand Up @@ -2136,6 +2175,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaPumpfunTokenLaunchTool(solanaKit),
new SolanaCreateImageTool(solanaKit),
new SolanaLendAssetTool(solanaKit),
new SolanaWithdrawAssetTool(solanaKit),
new SolanaTPSCalculatorTool(solanaKit),
new SolanaStakeTool(solanaKit),
new SolanaRestakeTool(solanaKit),
Expand Down
2 changes: 2 additions & 0 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ export * from "./resolve_sol_domain";
export * from "./get_primary_domain";
export * from "./launch_pumpfun_token";
export * from "./lend";
export * from "./withdraw";
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";
Expand Down
12 changes: 9 additions & 3 deletions src/tools/lend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,29 @@ import { SolanaAgentKit } from "../index";
/**
* Lend tokens for yields using Lulo
* @param agent SolanaAgentKit instance
* @param amount Amount of USDC to lend
* @param mintAddress SPL Mint address
* @param amount Amount to lend
* @returns Transaction signature
*/
export async function lendAsset(
agent: SolanaAgentKit,
mintAddress: string,
amount: number,
): Promise<string> {
try {
const response = await fetch(
`https://blink.lulo.fi/actions?amount=${amount}&symbol=USDC`,
`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({
account: agent.wallet.publicKey.toBase58(),
owner: agent.wallet.publicKey.toBase58(),
mintAddress: mintAddress,
depositAmount: amount,
}),
},
);
Expand Down
65 changes: 65 additions & 0 deletions src/tools/withdraw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
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 withdrawAsset(
agent: SolanaAgentKit,
mintAddress: string,
amount: number,
): Promise<string> {
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();

// 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(`Lending failed: ${error.message}`);
}
}

0 comments on commit cc00661

Please sign in to comment.