mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-13 15:10:04 +00:00
feat: drift vault info action
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
"no-constant-condition": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-empty-object-type": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
||||
"no-console": ["warn", { "allow": ["warn", "error"] }],
|
||||
"curly": ["error", "all"],
|
||||
@@ -30,4 +31,4 @@
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ const createDriftVaultAction: Action = {
|
||||
// regex matches SOL-SPOT
|
||||
marketName: z
|
||||
.string()
|
||||
.regex(/^([A-Za-z0-9]{2,7})-SPOT$/)
|
||||
.describe('Market name must be in the format "TOKEN-SPOT"'),
|
||||
redeemPeriod: z
|
||||
.number()
|
||||
|
||||
78
src/actions/drift/vaultInfo.ts
Normal file
78
src/actions/drift/vaultInfo.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { z } from "zod";
|
||||
import type { Action } from "../../types";
|
||||
import { getVaultInfo } from "../../tools";
|
||||
import type { SolanaAgentKit } from "../../agent";
|
||||
import { decodeName } from "@drift-labs/vaults-sdk";
|
||||
import { MainnetSpotMarkets, PERCENTAGE_PRECISION } from "@drift-labs/sdk";
|
||||
|
||||
const vaultInfoAction: Action = {
|
||||
name: "DRIFT_VAULT_INFO",
|
||||
similes: ["get drift vault info", "vault info", "vault information"],
|
||||
description: "Get information about a drift vault",
|
||||
examples: [
|
||||
[
|
||||
{
|
||||
input: {
|
||||
vaultAddress: "2nFeP7taii",
|
||||
},
|
||||
output: {
|
||||
status: "success",
|
||||
message: "Vault info retrieved successfully",
|
||||
data: {
|
||||
name: "My Drift Vault",
|
||||
marketName: "SOL-SPOT",
|
||||
redeemPeriod: 30,
|
||||
maxTokens: 1000,
|
||||
minDepositAmount: 100,
|
||||
managementFee: 10,
|
||||
profitShare: 5,
|
||||
hurdleRate: 0.1,
|
||||
permissioned: false,
|
||||
},
|
||||
},
|
||||
explanation: "Get information about a drift vault",
|
||||
},
|
||||
],
|
||||
],
|
||||
schema: z.object({
|
||||
vaultAddress: z.string(),
|
||||
}),
|
||||
handler: async (agent: SolanaAgentKit, input) => {
|
||||
try {
|
||||
const vaultInfo = await getVaultInfo(agent, input.vaultAddress as string);
|
||||
const spotToken = MainnetSpotMarkets[vaultInfo.spotMarketIndex];
|
||||
const data = {
|
||||
name: decodeName(vaultInfo.name),
|
||||
marketName: `${spotToken.symbol}-SPOT`,
|
||||
redeemPeriod: vaultInfo.redeemPeriod.toNumber(),
|
||||
maxTokens: vaultInfo.maxTokens.div(spotToken.precision).toNumber(),
|
||||
minDepositAmount: vaultInfo.minDepositAmount
|
||||
.div(spotToken.precision)
|
||||
.toNumber(),
|
||||
managementFee:
|
||||
(vaultInfo.managementFee.toNumber() /
|
||||
PERCENTAGE_PRECISION.toNumber()) *
|
||||
100,
|
||||
profitShare:
|
||||
(vaultInfo.profitShare / PERCENTAGE_PRECISION.toNumber()) * 100,
|
||||
hurdleRate:
|
||||
(vaultInfo.hurdleRate / PERCENTAGE_PRECISION.toNumber()) * 100,
|
||||
permissioned: vaultInfo.permissioned,
|
||||
};
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
message: "Vault info retrieved successfully",
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
status: "error",
|
||||
// @ts-expect-error - error message
|
||||
message: `Failed to retrieve vault info: ${e.message}`,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default vaultInfoAction;
|
||||
@@ -36,6 +36,7 @@ import depositIntoDriftVaultAction from "./drift/depositIntoVault";
|
||||
import requestWithdrawalFromVaultAction from "./drift/requestWithdrawalFromVault";
|
||||
import withdrawFromVaultAction from "./drift/withdrawFromVault";
|
||||
import tradeDelegatedDriftVaultAction from "./drift/tradeDelegatedDriftVault";
|
||||
import vaultInfoAction from "./drift/vaultInfo";
|
||||
|
||||
export const ACTIONS = {
|
||||
WALLET_ADDRESS_ACTION: getWalletAddressAction,
|
||||
@@ -77,6 +78,7 @@ export const ACTIONS = {
|
||||
REQUEST_WITHDRAWAL_FROM_DRIFT_VAULT_ACTION: requestWithdrawalFromVaultAction,
|
||||
WITHDRAW_FROM_DRIFT_VAULT_ACTION: withdrawFromVaultAction,
|
||||
TRADE_DELEGATED_DRIFT_VAULT_ACTION: tradeDelegatedDriftVaultAction,
|
||||
DRIFT_VAULT_INFO_ACTION: vaultInfoAction,
|
||||
};
|
||||
|
||||
export type { Action, ActionExample, Handler } from "../types/action";
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
getLimitOrderParams,
|
||||
getMarketOrderParams,
|
||||
getOrderParams,
|
||||
MainnetPerpMarkets,
|
||||
MainnetSpotMarkets,
|
||||
MarketType,
|
||||
numberToSafeBN,
|
||||
PERCENTAGE_PRECISION,
|
||||
@@ -33,7 +35,25 @@ import {
|
||||
import type { SolanaAgentKit } from "../agent";
|
||||
import { BN } from "bn.js";
|
||||
|
||||
function initClients(agent: SolanaAgentKit) {
|
||||
export function getMarketIndexAndType(name: `${string}-${string}`) {
|
||||
const [symbol, type] = name.toUpperCase().split("-");
|
||||
|
||||
if (type === "PERP") {
|
||||
const token = MainnetPerpMarkets.find((v) => v.symbol === symbol);
|
||||
if (!token) {
|
||||
throw new Error("Drift doesn't have that market");
|
||||
}
|
||||
return { marketIndex: token.marketIndex, marketType: MarketType.PERP };
|
||||
}
|
||||
|
||||
const token = MainnetSpotMarkets.find((v) => v.symbol === symbol);
|
||||
if (!token) {
|
||||
throw new Error("Drift doesn't have that market");
|
||||
}
|
||||
return { marketIndex: token.marketIndex, marketType: MarketType.SPOT };
|
||||
}
|
||||
|
||||
async function initClients(agent: SolanaAgentKit) {
|
||||
const wallet: IWallet = {
|
||||
publicKey: agent.wallet.publicKey,
|
||||
payer: agent.wallet,
|
||||
@@ -76,12 +96,17 @@ function initClients(agent: SolanaAgentKit) {
|
||||
program: vaultProgram,
|
||||
cliMode: false,
|
||||
});
|
||||
await driftClient.subscribe();
|
||||
|
||||
return { driftClient, vaultClient };
|
||||
async function cleanUp() {
|
||||
await driftClient.unsubscribe();
|
||||
}
|
||||
|
||||
return { driftClient, vaultClient, cleanUp };
|
||||
}
|
||||
|
||||
async function getOrCreateVaultDepositor(agent: SolanaAgentKit, vault: string) {
|
||||
const { vaultClient } = initClients(agent);
|
||||
const { vaultClient, cleanUp } = await initClients(agent);
|
||||
const vaultPublicKey = new PublicKey(vault);
|
||||
const vaultDepositor = getVaultDepositorAddressSync(
|
||||
vaultClient.program.programId,
|
||||
@@ -91,12 +116,14 @@ async function getOrCreateVaultDepositor(agent: SolanaAgentKit, vault: string) {
|
||||
|
||||
try {
|
||||
await vaultClient.getVaultDepositor(vaultDepositor);
|
||||
await cleanUp();
|
||||
return vaultDepositor;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
if (e.message === "Account not found") {
|
||||
await vaultClient.initializeVaultDepositor(vaultDepositor);
|
||||
}
|
||||
await cleanUp();
|
||||
return vaultDepositor;
|
||||
}
|
||||
}
|
||||
@@ -131,10 +158,8 @@ export async function createVault(
|
||||
},
|
||||
) {
|
||||
try {
|
||||
const { vaultClient, driftClient } = initClients(agent);
|
||||
const marketIndexAndType = driftClient.getMarketIndexAndType(
|
||||
params.marketName,
|
||||
);
|
||||
const { vaultClient, driftClient, cleanUp } = await initClients(agent);
|
||||
const marketIndexAndType = getMarketIndexAndType(params.marketName);
|
||||
|
||||
if (!marketIndexAndType) {
|
||||
throw new Error("Invalid market name");
|
||||
@@ -174,6 +199,8 @@ export async function createVault(
|
||||
permissioned: params.permissioned ?? false,
|
||||
});
|
||||
|
||||
await cleanUp();
|
||||
|
||||
return tx;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
@@ -209,7 +236,7 @@ export async function updateVault(
|
||||
},
|
||||
) {
|
||||
try {
|
||||
const { vaultClient } = initClients(agent);
|
||||
const { vaultClient, cleanUp } = await initClients(agent);
|
||||
const vaultPublicKey = new PublicKey(vault);
|
||||
const vaultDetails = await vaultClient.getVault(vaultPublicKey);
|
||||
|
||||
@@ -233,6 +260,8 @@ export async function updateVault(
|
||||
permissioned: params.permissioned ?? vaultDetails.permissioned,
|
||||
});
|
||||
|
||||
await cleanUp();
|
||||
|
||||
return tx;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
@@ -240,6 +269,24 @@ export async function updateVault(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getVaultInfo(
|
||||
agent: SolanaAgentKit,
|
||||
vaultAddress: string,
|
||||
) {
|
||||
try {
|
||||
const { vaultClient, cleanUp } = await initClients(agent);
|
||||
const vaultPublicKey = new PublicKey(vaultAddress);
|
||||
const vaultDetails = await vaultClient.getVault(vaultPublicKey);
|
||||
|
||||
await cleanUp();
|
||||
|
||||
return vaultDetails;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
throw new Error(`Failed to get vault info: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Deposit tokens into a vault
|
||||
@param agent SolanaAgentKit instance
|
||||
@@ -252,8 +299,9 @@ export async function depositIntoVault(
|
||||
amount: number,
|
||||
vault: string,
|
||||
) {
|
||||
const { vaultClient, driftClient, cleanUp } = await initClients(agent);
|
||||
|
||||
try {
|
||||
const { vaultClient, driftClient } = initClients(agent);
|
||||
const vaultPublicKey = new PublicKey(vault);
|
||||
const [isOwned, vaultDetails] = await Promise.all([
|
||||
getIsOwned(agent, vault),
|
||||
@@ -275,8 +323,11 @@ export async function depositIntoVault(
|
||||
}
|
||||
|
||||
const vaultDepositor = await getOrCreateVaultDepositor(agent, vault);
|
||||
const tx = await vaultClient.deposit(vaultDepositor, amountBN);
|
||||
|
||||
return await vaultClient.deposit(vaultDepositor, amountBN);
|
||||
await cleanUp();
|
||||
|
||||
return tx;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
throw new Error(`Failed to deposit into Drift vault: ${e.message}`);
|
||||
@@ -295,7 +346,7 @@ export async function requestWithdrawalFromVault(
|
||||
vault: string,
|
||||
) {
|
||||
try {
|
||||
const { vaultClient } = initClients(agent);
|
||||
const { vaultClient, cleanUp } = await initClients(agent);
|
||||
const vaultPublicKey = new PublicKey(vault);
|
||||
const isOwned = await getIsOwned(agent, vault);
|
||||
|
||||
@@ -315,6 +366,8 @@ export async function requestWithdrawalFromVault(
|
||||
WithdrawUnit.SHARES,
|
||||
);
|
||||
|
||||
await cleanUp();
|
||||
|
||||
return tx;
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
@@ -335,7 +388,7 @@ export async function withdrawFromDriftVault(
|
||||
vault: string,
|
||||
) {
|
||||
try {
|
||||
const { vaultClient } = initClients(agent);
|
||||
const { vaultClient, cleanUp } = await initClients(agent);
|
||||
const vaultPublicKey = new PublicKey(vault);
|
||||
const isOwned = await getIsOwned(agent, vault);
|
||||
|
||||
@@ -347,6 +400,8 @@ export async function withdrawFromDriftVault(
|
||||
|
||||
const tx = await vaultClient.withdraw(vaultDepositor);
|
||||
|
||||
await cleanUp();
|
||||
|
||||
return tx;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
@@ -362,11 +417,14 @@ export async function withdrawFromDriftVault(
|
||||
*/
|
||||
async function getIsOwned(agent: SolanaAgentKit, vault: string) {
|
||||
try {
|
||||
const { vaultClient } = initClients(agent);
|
||||
const { vaultClient, cleanUp } = await initClients(agent);
|
||||
const vaultPublicKey = new PublicKey(vault);
|
||||
const vaultDetails = await vaultClient.getVault(vaultPublicKey);
|
||||
const isOwned = vaultDetails.delegate.equals(agent.wallet.publicKey);
|
||||
|
||||
return vaultDetails.delegate.equals(agent.wallet.publicKey);
|
||||
await cleanUp();
|
||||
|
||||
return isOwned;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
throw new Error(`Failed to check if vault is owned: ${e.message}`);
|
||||
@@ -392,7 +450,7 @@ export async function tradeDriftVault(
|
||||
price?: number,
|
||||
) {
|
||||
try {
|
||||
const { driftClient, vaultClient } = initClients(agent);
|
||||
const { driftClient, vaultClient, cleanUp } = await initClients(agent);
|
||||
const [isOwned, vaultDetails, driftLookupTableAccount] = await Promise.all([
|
||||
getIsOwned(agent, vault),
|
||||
vaultClient.getVault(new PublicKey(vault)),
|
||||
@@ -433,7 +491,7 @@ export async function tradeDriftVault(
|
||||
);
|
||||
}
|
||||
|
||||
const perpMarketIndexAndType = driftClient.getMarketIndexAndType(
|
||||
const perpMarketIndexAndType = getMarketIndexAndType(
|
||||
`${symbol.toUpperCase()}-PERP`,
|
||||
);
|
||||
|
||||
@@ -506,6 +564,8 @@ export async function tradeDriftVault(
|
||||
),
|
||||
);
|
||||
|
||||
await cleanUp();
|
||||
|
||||
return tx;
|
||||
} catch (e) {
|
||||
// @ts-expect-error - error message is a string
|
||||
|
||||
Reference in New Issue
Block a user