diff --git a/README.md b/README.md index 4d36f4a..e1d0bab 100644 --- a/README.md +++ b/README.md @@ -466,6 +466,38 @@ Update the address a drift vault is delegated to. const signature = await agent.updateDriftVaultDelegate("41Y8C4oxk4zgJT1KXyQr35UhZcfsp5mP86Z2G7UUzojU", "new-address") ``` +### Get Voltr Vault Position Values + +Get the current position values and total value of assets in a Voltr vault. + +```typescript +const values = await agent.voltrGetPositionValues("7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K") +``` + +### Deposit into Voltr Strategy + +Deposit assets into a specific strategy within a Voltr vault. + +```typescript +const signature = await agent.voltrDepositStrategy( + new BN("1000000000"), // amount in base units (e.g., 1 USDC = 1000000) + "7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K", // vault + "9ZQQYvr4x7AMqd6abVa1f5duGjti5wk1MHsX6hogPsLk" // strategy +) +``` + +### Withdraw from Voltr Strategy + +Withdraw assets from a specific strategy within a Voltr vault. + +```typescript +const signature = await agent.voltrWithdrawStrategy( + new BN("1000000000"), // amount in base units (e.g., 1 USDC = 1000000) + "7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K", // vault + "9ZQQYvr4x7AMqd6abVa1f5duGjti5wk1MHsX6hogPsLk" // strategy +) +``` + ## Examples ### LangGraph Multi-Agent System diff --git a/package.json b/package.json index d6966fd..26e2604 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@sqds/multisig": "^2.1.3", "@tensor-oss/tensorswap-sdk": "^4.5.0", "@tiplink/api": "^0.3.1", + "@voltr/vault-sdk": "^0.1.1", "ai": "^4.0.22", "bn.js": "^5.2.1", "bs58": "^6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea5bf1e..2b64324 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ importers: '@tiplink/api': specifier: ^0.3.1 version: 0.3.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(sodium-native@3.4.1)(utf-8-validate@5.0.10) + '@voltr/vault-sdk': + specifier: ^0.1.1 + version: 0.1.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) ai: specifier: ^4.0.22 version: 4.0.22(react@19.0.0)(zod@3.24.1) @@ -1606,6 +1609,9 @@ packages: '@ungap/structured-clone@1.2.1': resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} + '@voltr/vault-sdk@0.1.1': + resolution: {integrity: sha512-xh8bxPCwNpjVqEN32+Q40Xf08vdORAmQACDE6ru3T+9qrwNgraLcPEzvWeBNTE0FAMAZdNsYOxWbc2FSK0ZQww==} + '@wallet-standard/base@1.1.0': resolution: {integrity: sha512-DJDQhjKmSNVLKWItoKThJS+CsJQjR9AOBOirBVT1F9YpRyC9oYHE+ZnSf8y8bxUphtKqdQMPVQ2mHohYdRvDVQ==} engines: {node: '>=16'} @@ -5624,7 +5630,7 @@ snapshots: '@metaplex-foundation/beet-solana@0.4.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: - '@metaplex-foundation/beet': 0.7.1 + '@metaplex-foundation/beet': 0.7.2 '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) bs58: 5.0.0 debug: 4.4.0 @@ -5834,8 +5840,8 @@ snapshots: '@metaplex-foundation/mpl-token-metadata@2.13.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': dependencies: - '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/beet-solana': 0.4.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet': 0.7.2 + '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@metaplex-foundation/cusper': 0.0.2 '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -5851,8 +5857,8 @@ snapshots: '@metaplex-foundation/mpl-token-metadata@2.13.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': dependencies: - '@metaplex-foundation/beet': 0.7.1 - '@metaplex-foundation/beet-solana': 0.4.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@metaplex-foundation/beet': 0.7.2 + '@metaplex-foundation/beet-solana': 0.4.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@metaplex-foundation/cusper': 0.0.2 '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -7535,6 +7541,18 @@ snapshots: '@ungap/structured-clone@1.2.1': {} + '@voltr/vault-sdk@0.1.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor': 0.30.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.4.9(@solana/web3.js@1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + '@wallet-standard/base@1.1.0': {} '@wallet-standard/features@1.1.0': diff --git a/src/actions/index.ts b/src/actions/index.ts index 29cf233..84774cb 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -59,6 +59,9 @@ import withdrawFromDriftAccountAction from "./drift/withdrawFromDriftAccount"; import driftUserAccountInfoAction from "./drift/driftUserAccountInfo"; import deriveDriftVaultAddressAction from "./drift/deriveVaultAddress"; import updateDriftVaultDelegateAction from "./drift/updateDriftVaultDelegate"; +import getVoltrPositionValuesAction from "./voltr/getPositionValues"; +import depositVoltrStrategyAction from "./voltr/depositStrategy"; +import withdrawVoltrStrategyAction from "./voltr/withdrawStrategy"; export const ACTIONS = { WALLET_ADDRESS_ACTION: getWalletAddressAction, @@ -123,6 +126,9 @@ export const ACTIONS = { DRIFT_USER_ACCOUNT_INFO_ACTION: driftUserAccountInfoAction, DERIVE_DRIFT_VAULT_ADDRESS_ACTION: deriveDriftVaultAddressAction, UPDATE_DRIFT_VAULT_DELEGATE_ACTION: updateDriftVaultDelegateAction, + GET_VOLTR_POSITION_VALUES_ACTION: getVoltrPositionValuesAction, + DEPOSIT_VOLTR_STRATEGY_ACTION: depositVoltrStrategyAction, + WITHDRAW_VOLTR_STRATEGY_ACTION: withdrawVoltrStrategyAction, }; export type { Action, ActionExample, Handler } from "../types/action"; diff --git a/src/actions/voltr/depositStrategy.ts b/src/actions/voltr/depositStrategy.ts new file mode 100644 index 0000000..d1305c9 --- /dev/null +++ b/src/actions/voltr/depositStrategy.ts @@ -0,0 +1,83 @@ +import { Action } from "../../types/action"; +import { SolanaAgentKit } from "../../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { BN } from "bn.js"; + +const depositVoltrStrategyAction: Action = { + name: "DEPOSIT_VOLTR_STRATEGY", + similes: [ + "deposit to voltr strategy", + "add funds to voltr vault strategy", + "invest in voltr strategy", + "deposit assets to voltr", + "contribute to voltr vault", + "fund voltr strategy", + ], + description: "Deposit assets into a specific strategy within a Voltr vault", + examples: [ + [ + { + input: { + depositAmount: "1000000000", // 1 USDC with 6 decimals + vault: "7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K", + strategy: "9ZQQYvr4x7AMqd6abVa1f5duGjti5wk1MHsX6hogPsLk", + }, + output: { + status: "success", + vault: "7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K", + strategy: "9ZQQYvr4x7AMqd6abVa1f5duGjti5wk1MHsX6hogPsLk", + signature: "2ZE7Rz...", + message: "Successfully deposited 1000000000 into strategy", + }, + explanation: "Deposit 1 USDC into a Voltr vault strategy", + }, + ], + ], + schema: z.object({ + depositAmount: z + .string() + .min(1) + .describe("The amount to deposit (in base units including decimals)"), + vault: z + .string() + .min(1) + .describe( + "The public key of the Voltr source vault to take assets from, e.g., 'Ga27...'", + ), + strategy: z + .string() + .min(1) + .describe( + "The public key of the initialized target strategy to deposit into, e.g., 'Jheh...'", + ), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const depositAmount = new BN(input.depositAmount); + const vault = new PublicKey(input.vault); + const strategy = new PublicKey(input.strategy); + + const signature = await agent.voltrDepositStrategy( + depositAmount, + vault, + strategy, + ); + + return { + status: "success", + vault: vault.toBase58(), + strategy: strategy.toBase58(), + signature, + message: `Successfully deposited ${input.depositAmount} into strategy`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to deposit into strategy: ${error.message}`, + }; + } + }, +}; + +export default depositVoltrStrategyAction; diff --git a/src/actions/voltr/getPositionValues.ts b/src/actions/voltr/getPositionValues.ts new file mode 100644 index 0000000..a98424e --- /dev/null +++ b/src/actions/voltr/getPositionValues.ts @@ -0,0 +1,74 @@ +import { Action } from "../../types/action"; +import { SolanaAgentKit } from "../../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; + +const getVoltrPositionValuesAction: Action = { + name: "GET_VOLTR_POSITION_VALUES", + similes: [ + "get voltr vault value", + "check voltr position", + "get voltr vault assets", + "view voltr holdings", + "check voltr portfolio", + "get voltr vault breakdown", + ], + description: + "Get the current position values and total assets for a Voltr vault", + examples: [ + [ + { + input: { + vault: "7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K", + }, + output: { + status: "success", + data: { + totalValue: 1000000, + positions: [ + { + strategy: "4JHtgXyMb9gFJ1hGd2sh645jrZcxurSG3QP7Le3aTMTx", + value: 600000, + }, + { + strategy: "4i9kzGr1UkxBCCUkQUQ4vsF51fjdt2knKxrwM1h1NW4g", + value: 400000, + }, + ], + }, + message: "Successfully retrieved Voltr vault position values", + }, + explanation: + "Get position values for a Voltr vault showing total value and value per strategy", + }, + ], + ], + schema: z.object({ + vault: z + .string() + .min(1) + .describe("The public key of the Voltr vault to query, e.g., 'Ga27...'"), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const vault = new PublicKey(input.vault); + + const result = await agent.voltrGetPositionValues(vault); + const positionData = JSON.parse(result); + + return { + status: "success", + vault: vault.toBase58(), + data: positionData, + message: "Successfully retrieved Voltr vault position values", + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to get vault position values: ${error.message}`, + }; + } + }, +}; + +export default getVoltrPositionValuesAction; diff --git a/src/actions/voltr/withdrawStrategy.ts b/src/actions/voltr/withdrawStrategy.ts new file mode 100644 index 0000000..784207e --- /dev/null +++ b/src/actions/voltr/withdrawStrategy.ts @@ -0,0 +1,83 @@ +import { Action } from "../../types/action"; +import { SolanaAgentKit } from "../../agent"; +import { z } from "zod"; +import { PublicKey } from "@solana/web3.js"; +import { BN } from "bn.js"; + +const withdrawVoltrStrategyAction: Action = { + name: "WITHDRAW_VOLTR_STRATEGY", + similes: [ + "withdraw from voltr strategy", + "remove funds from voltr vault strategy", + "take out from voltr strategy", + "withdraw assets from voltr", + "pull from voltr vault", + "redeem from voltr strategy", + ], + description: "Withdraw assets from a specific strategy within a Voltr vault", + examples: [ + [ + { + input: { + withdrawAmount: "1000000000", // 1 USDC with 6 decimals + vault: "7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K", + strategy: "9ZQQYvr4x7AMqd6abVa1f5duGjti5wk1MHsX6hogPsLk", + }, + output: { + status: "success", + vault: "7opUkqYtxmQRriZvwZkPcg6LqmGjAh1RSEsVrdsGDx5K", + strategy: "9ZQQYvr4x7AMqd6abVa1f5duGjti5wk1MHsX6hogPsLk", + signature: "2ZE7Rz...", + message: "Successfully withdrew 1000000000 from strategy", + }, + explanation: "Withdraw 1 USDC from a Voltr vault strategy", + }, + ], + ], + schema: z.object({ + withdrawAmount: z + .string() + .min(1) + .describe("The amount to withdraw (in base units including decimals)"), + vault: z + .string() + .min(1) + .describe( + "The public key of the Voltr source vault to deposit assets into, e.g., 'Ga27...'", + ), + strategy: z + .string() + .min(1) + .describe( + "The public key of the initialized target strategy to withdraw from, e.g., 'Jheh...'", + ), + }), + handler: async (agent: SolanaAgentKit, input: Record) => { + try { + const withdrawAmount = new BN(input.withdrawAmount); + const vault = new PublicKey(input.vault); + const strategy = new PublicKey(input.strategy); + + const signature = await agent.voltrWithdrawStrategy( + withdrawAmount, + vault, + strategy, + ); + + return { + status: "success", + vault: vault.toBase58(), + strategy: strategy.toBase58(), + signature, + message: `Successfully withdrew ${input.withdrawAmount} from strategy`, + }; + } catch (error: any) { + return { + status: "error", + message: `Failed to withdraw from strategy: ${error.message}`, + }; + } + }, +}; + +export default withdrawVoltrStrategyAction; diff --git a/src/agent/index.ts b/src/agent/index.ts index 7cc32fa..71a86aa 100644 --- a/src/agent/index.ts +++ b/src/agent/index.ts @@ -98,6 +98,9 @@ import { withdrawFromDriftVault, updateVaultDelegate, get_token_balance, + voltrGetPositionValues, + voltrDepositStrategy, + voltrWithdrawStrategy, } from "../tools"; import { Config, @@ -840,4 +843,24 @@ export class SolanaAgentKit { async updateDriftVaultDelegate(vaultAddress: string, delegate: string) { return await updateVaultDelegate(this, vaultAddress, delegate); } + + async voltrDepositStrategy( + depositAmount: BN, + vault: PublicKey, + strategy: PublicKey, + ): Promise { + return voltrDepositStrategy(this, depositAmount, vault, strategy); + } + + async voltrWithdrawStrategy( + withdrawAmount: BN, + vault: PublicKey, + strategy: PublicKey, + ): Promise { + return voltrWithdrawStrategy(this, withdrawAmount, vault, strategy); + } + + async voltrGetPositionValues(vault: PublicKey): Promise { + return voltrGetPositionValues(this, vault); + } } diff --git a/src/langchain/index.ts b/src/langchain/index.ts index 809c340..d77dc20 100644 --- a/src/langchain/index.ts +++ b/src/langchain/index.ts @@ -26,6 +26,7 @@ export * from "./lightprotocol"; export * from "./squads"; export * from "./helius"; export * from "./drift"; +export * from "./voltr"; import { SolanaAgentKit } from "../agent"; import { @@ -114,6 +115,9 @@ import { SolanaUpdateDriftVaultTool, SolanaWithdrawFromDriftAccountTool, SolanaWithdrawFromDriftVaultTool, + SolanaVoltrGetPositionValues, + SolanaVoltrDepositStrategy, + SolanaVoltrWithdrawStrategy, } from "./index"; export function createSolanaTools(solanaKit: SolanaAgentKit) { @@ -208,5 +212,8 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) { new SolanaDriftVaultInfoTool(solanaKit), new SolanaWithdrawFromDriftAccountTool(solanaKit), new SolanaWithdrawFromDriftVaultTool(solanaKit), + new SolanaVoltrGetPositionValues(solanaKit), + new SolanaVoltrDepositStrategy(solanaKit), + new SolanaVoltrWithdrawStrategy(solanaKit), ]; } diff --git a/src/langchain/voltr/deposit_strategy.ts b/src/langchain/voltr/deposit_strategy.ts new file mode 100644 index 0000000..7342391 --- /dev/null +++ b/src/langchain/voltr/deposit_strategy.ts @@ -0,0 +1,39 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; +import { PublicKey } from "@solana/web3.js"; +import { BN } from "bn.js"; + +export class SolanaVoltrDepositStrategy extends Tool { + name = "solana_voltr_deposit_strategy"; + description = `Deposit amount into a strategy for Voltr's vaults + + Inputs (input is a json string): + depositAmount: number (required) + vault: string (required) + strategy: string (required) + `; + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + async _call(input: string): Promise { + try { + const inputFormat = JSON.parse(input); + const tx = await this.solanaKit.voltrDepositStrategy( + new BN(inputFormat.depositAmount), + new PublicKey(inputFormat.vault), + new PublicKey(inputFormat.strategy), + ); + return JSON.stringify({ + status: "success", + message: `Deposited ${inputFormat.depositAmount} into strategy ${inputFormat.strategy} of vault ${inputFormat.vault} successfully`, + transaction: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} diff --git a/src/langchain/voltr/get_position_values.ts b/src/langchain/voltr/get_position_values.ts new file mode 100644 index 0000000..697614f --- /dev/null +++ b/src/langchain/voltr/get_position_values.ts @@ -0,0 +1,18 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; +import { PublicKey } from "@solana/web3.js"; + +export class SolanaVoltrGetPositionValues extends Tool { + name = "solana_voltr_get_position_values"; + description = `Get the total asset value and current value for each strategy of a given Voltr vault + + Inputs: + vault: string (required) + `; + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + async _call(input: string): Promise { + return this.solanaKit.voltrGetPositionValues(new PublicKey(input)); + } +} diff --git a/src/langchain/voltr/index.ts b/src/langchain/voltr/index.ts new file mode 100644 index 0000000..b1e2a6e --- /dev/null +++ b/src/langchain/voltr/index.ts @@ -0,0 +1,3 @@ +export * from "./deposit_strategy"; +export * from "./withdraw_strategy"; +export * from "./get_position_values"; diff --git a/src/langchain/voltr/withdraw_strategy.ts b/src/langchain/voltr/withdraw_strategy.ts new file mode 100644 index 0000000..b8545b7 --- /dev/null +++ b/src/langchain/voltr/withdraw_strategy.ts @@ -0,0 +1,39 @@ +import { Tool } from "langchain/tools"; +import { SolanaAgentKit } from "../../agent"; +import { PublicKey } from "@solana/web3.js"; +import { BN } from "bn.js"; + +export class SolanaVoltrWithdrawStrategy extends Tool { + name = "solana_voltr_withdraw_strategy"; + description = `Withdraw amount from a strategy for Voltr's vaults + + Inputs (input is a json string): + withdrawAmount: number (required) + vault: string (required) + strategy: string (required) + `; + constructor(private solanaKit: SolanaAgentKit) { + super(); + } + async _call(input: string): Promise { + try { + const inputFormat = JSON.parse(input); + const tx = await this.solanaKit.voltrWithdrawStrategy( + new BN(inputFormat.withdrawAmount), + new PublicKey(inputFormat.vault), + new PublicKey(inputFormat.strategy), + ); + return JSON.stringify({ + status: "success", + message: `Withdrew ${inputFormat.withdrawAmount} from strategy ${inputFormat.strategy} of vault ${inputFormat.vault} successfully`, + transaction: tx, + }); + } catch (error: any) { + return JSON.stringify({ + status: "error", + message: error.message, + code: error.code || "UNKNOWN_ERROR", + }); + } + } +} diff --git a/src/tools/index.ts b/src/tools/index.ts index e5646ad..b02c01c 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -25,3 +25,4 @@ export * from "./tiplink"; export * from "./lightprotocol"; export * from "./squads"; export * from "./helius"; +export * from "./voltr"; diff --git a/src/tools/voltr/index.ts b/src/tools/voltr/index.ts new file mode 100644 index 0000000..328d565 --- /dev/null +++ b/src/tools/voltr/index.ts @@ -0,0 +1,3 @@ +export * from "./voltr_deposit_strategy"; +export * from "./voltr_withdraw_strategy"; +export * from "./voltr_get_position_values"; diff --git a/src/tools/voltr/voltr_deposit_strategy.ts b/src/tools/voltr/voltr_deposit_strategy.ts new file mode 100644 index 0000000..4da97ee --- /dev/null +++ b/src/tools/voltr/voltr_deposit_strategy.ts @@ -0,0 +1,99 @@ +import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token"; +import { SolanaAgentKit } from "../../agent"; +import { + PublicKey, + sendAndConfirmTransaction, + Transaction, +} from "@solana/web3.js"; +import { VoltrClient } from "@voltr/vault-sdk"; +import BN from "bn.js"; + +/** + * Deposits assets into a Voltr strategy + * @param agent SolanaAgentKit instance + * @param depositAmount Amount to deposit in base units (BN) + * @param vault Public key of the target vault + * @param strategy Public key of the target strategy + * @returns Transaction signature for the deposit + */ +export async function voltrDepositStrategy( + agent: SolanaAgentKit, + depositAmount: BN, + vault: PublicKey, + strategy: PublicKey, +): Promise { + const vc = new VoltrClient(agent.connection, agent.wallet); + const vaultAccount = await vc.fetchVaultAccount(vault); + const vaultAssetMint = vaultAccount.asset.mint; + const assetTokenProgram = await agent.connection + .getAccountInfo(new PublicKey(vaultAssetMint)) + .then((account) => account?.owner); + + if ( + !assetTokenProgram || + !( + assetTokenProgram.equals(TOKEN_PROGRAM_ID) || + assetTokenProgram.equals(TOKEN_2022_PROGRAM_ID) + ) + ) { + throw new Error("Invalid asset token program"); + } + + const response = await fetch( + `https://voltr.xyz/api/remaining-accounts/deposit-strategy?vault=${vault.toBase58()}&strategy=${strategy.toBase58()}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }, + ); + + const data = (await response.json()).data as { + instructionDiscriminator: number[] | null; + additionalArgs: number[] | null; + remainingAccounts: + | { + pubkey: string; + isSigner: boolean; + isWritable: boolean; + }[] + | null; + }; + + const additionalArgs = data.additionalArgs + ? Buffer.from(data.additionalArgs) + : null; + const instructionDiscriminator = data.instructionDiscriminator + ? Buffer.from(data.instructionDiscriminator) + : null; + const remainingAccounts = + data.remainingAccounts?.map((account) => ({ + pubkey: new PublicKey(account.pubkey), + isSigner: account.isSigner, + isWritable: account.isWritable, + })) ?? []; + + const depositIx = await vc.createDepositStrategyIx( + { + depositAmount, + additionalArgs, + instructionDiscriminator, + }, + { + vault, + vaultAssetMint, + strategy: strategy, + assetTokenProgram, + remainingAccounts, + }, + ); + + const transaction = new Transaction(); + transaction.add(depositIx); + + const txSig = await sendAndConfirmTransaction(agent.connection, transaction, [ + agent.wallet, + ]); + return txSig; +} diff --git a/src/tools/voltr/voltr_get_position_values.ts b/src/tools/voltr/voltr_get_position_values.ts new file mode 100644 index 0000000..ca474da --- /dev/null +++ b/src/tools/voltr/voltr_get_position_values.ts @@ -0,0 +1,20 @@ +import { SolanaAgentKit } from "../../agent"; +import { PublicKey } from "@solana/web3.js"; +import { VoltrClient } from "@voltr/vault-sdk"; + +/** + * Gets the value of assets in a Voltr vault + * @param agent SolanaAgentKit instance + * @param vault Public key of the target vault + * @returns Position and total values for the vault + */ +export async function voltrGetPositionValues( + agent: SolanaAgentKit, + vault: PublicKey, +): Promise { + const vc = new VoltrClient(agent.connection, agent.wallet); + const positionAndTotalValues = + await vc.getPositionAndTotalValuesForVault(vault); + + return JSON.stringify(positionAndTotalValues); +} diff --git a/src/tools/voltr/voltr_withdraw_strategy.ts b/src/tools/voltr/voltr_withdraw_strategy.ts new file mode 100644 index 0000000..bcc4fc7 --- /dev/null +++ b/src/tools/voltr/voltr_withdraw_strategy.ts @@ -0,0 +1,99 @@ +import { SolanaAgentKit } from "../../agent"; +import { + PublicKey, + sendAndConfirmTransaction, + Transaction, +} from "@solana/web3.js"; +import BN from "bn.js"; +import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { VoltrClient } from "@voltr/vault-sdk"; + +/** + * Withdraws assets from a Voltr strategy + * @param agent SolanaAgentKit instance + * @param withdrawAmount Amount to withdraw in base units (BN) + * @param vault Public key of the target vault + * @param strategy Public key of the target strategy + * @returns Transaction signature for the deposit + */ +export async function voltrWithdrawStrategy( + agent: SolanaAgentKit, + withdrawAmount: BN, + vault: PublicKey, + strategy: PublicKey, +): Promise { + const vc = new VoltrClient(agent.connection, agent.wallet); + const vaultAccount = await vc.fetchVaultAccount(vault); + const vaultAssetMint = vaultAccount.asset.mint; + const assetTokenProgram = await agent.connection + .getAccountInfo(new PublicKey(vaultAssetMint)) + .then((account) => account?.owner); + + if ( + !assetTokenProgram || + !( + assetTokenProgram.equals(TOKEN_PROGRAM_ID) || + assetTokenProgram.equals(TOKEN_2022_PROGRAM_ID) + ) + ) { + throw new Error("Invalid asset token program"); + } + + const response = await fetch( + `https://voltr.xyz/api/remaining-accounts/deposit-strategy?vault=${vault.toBase58()}&strategy=${strategy.toBase58()}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }, + ); + + const data = (await response.json()).data as { + instructionDiscriminator: number[] | null; + additionalArgs: number[] | null; + remainingAccounts: + | { + pubkey: string; + isSigner: boolean; + isWritable: boolean; + }[] + | null; + }; + + const additionalArgs = data.additionalArgs + ? Buffer.from(data.additionalArgs) + : null; + const instructionDiscriminator = data.instructionDiscriminator + ? Buffer.from(data.instructionDiscriminator) + : null; + const remainingAccounts = + data.remainingAccounts?.map((account) => ({ + pubkey: new PublicKey(account.pubkey), + isSigner: account.isSigner, + isWritable: account.isWritable, + })) ?? []; + + const withdrawIx = await vc.createWithdrawStrategyIx( + { + withdrawAmount, + additionalArgs, + instructionDiscriminator, + }, + { + vault, + vaultAssetMint, + strategy, + assetTokenProgram, + remainingAccounts, + }, + ); + + const transaction = new Transaction(); + transaction.add(withdrawIx); + + const txSig = await sendAndConfirmTransaction(agent.connection, transaction, [ + agent.wallet, + ]); + return txSig; +}