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 10, 2025
1 parent fc92d44 commit e9466ed
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 9 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ RPC_URL=
SOLANA_PRIVATE_KEY=
JUPITER_REFERRAL_ACCOUNT=
JUPITER_FEE_BPS=
FLASH_PRIVILEGE= referral | nft | none
FLASH_PRIVILEGE= referral | nft | none
FLEXLEND_API_KEY=
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:

Expand Down
4 changes: 0 additions & 4 deletions docs/interfaces/JupiterTokenData.html

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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,
Expand Down
54 changes: 54 additions & 0 deletions src/actions/luloLend.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 { 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<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;
54 changes: 54 additions & 0 deletions src/actions/luloWithdraw.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 { 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<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 @@ -14,6 +14,8 @@ import {
getPrimaryDomain,
launchPumpFunToken,
lendAsset,
luloLend,
luloWithdraw,
mintCollectionNFT,
openbookCreateMarket,
manifestCreateMarket,
Expand Down Expand Up @@ -275,6 +277,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
74 changes: 74 additions & 0 deletions src/langchain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
try {
const parsedInput = JSON.parse(input);
const mintAddress = parsedInput.mintAddress
const amount = parsedInput.amount;

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

Check failure on line 1075 in src/langchain/index.ts

View workflow job for this annotation

GitHub Actions / check

Expected 1 arguments, but got 2.

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<string> {
try {
const parsedInput = JSON.parse(input);
const mintAddress = parsedInput.mintAddress
const amount = parsedInput.amount;

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

Check failure on line 1111 in src/langchain/index.ts

View workflow job for this annotation

GitHub Actions / check

Property 'withdrawAssets' does not exist on type 'SolanaAgentKit'.

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 @@ -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),
Expand Down
4 changes: 3 additions & 1 deletion src/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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";
2 changes: 1 addition & 1 deletion src/tools/lend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ export async function lendAsset(
} catch (error: any) {
throw new Error(`Lending failed: ${error.message}`);
}
}
}
64 changes: 64 additions & 0 deletions src/tools/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

0 comments on commit e9466ed

Please sign in to comment.