Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deposit and withdraw with lulo #117

Merged
merged 2 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ SOLANA_PRIVATE_KEY=
JUPITER_REFERRAL_ACCOUNT=
JUPITER_FEE_BPS=
FLASH_PRIVILEGE= referral | nft | none
HELIUS_API_KEY=
FLEXLEND_API_KEY=
HELIUS_API_KEY=
4 changes: 4 additions & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,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";
Expand Down Expand Up @@ -81,6 +83,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,
Expand Down
62 changes: 62 additions & 0 deletions src/actions/lulo/luloLend.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>) => {
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;
62 changes: 62 additions & 0 deletions src/actions/lulo/luloWithdraw.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>) => {
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;
10 changes: 10 additions & 0 deletions src/agent/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
getPrimaryDomain,
launchPumpFunToken,
lendAsset,
luloLend,
luloWithdraw,
mintCollectionNFT,
openbookCreateMarket,
manifestCreateMarket,
Expand Down Expand Up @@ -314,6 +316,14 @@ export class SolanaAgentKit {
return lendAsset(this, amount);
}

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

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

async getTPS(): Promise<number> {
return getTPS(this);
}
Expand Down
4 changes: 4 additions & 0 deletions src/langchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import {
SolanaPumpfunTokenLaunchTool,
SolanaCreateImageTool,
SolanaLendAssetTool,
SolanaLuloLendTool,
SolanaLuloWithdrawTool,
SolanaTPSCalculatorTool,
SolanaStakeTool,
SolanaRestakeTool,
Expand Down Expand Up @@ -135,6 +137,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),
Expand Down
2 changes: 2 additions & 0 deletions src/langchain/lulo/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from "./lend_asset";
export * from "./lulo_lend";
export * from "./lulo_withdraw";
37 changes: 37 additions & 0 deletions src/langchain/lulo/lulo_lend.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
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",
});
}
}
}
37 changes: 37 additions & 0 deletions src/langchain/lulo/lulo_withdraw.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
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",
});
}
}
}
2 changes: 2 additions & 0 deletions src/tools/lulo/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from "./lend";
export * from "./lulo_lend";
export * from "./lulo_withdraw";
64 changes: 64 additions & 0 deletions src/tools/lulo/lulo_lend.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
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}`);
}
}
Loading
Loading