chore: merge Voltr changes into remote branch

This commit is contained in:
jakeyvee
2025-01-15 21:18:14 +08:00
402 changed files with 65213 additions and 8710 deletions

View File

@@ -0,0 +1,57 @@
import { createCollectionImp, createSingleImp } from "@3land/listings-sdk";
import {
StoreInitOptions,
CreateCollectionOptions,
CreateSingleOptions,
} from "@3land/listings-sdk/dist/types/implementation/implementationTypes";
/**
* Create a collection on 3Land
* @param optionsWithBase58 represents the privateKey of the wallet - can be an array of numbers, Uint8Array or base58 string
* @param collectionOpts represents the options for the collection creation
* @returns
*/
export async function createCollection(
optionsWithBase58: StoreInitOptions,
collectionOpts: CreateCollectionOptions,
) {
try {
const collection = await createCollectionImp(
optionsWithBase58,
collectionOpts,
);
return collection;
} catch (error: any) {
throw new Error(`Collection creation failed: ${error.message}`);
}
}
/**
* Create a single edition on 3Land
* @param optionsWithBase58 represents the privateKey of the wallet - can be an array of numbers, Uint8Array or base58 string
* @param collectionAccount represents the account for the nft collection
* @param createItemOptions the options for the creation of the single NFT listing
* @returns
*/
export async function createSingle(
optionsWithBase58: StoreInitOptions,
collectionAccount: string,
createItemOptions: CreateSingleOptions,
isMainnet: boolean,
) {
try {
const landStore = isMainnet
? "AmQNs2kgw4LvS9sm6yE9JJ4Hs3JpVu65eyx9pxMG2xA"
: "GyPCu89S63P9NcCQAtuSJesiefhhgpGWrNVJs4bF2cSK";
const singleEditionTx = await createSingleImp(
optionsWithBase58,
landStore,
collectionAccount,
createItemOptions,
);
return singleEditionTx;
} catch (error: any) {
throw new Error(`Single edition creation failed: ${error.message}`);
}
}

1
src/tools/3land/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from "./create_3land_collectible";

View File

@@ -0,0 +1,506 @@
import {
PublicKey,
SystemProgram,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../../index";
import { TOKENS, DEFAULT_OPTIONS } from "../../constants";
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { BN } from "@coral-xyz/anchor";
import AdrenaClient from "../../utils/AdrenaClient";
import { sendTx } from "../../utils/send_tx";
const PRICE_DECIMALS = 10;
const ADRENA_PROGRAM_ID = new PublicKey(
"13gDzEXCdocbj8iAiqrScGo47NiSuYENGsRqi3SEAwet",
);
// i.e percentage = -2 (for -2%)
// i.e percentage = 5 (for 5%)
function applySlippage(nb: BN, percentage: number): BN {
const negative = percentage < 0 ? true : false;
// Do x10_000 so percentage can be up to 4 decimals
const percentageBN = new BN(
(negative ? percentage * -1 : percentage) * 10_000,
);
const delta = nb.mul(percentageBN).divRound(new BN(10_000 * 100));
return negative ? nb.sub(delta) : nb.add(delta);
}
/**
* Close short trade on Adrena
* @returns Transaction signature
*/
export async function closePerpTradeShort({
agent,
price,
tradeMint,
}: {
agent: SolanaAgentKit;
price: number;
tradeMint: PublicKey;
}) {
const client = await AdrenaClient.load(agent);
const owner = agent.wallet.publicKey;
const custody = client.getCustodyByMint(tradeMint);
const collateralCustody = client.getCustodyByMint(TOKENS.USDC);
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
AdrenaClient.stakingRewardTokenMint,
);
const stakingRewardTokenCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(
AdrenaClient.stakingRewardTokenMint,
);
const position = AdrenaClient.findPositionAddress(
owner,
custody.pubkey,
"long",
);
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
const userProfile =
await client.program.account.userProfile.fetchNullable(userProfilePda);
const receivingAccount = AdrenaClient.findATAAddressSync(
owner,
collateralCustody.mint,
);
const preInstructions: TransactionInstruction[] = [];
const collateralCustodyOracle = collateralCustody.oracle;
const collateralCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(collateralCustody.mint);
if (
!(await AdrenaClient.isAccountInitialized(
agent.connection,
receivingAccount,
))
) {
preInstructions.push(
AdrenaClient.createATAInstruction({
ataAddress: receivingAccount,
mint: collateralCustody.mint,
owner,
}),
);
}
const instruction = await client.program.methods
.closePositionShort({
price: new BN(price * 10 ** PRICE_DECIMALS),
})
.accountsStrict({
owner,
receivingAccount,
transferAuthority: AdrenaClient.transferAuthority,
pool: AdrenaClient.mainPool,
position: position,
custody: custody.pubkey,
custodyTradeOracle: custody.tradeOracle,
tokenProgram: TOKEN_PROGRAM_ID,
lmStaking: AdrenaClient.lmStaking,
lpStaking: AdrenaClient.lpStaking,
cortex: AdrenaClient.cortex,
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
stakingRewardTokenCustodyTokenAccount,
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
lpTokenMint: AdrenaClient.lpTokenMint,
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
adrenaProgram: AdrenaClient.programId,
userProfile: userProfile ? userProfilePda : null,
caller: owner,
collateralCustody: collateralCustody.pubkey,
collateralCustodyOracle,
collateralCustodyTokenAccount,
})
.instruction();
return sendTx(agent, [...preInstructions, instruction]);
}
/**
* Close long trade on Adrena
* @returns Transaction signature
*/
export async function closePerpTradeLong({
agent,
price,
tradeMint,
}: {
agent: SolanaAgentKit;
price: number;
tradeMint: PublicKey;
}) {
const client = await AdrenaClient.load(agent);
const owner = agent.wallet.publicKey;
const custody = client.getCustodyByMint(tradeMint);
const custodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(tradeMint);
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
AdrenaClient.stakingRewardTokenMint,
);
const stakingRewardTokenCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(
AdrenaClient.stakingRewardTokenMint,
);
const position = AdrenaClient.findPositionAddress(
owner,
custody.pubkey,
"long",
);
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
const userProfile =
await client.program.account.userProfile.fetchNullable(userProfilePda);
const receivingAccount = AdrenaClient.findATAAddressSync(owner, custody.mint);
const preInstructions: TransactionInstruction[] = [];
if (
!(await AdrenaClient.isAccountInitialized(
agent.connection,
receivingAccount,
))
) {
preInstructions.push(
AdrenaClient.createATAInstruction({
ataAddress: receivingAccount,
mint: custody.mint,
owner,
}),
);
}
const instruction = await client.program.methods
.closePositionLong({
price: new BN(price * 10 ** PRICE_DECIMALS),
})
.accountsStrict({
owner,
receivingAccount,
transferAuthority: AdrenaClient.transferAuthority,
pool: AdrenaClient.mainPool,
position: position,
custody: custody.pubkey,
custodyTokenAccount,
custodyOracle: custody.oracle,
custodyTradeOracle: custody.tradeOracle,
tokenProgram: TOKEN_PROGRAM_ID,
lmStaking: AdrenaClient.lmStaking,
lpStaking: AdrenaClient.lpStaking,
cortex: AdrenaClient.cortex,
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
stakingRewardTokenCustodyTokenAccount,
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
lpTokenMint: AdrenaClient.lpTokenMint,
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
adrenaProgram: AdrenaClient.programId,
userProfile: userProfile ? userProfilePda : null,
caller: owner,
})
.instruction();
return sendTx(agent, [...preInstructions, instruction]);
}
/**
* Open long trade on Adrena
*
* Note: provide the same token as collateralMint and as tradeMint to avoid swap
* @returns Transaction signature
*/
export async function openPerpTradeLong({
agent,
price,
collateralAmount,
collateralMint = TOKENS.jitoSOL,
leverage = DEFAULT_OPTIONS.LEVERAGE_BPS,
tradeMint = TOKENS.jitoSOL,
slippage = 0.3,
}: {
agent: SolanaAgentKit;
price: number;
collateralAmount: number;
collateralMint?: PublicKey;
leverage?: number;
tradeMint?: PublicKey;
slippage?: number;
}): Promise<string> {
const client = await AdrenaClient.load(agent);
const owner = agent.wallet.publicKey;
const collateralAccount = AdrenaClient.findATAAddressSync(owner, tradeMint);
const fundingAccount = AdrenaClient.findATAAddressSync(owner, collateralMint);
const receivingCustody = AdrenaClient.findCustodyAddress(collateralMint);
const receivingCustodyOracle = client.getCustodyByMint(collateralMint).oracle;
const receivingCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(collateralMint);
// Principal custody is the custody of the targeted token
// i.e open a 1 ETH long position, principal custody is ETH
const principalCustody = AdrenaClient.findCustodyAddress(tradeMint);
const principalCustodyAccount = client.getCustodyByMint(tradeMint);
const principalCustodyOracle = principalCustodyAccount.oracle;
const principalCustodyTradeOracle = principalCustodyAccount.tradeOracle;
const principalCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(tradeMint);
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
AdrenaClient.stakingRewardTokenMint,
);
const stakingRewardTokenCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(
AdrenaClient.stakingRewardTokenMint,
);
const position = AdrenaClient.findPositionAddress(
owner,
principalCustody,
"long",
);
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
const userProfile =
await client.program.account.userProfile.fetchNullable(userProfilePda);
const priceWithSlippage = applySlippage(
new BN(price * 10 ** PRICE_DECIMALS),
slippage,
);
const scaledCollateralAmount = new BN(
collateralAmount *
Math.pow(10, client.getCustodyByMint(collateralMint).decimals),
);
const preInstructions: TransactionInstruction[] = [];
if (
!(await AdrenaClient.isAccountInitialized(
agent.connection,
collateralAccount,
))
) {
preInstructions.push(
AdrenaClient.createATAInstruction({
ataAddress: collateralAccount,
mint: tradeMint,
owner,
}),
);
}
const instruction = await client.program.methods
.openOrIncreasePositionWithSwapLong({
price: priceWithSlippage,
collateral: scaledCollateralAmount,
leverage,
referrer: null,
})
.accountsStrict({
owner,
payer: owner,
fundingAccount,
collateralAccount,
receivingCustody,
receivingCustodyOracle,
receivingCustodyTokenAccount,
principalCustody,
principalCustodyOracle,
principalCustodyTradeOracle,
principalCustodyTokenAccount,
transferAuthority: AdrenaClient.transferAuthority,
cortex: AdrenaClient.cortex,
lmStaking: AdrenaClient.lmStaking,
lpStaking: AdrenaClient.lpStaking,
pool: AdrenaClient.mainPool,
position,
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
stakingRewardTokenCustodyTokenAccount,
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
lpTokenMint: AdrenaClient.lpTokenMint,
userProfile: userProfile ? userProfilePda : null,
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
systemProgram: SystemProgram.programId,
tokenProgram: TOKEN_PROGRAM_ID,
adrenaProgram: ADRENA_PROGRAM_ID,
})
.instruction();
return sendTx(agent, [...preInstructions, instruction]);
}
/**
* Open short trade on Adrena
*
* Note: provide USDC as collateralMint to avoid swap
* @returns Transaction signature
*/
export async function openPerpTradeShort({
agent,
price,
collateralAmount,
collateralMint = TOKENS.USDC,
leverage = DEFAULT_OPTIONS.LEVERAGE_BPS,
tradeMint = TOKENS.jitoSOL,
slippage = 0.3,
}: {
agent: SolanaAgentKit;
price: number;
collateralAmount: number;
collateralMint?: PublicKey;
leverage?: number;
tradeMint?: PublicKey;
slippage?: number;
}): Promise<string> {
const client = await AdrenaClient.load(agent);
const owner = agent.wallet.publicKey;
const collateralAccount = AdrenaClient.findATAAddressSync(owner, tradeMint);
const fundingAccount = AdrenaClient.findATAAddressSync(owner, collateralMint);
const receivingCustody = AdrenaClient.findCustodyAddress(collateralMint);
const receivingCustodyOracle = client.getCustodyByMint(collateralMint).oracle;
const receivingCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(collateralMint);
// Principal custody is the custody of the targeted token
// i.e open a 1 BTC short position, principal custody is BTC
const principalCustody = AdrenaClient.findCustodyAddress(tradeMint);
const principalCustodyAccount = client.getCustodyByMint(tradeMint);
const principalCustodyTradeOracle = principalCustodyAccount.tradeOracle;
const principalCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(tradeMint);
const usdcAta = AdrenaClient.findATAAddressSync(owner, TOKENS.USDC);
const preInstructions: TransactionInstruction[] = [];
if (!(await AdrenaClient.isAccountInitialized(agent.connection, usdcAta))) {
preInstructions.push(
AdrenaClient.createATAInstruction({
ataAddress: usdcAta,
mint: TOKENS.USDC,
owner,
}),
);
}
// Custody used to provide collateral when opening the position
// Should be a stable token, by default, use USDC
const instructionCollateralMint = TOKENS.USDC;
const collateralCustody = AdrenaClient.findCustodyAddress(
instructionCollateralMint,
);
const collateralCustodyOracle = client.getCustodyByMint(
instructionCollateralMint,
).oracle;
const collateralCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(instructionCollateralMint);
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
AdrenaClient.stakingRewardTokenMint,
);
const stakingRewardTokenCustodyTokenAccount =
AdrenaClient.findCustodyTokenAccountAddress(
AdrenaClient.stakingRewardTokenMint,
);
const position = AdrenaClient.findPositionAddress(
owner,
principalCustody,
"long",
);
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
const userProfile =
await client.program.account.userProfile.fetchNullable(userProfilePda);
const priceWithSlippage = applySlippage(
new BN(price * 10 ** PRICE_DECIMALS),
slippage,
);
const scaledCollateralAmount = new BN(
collateralAmount *
Math.pow(10, client.getCustodyByMint(collateralMint).decimals),
);
const instruction = await client.program.methods
.openOrIncreasePositionWithSwapShort({
price: priceWithSlippage,
collateral: scaledCollateralAmount,
leverage,
referrer: null,
})
.accountsStrict({
owner,
payer: owner,
fundingAccount,
collateralAccount,
receivingCustody,
receivingCustodyOracle,
receivingCustodyTokenAccount,
principalCustody,
principalCustodyTradeOracle,
principalCustodyTokenAccount,
collateralCustody,
collateralCustodyOracle,
collateralCustodyTokenAccount,
transferAuthority: AdrenaClient.transferAuthority,
cortex: AdrenaClient.cortex,
lmStaking: AdrenaClient.lmStaking,
lpStaking: AdrenaClient.lpStaking,
pool: AdrenaClient.mainPool,
position,
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
stakingRewardTokenCustodyTokenAccount,
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
lpTokenMint: AdrenaClient.lpTokenMint,
userProfile: userProfile ? userProfilePda : null,
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
systemProgram: SystemProgram.programId,
tokenProgram: TOKEN_PROGRAM_ID,
adrenaProgram: ADRENA_PROGRAM_ID,
})
.instruction();
return sendTx(agent, [...preInstructions, instruction]);
}

View File

@@ -0,0 +1 @@
export * from "./adrena_perp_trading";

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import OpenAI from "openai";
/**

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "..";
import { SolanaAgentKit } from "../../agent";
/**
* Get the agents wallet address

2
src/tools/agent/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./create_image";
export * from "./get_wallet_address";

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import { getAllTld } from "@onsol/tldparser";
/**

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
import { PublicKey } from "@solana/web3.js";
import { TldParser } from "@onsol/tldparser";

View File

@@ -1,5 +1,5 @@
import { TldParser } from "@onsol/tldparser";
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
/**
* Get all domains owned by an address for a specific TLD
* @param agent SolanaAgentKit instance

View File

@@ -0,0 +1,4 @@
export * from "./get_all_domains_tlds";
export * from "./get_owned_all_domains";
export * from "./get_owned_domains_for_tld";
export * from "./resolve_domain";

View File

@@ -1,5 +1,5 @@
import { TldParser } from "@onsol/tldparser";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../agent";
import { PublicKey } from "@solana/web3.js";
/**

View File

@@ -1,37 +0,0 @@
import {
PublicKey,
sendAndConfirmTransaction,
Transaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { ManifestClient } from "@cks-systems/manifest-sdk";
/**
* Cancels all orders from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function cancelAllOrders(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const cancelAllOrdersIx = await mfxClient.cancelAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(cancelAllOrdersIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Cancel all orders failed: ${error.message}`);
}
}

View File

@@ -1,5 +1,5 @@
import { PublicKey } from "@solana/web3.js";
import { JupiterTokenData } from "../types";
import { JupiterTokenData } from "../../types";
export async function getTokenDataByAddress(
mint: PublicKey,
@@ -9,17 +9,14 @@ export async function getTokenDataByAddress(
throw new Error("Mint address is required");
}
const response = await fetch("https://tokens.jup.ag/tokens?tags=verified", {
const response = await fetch(`https://tokens.jup.ag/token/${mint}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = (await response.json()) as JupiterTokenData[];
const token = data.find((token: JupiterTokenData) => {
return token.address === mint.toBase58();
});
const token = (await response.json()) as JupiterTokenData;
return token;
} catch (error: any) {
throw new Error(`Error fetching token data: ${error.message}`);

View File

@@ -0,0 +1 @@
export * from "./get_token_data";

460
src/tools/drift/drift.ts Normal file
View File

@@ -0,0 +1,460 @@
import {
BASE_PRECISION,
convertToNumber,
DRIFT_PROGRAM_ID,
DriftClient,
FastSingleTxSender,
getLimitOrderParams,
getMarketOrderParams,
getUserAccountPublicKeySync,
MainnetSpotMarkets,
numberToSafeBN,
PositionDirection,
PostOnlyParams,
PRICE_PRECISION,
QUOTE_PRECISION,
User,
type IWallet,
} from "@drift-labs/sdk";
import type { SolanaAgentKit } from "../../agent";
import * as anchor from "@coral-xyz/anchor";
import { IDL, VAULT_PROGRAM_ID, VaultClient } from "@drift-labs/vaults-sdk";
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import { Transaction } from "@solana/web3.js";
import { ComputeBudgetProgram } from "@solana/web3.js";
export async function initClients(
agent: SolanaAgentKit,
params?: {
authority: PublicKey;
activeSubAccountId: number;
subAccountIds: number[];
},
) {
const wallet: IWallet = {
publicKey: agent.wallet.publicKey,
payer: agent.wallet,
signAllTransactions: async (txs) => {
for (const tx of txs) {
tx.sign(agent.wallet);
}
return txs;
},
signTransaction: async (tx) => {
tx.sign(agent.wallet);
return tx;
},
};
// @ts-expect-error - false undefined type conflict
const driftClient = new DriftClient({
connection: agent.connection,
wallet,
env: "mainnet-beta",
authority: params?.authority,
activeSubAccountId: params?.activeSubAccountId,
subAccountIds: params?.subAccountIds,
txParams: {
computeUnitsPrice: 0.000001 * 1000000 * 1000000,
},
txSender: new FastSingleTxSender({
connection: agent.connection,
wallet,
timeout: 30000,
blockhashRefreshInterval: 1000,
opts: {
commitment: agent.connection.commitment ?? "confirmed",
skipPreflight: false,
preflightCommitment: agent.connection.commitment ?? "confirmed",
},
}),
});
const vaultProgram = new anchor.Program(
IDL,
VAULT_PROGRAM_ID,
driftClient.provider,
);
const vaultClient = new VaultClient({
driftClient,
// @ts-expect-error - type mismatch due to different dep versions
program: vaultProgram,
cliMode: false,
});
await driftClient.subscribe();
async function cleanUp() {
await driftClient.unsubscribe();
}
return { driftClient, vaultClient, cleanUp };
}
/**
* Create a drift user account provided an amount
* @param amount amount of the token to deposit
* @param symbol symbol of the token to deposit
*/
export async function createDriftUserAccount(
agent: SolanaAgentKit,
amount: number,
symbol: string,
) {
try {
const { driftClient, cleanUp } = await initClients(agent);
const user = new User({
driftClient,
userAccountPublicKey: getUserAccountPublicKeySync(
new PublicKey(DRIFT_PROGRAM_ID),
agent.wallet.publicKey,
),
});
const userAccountExists = await user.exists();
const token = MainnetSpotMarkets.find(
(v) => v.symbol === symbol.toUpperCase(),
);
if (!token) {
throw new Error(`Token with symbol ${symbol} not found`);
}
if (!userAccountExists) {
const depositAmount = numberToSafeBN(amount, token.precision);
const [txSignature, account] =
await driftClient.initializeUserAccountAndDepositCollateral(
depositAmount,
getAssociatedTokenAddressSync(token.mint, agent.wallet.publicKey),
);
await cleanUp();
return { txSignature, account };
}
await cleanUp();
return {
message: "User account already exists",
account: user.userAccountPublicKey,
};
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to create user account: ${e.message}`);
}
}
/**
* Deposit to your drift user account
* @param agent
* @param amount
* @param symbol
* @param isRepay
* @returns
*/
export async function depositToDriftUserAccount(
agent: SolanaAgentKit,
amount: number,
symbol: string,
isRepay = false,
) {
try {
const { driftClient, cleanUp } = await initClients(agent);
const publicKey = agent.wallet.publicKey;
const user = new User({
driftClient,
userAccountPublicKey: getUserAccountPublicKeySync(
new PublicKey(DRIFT_PROGRAM_ID),
publicKey,
),
});
const userAccountExists = await user.exists();
const token = MainnetSpotMarkets.find(
(v) => v.symbol === symbol.toUpperCase(),
);
if (!token) {
throw new Error(`Token with symbol ${symbol} not found`);
}
if (!userAccountExists) {
throw new Error("You need to create a Drift user account first.");
}
const depositAmount = numberToSafeBN(amount, token.precision);
const [depInstruction, latestBlockhash] = await Promise.all([
driftClient.getDepositTxnIx(
depositAmount,
token.marketIndex,
getAssociatedTokenAddressSync(token.mint, publicKey),
undefined,
isRepay,
),
driftClient.connection.getLatestBlockhash(),
]);
const tx = new Transaction().add(...depInstruction).add(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 0.000001 * 1000000 * 1000000,
}),
);
tx.recentBlockhash = latestBlockhash.blockhash;
tx.sign(agent.wallet);
const txSignature = await driftClient.txSender.sendRawTransaction(
tx.serialize(),
{ ...driftClient.opts },
);
await cleanUp();
return txSignature;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to deposit to user account: ${e.message}`);
}
}
export async function withdrawFromDriftUserAccount(
agent: SolanaAgentKit,
amount: number,
symbol: string,
isBorrow = false,
) {
try {
const { driftClient, cleanUp } = await initClients(agent);
const user = new User({
driftClient,
userAccountPublicKey: getUserAccountPublicKeySync(
new PublicKey(DRIFT_PROGRAM_ID),
agent.wallet.publicKey,
),
});
const userAccountExists = await user.exists();
if (!userAccountExists) {
throw new Error("You need to create a Drift user account first.");
}
const token = MainnetSpotMarkets.find(
(v) => v.symbol === symbol.toUpperCase(),
);
if (!token) {
throw new Error(`Token with symbol ${symbol} not found`);
}
const withdrawAmount = numberToSafeBN(amount, token.precision);
const [withdrawInstruction, latestBlockhash] = await Promise.all([
driftClient.getWithdrawalIxs(
withdrawAmount,
token.marketIndex,
getAssociatedTokenAddressSync(token.mint, agent.wallet.publicKey),
!isBorrow,
),
driftClient.connection.getLatestBlockhash(),
]);
const tx = new Transaction().add(...withdrawInstruction).add(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 0.000001 * 1000000 * 1000000,
}),
);
tx.recentBlockhash = latestBlockhash.blockhash;
tx.sign(agent.wallet);
const txSignature = await driftClient.txSender.sendRawTransaction(
tx.serialize(),
{ ...driftClient.opts },
);
await cleanUp();
return txSignature;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to withdraw from user account: ${e.message}`);
}
}
/**
* Open a perpetual trade on drift
* @param agent
* @param params.amount
* @param params.symbol
* @param params.action
* @param params.type
* @param params.price this should only be supplied if type is limit
* @param params.reduceOnly
*/
export async function driftPerpTrade(
agent: SolanaAgentKit,
params: {
amount: number;
symbol: string;
action: "long" | "short";
type: "market" | "limit";
price?: number | undefined;
},
) {
try {
const { driftClient, cleanUp } = await initClients(agent);
const user = new User({
driftClient,
userAccountPublicKey: getUserAccountPublicKeySync(
new PublicKey(DRIFT_PROGRAM_ID),
agent.wallet.publicKey,
),
});
const userAccountExists = await user.exists();
if (!userAccountExists) {
throw new Error("You need to create a Drift user account first.");
}
const market = driftClient.getMarketIndexAndType(
`${params.symbol.toUpperCase()}-PERP`,
);
if (!market) {
throw new Error(`Token with symbol ${params.symbol} not found`);
}
const baseAssetPrice = driftClient.getOracleDataForPerpMarket(
market.marketIndex,
);
const convertedAmount =
params.amount / convertToNumber(baseAssetPrice.price, PRICE_PRECISION);
let signature: anchor.web3.TransactionSignature;
if (params.type === "limit") {
if (!params.price) {
throw new Error("Price is required for limit orders");
}
signature = await driftClient.placePerpOrder(
getLimitOrderParams({
baseAssetAmount: numberToSafeBN(convertedAmount, BASE_PRECISION),
reduceOnly: false,
direction:
params.action === "long"
? PositionDirection.LONG
: PositionDirection.SHORT,
marketIndex: market.marketIndex,
price: numberToSafeBN(params.price, PRICE_PRECISION),
postOnly: PostOnlyParams.SLIDE,
}),
{
computeUnitsPrice: 0.000001 * 1000000 * 1000000,
},
);
} else {
signature = await driftClient.placePerpOrder(
getMarketOrderParams({
baseAssetAmount: numberToSafeBN(convertedAmount, BASE_PRECISION),
reduceOnly: false,
direction:
params.action === "long"
? PositionDirection.LONG
: PositionDirection.SHORT,
marketIndex: market.marketIndex,
}),
{
computeUnitsPrice: 0.000001 * 1000000 * 1000000,
},
);
}
if (!signature) {
throw new Error("Failed to place order. Please make sure ");
}
await cleanUp();
return signature;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to place order: ${e.message}`);
}
}
/**
* Check if a user has a drift account
* @param agent
*/
export async function doesUserHaveDriftAccount(agent: SolanaAgentKit) {
try {
const { driftClient, cleanUp } = await initClients(agent);
const user = new User({
driftClient,
userAccountPublicKey: getUserAccountPublicKeySync(
new PublicKey(DRIFT_PROGRAM_ID),
agent.wallet.publicKey,
),
});
user.getActivePerpPositions();
const userAccountExists = await user.exists();
await cleanUp();
return {
hasAccount: userAccountExists,
account: user.userAccountPublicKey,
};
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to check user account: ${e.message}`);
}
}
/**
* Get account info for a drift User
* @param agent
* @returns
*/
export async function driftUserAccountInfo(agent: SolanaAgentKit) {
try {
const { driftClient, cleanUp } = await initClients(agent);
const user = new User({
driftClient,
userAccountPublicKey: getUserAccountPublicKeySync(
new PublicKey(DRIFT_PROGRAM_ID),
agent.wallet.publicKey,
),
});
const userAccountExists = await user.exists();
if (!userAccountExists) {
throw new Error("User account does not exist");
}
await user.subscribe();
const account = user.getUserAccount();
await user.unsubscribe();
await cleanUp();
const perpPositions = account.perpPositions.map((pos) => ({
...pos,
baseAssetAmount: convertToNumber(pos.baseAssetAmount, BASE_PRECISION),
settledPnl: convertToNumber(pos.settledPnl, QUOTE_PRECISION),
}));
const spotPositions = account.spotPositions.map((pos) => ({
...pos,
scaledBalance: convertToNumber(pos.scaledBalance, BASE_PRECISION),
cumulativeDeposits: convertToNumber(
pos.cumulativeDeposits,
BASE_PRECISION,
),
symbol: MainnetSpotMarkets.find((v) => v.marketIndex === pos.marketIndex)
?.symbol,
}));
return {
...account,
name: account.name,
authority: account.authority,
totalDeposits: `$${convertToNumber(account.totalDeposits, QUOTE_PRECISION)}`,
totalWithdraws: `$${convertToNumber(account.totalWithdraws, QUOTE_PRECISION)}`,
settledPerpPnl: `$${convertToNumber(account.settledPerpPnl, QUOTE_PRECISION)}`,
lastActiveSlot: account.lastActiveSlot.toNumber(),
perpPositions,
spotPositions,
};
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to check user account: ${e.message}`);
}
}

View File

@@ -0,0 +1,641 @@
import {
BASE_PRECISION,
convertToNumber,
getLimitOrderParams,
getMarketOrderParams,
getOrderParams,
MainnetPerpMarkets,
MainnetSpotMarkets,
MarketType,
numberToSafeBN,
PERCENTAGE_PRECISION,
PositionDirection,
PostOnlyParams,
PRICE_PRECISION,
QUOTE_PRECISION,
TEN,
} from "@drift-labs/sdk";
import {
WithdrawUnit,
decodeName,
encodeName,
getVaultAddressSync,
getVaultDepositorAddressSync,
} from "@drift-labs/vaults-sdk";
import {
ComputeBudgetProgram,
PublicKey,
type TransactionInstruction,
} from "@solana/web3.js";
import type { SolanaAgentKit } from "../../agent";
import { BN } from "bn.js";
import { initClients } from "./drift";
export function getMarketIndexAndType(name: `${string}-${string}`) {
const [symbol, type] = name.toUpperCase().split("-");
if (type === "PERP") {
const token = MainnetPerpMarkets.find((v) => v.baseAssetSymbol === 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 getOrCreateVaultDepositor(agent: SolanaAgentKit, vault: string) {
const { vaultClient, cleanUp } = await initClients(agent);
const vaultPublicKey = new PublicKey(vault);
const vaultDepositor = getVaultDepositorAddressSync(
vaultClient.program.programId,
vaultPublicKey,
agent.wallet.publicKey,
);
try {
await vaultClient.getVaultDepositor(vaultDepositor);
await cleanUp();
return vaultDepositor;
} catch (e) {
// @ts-expect-error - error message is a string
if (e.message.includes("Account does not exist")) {
await vaultClient.initializeVaultDepositor(
vaultPublicKey,
agent.wallet.publicKey,
);
}
await new Promise((resolve) => setTimeout(resolve, 2000));
await cleanUp();
return vaultDepositor;
}
}
async function getVaultAvailableBalance(agent: SolanaAgentKit, vault: string) {
try {
const { cleanUp, vaultClient } = await initClients(agent);
const vaultDetails = await vaultClient.getVault(new PublicKey(vault));
const currentVaultBalance = convertToNumber(
vaultDetails.netDeposits,
QUOTE_PRECISION,
);
const vaultWithdrawalsRequested = convertToNumber(
vaultDetails.totalWithdrawRequested,
QUOTE_PRECISION,
);
const availableBalanceInUSD =
currentVaultBalance - vaultWithdrawalsRequested;
await cleanUp();
return availableBalanceInUSD;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to get vault available balance: ${e.message}`);
}
}
/**
Create a vault
@param agent SolanaAgentKit instance
@param params Vault creation parameters
@param params.name Name of the vault (must be unique)
@param params.marketName Market name of the vault (e.g. "USDC-SPOT")
@param params.redeemPeriod Redeem period in seconds
@param params.maxTokens Maximum amount that can be deposited into the vault (in tokens)
@param params.minDepositAmount Minimum amount that can be deposited into the vault (in tokens)
@param params.managementFee Management fee percentage (e.g 2 == 2%)
@param params.profitShare Profit share percentage (e.g 20 == 20%)
@param params.hurdleRate Hurdle rate percentage
@param params.permissioned Whether the vault uses a whitelist
@returns Promise<anchor.Web3.TransactionSignature> - The transaction signature of the vault creation
*/
export async function createVault(
agent: SolanaAgentKit,
params: {
name: string;
marketName: `${string}-${string}`;
redeemPeriod: number;
maxTokens: number;
minDepositAmount: number;
managementFee: number;
profitShare: number;
hurdleRate?: number;
permissioned?: boolean;
},
) {
try {
const { vaultClient, driftClient, cleanUp } = await initClients(agent);
const marketIndexAndType = getMarketIndexAndType(params.marketName);
if (!marketIndexAndType) {
throw new Error("Invalid market name");
}
const spotMarket = driftClient.getSpotMarketAccount(
marketIndexAndType.marketIndex,
);
if (!spotMarket) {
throw new Error("Market not found");
}
const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
if (marketIndexAndType.marketType === MarketType.PERP) {
throw new Error("Only SPOT market names are supported");
}
const tx = await vaultClient.initializeVault({
name: encodeName(params.name),
spotMarketIndex: marketIndexAndType.marketIndex,
hurdleRate: new BN(params.hurdleRate ?? 0)
.mul(PERCENTAGE_PRECISION)
.div(new BN(100))
.toNumber(),
profitShare: new BN(params.profitShare)
.mul(PERCENTAGE_PRECISION)
.div(new BN(100))
.toNumber(),
minDepositAmount: numberToSafeBN(params.minDepositAmount, spotPrecision),
redeemPeriod: new BN(params.redeemPeriod * 86400),
maxTokens: numberToSafeBN(params.maxTokens, spotPrecision),
managementFee: new BN(params.managementFee)
.mul(PERCENTAGE_PRECISION)
.div(new BN(100)),
permissioned: params.permissioned ?? false,
});
await cleanUp();
return tx;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to create Drift vault: ${e.message}`);
}
}
export async function updateVaultDelegate(
agent: SolanaAgentKit,
vault: string,
delegateAddress: string,
) {
try {
const { vaultClient, cleanUp } = await initClients(agent);
const signature = await vaultClient.updateDelegate(
new PublicKey(vault),
new PublicKey(delegateAddress),
);
await cleanUp();
return signature;
} catch (e) {
throw new Error(
// @ts-expect-error - error message is a string
`Failed to update vault delegate: ${e.message}`,
);
}
}
/**
Update the vault's info
@param agent SolanaAgentKit instance
@param vault Vault address
@param params Vault update parameters
@param params.redeemPeriod Redeem period in seconds
@param params.maxTokens Maximum amount that can be deposited into the vault (in tokens)
@param params.minDepositAmount Minimum amount that can be deposited into the vault (in tokens)
@param params.managementFee Management fee percentage (e.g 2 == 2%)
@param params.profitShare Profit share percentage (e.g 20 == 20%)
@param params.hurdleRate Hurdle rate percentage
@param params.permissioned Whether the vault uses a whitelist
@returns Promise<anchor.Web3.TransactionSignature> - The transaction signature of the vault update
*/
export async function updateVault(
agent: SolanaAgentKit,
vault: string,
params: {
redeemPeriod?: number;
maxTokens?: number;
minDepositAmount?: number;
managementFee?: number;
profitShare?: number;
hurdleRate?: number;
permissioned?: boolean;
},
) {
try {
const { vaultClient, cleanUp, driftClient } = await initClients(agent);
const vaultPublicKey = new PublicKey(vault);
const vaultDetails = await vaultClient.getVault(vaultPublicKey);
const spotMarket = driftClient.getSpotMarketAccount(
vaultDetails.spotMarketIndex,
);
if (!spotMarket) {
throw new Error("Market not found");
}
const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
const tx = await vaultClient.managerUpdateVault(vaultPublicKey, {
redeemPeriod: params.redeemPeriod
? new BN(params.redeemPeriod * 86400)
: null,
maxTokens: params.maxTokens
? numberToSafeBN(params.maxTokens, spotPrecision)
: null,
minDepositAmount: params.minDepositAmount
? numberToSafeBN(params.minDepositAmount, spotPrecision)
: null,
managementFee: params.managementFee
? new BN(params.managementFee)
.mul(PERCENTAGE_PRECISION)
.div(new BN(100))
: null,
profitShare: params.profitShare
? new BN(params.profitShare)
.mul(PERCENTAGE_PRECISION)
.div(new BN(100))
.toNumber()
: null,
hurdleRate: params.hurdleRate
? new BN(params.hurdleRate)
.mul(PERCENTAGE_PRECISION)
.div(new BN(100))
.toNumber()
: null,
permissioned: params.permissioned ?? vaultDetails.permissioned,
});
await cleanUp();
return tx;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to update Drift vault: ${e.message}`);
}
}
export const validateAndEncodeAddress = (input: string, programId: string) => {
try {
return new PublicKey(input);
} catch {
return getVaultAddressSync(new PublicKey(programId), encodeName(input));
}
};
/**
* Get information on a particular vault given its name
* @param agent
* @param vaultNameOrAddress
* @returns
*/
export async function getVaultInfo(
agent: SolanaAgentKit,
vaultNameOrAddress: string,
) {
try {
const { vaultClient, cleanUp } = await initClients(agent);
const vaultPublicKey = validateAndEncodeAddress(
vaultNameOrAddress,
vaultClient.program.programId.toBase58(),
);
const [vaultDetails, vaultBalance] = await Promise.all([
vaultClient.getVault(vaultPublicKey),
getVaultAvailableBalance(agent, vaultPublicKey.toBase58()),
]);
await cleanUp();
const spotToken = MainnetSpotMarkets[vaultDetails.spotMarketIndex];
const data = {
name: decodeName(vaultDetails.name),
delegate: vaultDetails.delegate.toBase58(),
address: vaultPublicKey.toBase58(),
marketName: `${spotToken.symbol}-SPOT`,
balance: `${vaultBalance} ${spotToken.symbol}`,
redeemPeriod: vaultDetails.redeemPeriod.toNumber(),
maxTokens: vaultDetails.maxTokens.div(spotToken.precision).toNumber(),
minDepositAmount: vaultDetails.minDepositAmount
.div(spotToken.precision)
.toNumber(),
managementFee:
(vaultDetails.managementFee.toNumber() /
PERCENTAGE_PRECISION.toNumber()) *
100,
profitShare:
(vaultDetails.profitShare / PERCENTAGE_PRECISION.toNumber()) * 100,
hurdleRate:
(vaultDetails.hurdleRate / PERCENTAGE_PRECISION.toNumber()) * 100,
permissioned: vaultDetails.permissioned,
};
return data;
} 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
@param amount Amount to deposit into the vault (in tokens)
@param vault Vault address
@returns Promise<anchor.Web3.TransactionSignature> - The transaction signature of the deposit
*/
export async function depositIntoVault(
agent: SolanaAgentKit,
amount: number,
vault: string,
) {
const { vaultClient, driftClient, cleanUp } = await initClients(agent);
try {
const vaultPublicKey = new PublicKey(vault);
const [isOwned, vaultDetails, vaultDepositor] = await Promise.all([
getIsOwned(agent, vault),
vaultClient.getVault(vaultPublicKey),
getOrCreateVaultDepositor(agent, vault),
]);
const spotMarket = driftClient.getSpotMarketAccount(
vaultDetails.spotMarketIndex,
);
if (!spotMarket) {
throw new Error("Market not found");
}
const spotPrecision = TEN.pow(new BN(spotMarket.decimals));
const amountBN = numberToSafeBN(amount, spotPrecision);
if (isOwned) {
return await vaultClient.managerDeposit(vaultPublicKey, amountBN);
}
const tx = 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}`);
}
}
/**
Request a withdrawal from a vault. If successful redemption period starts and the user can redeem the tokens after the period ends
@param agent SolanaAgentKit instance
@param amount Amount to withdraw from the vault (in shares)
@param vault Vault address
*/
export async function requestWithdrawalFromVault(
agent: SolanaAgentKit,
amount: number,
vault: string,
) {
try {
const { vaultClient, cleanUp } = await initClients(agent);
const vaultPublicKey = new PublicKey(vault);
const isOwned = await getIsOwned(agent, vault);
if (isOwned) {
return await vaultClient.managerRequestWithdraw(
vaultPublicKey,
numberToSafeBN(amount, QUOTE_PRECISION),
WithdrawUnit.TOKEN,
);
}
const vaultDepositor = await getOrCreateVaultDepositor(agent, vault);
const tx = await vaultClient.requestWithdraw(
vaultDepositor,
numberToSafeBN(amount, QUOTE_PRECISION),
WithdrawUnit.TOKEN,
);
await cleanUp();
return tx;
} catch (e) {
throw new Error(
// @ts-expect-error - error message is a string
`Failed to request withdrawal from Drift vault: ${e.message}`,
);
}
}
/**
Withdraw tokens once the redemption period has elapsed.
@param agent SolanaAgentKit instance
@param vault Vault address
@returns Promise<anchor.Web3.TransactionSignature> - The transaction signature of the redemption
*/
export async function withdrawFromDriftVault(
agent: SolanaAgentKit,
vault: string,
) {
try {
const { vaultClient, cleanUp } = await initClients(agent);
const vaultPublicKey = new PublicKey(vault);
const isOwned = await getIsOwned(agent, vault);
if (isOwned) {
return await vaultClient.managerWithdraw(vaultPublicKey);
}
const vaultDepositor = await getOrCreateVaultDepositor(agent, vault);
const tx = await vaultClient.withdraw(vaultDepositor);
await cleanUp();
return tx;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to redeem tokens from Drift vault: ${e.message}`);
}
}
/**
Get if vault is owned by the user
@param agent SolanaAgentKit instance
@param vault Vault address
@returns Promise<boolean> - Whether the vault is owned by the user
*/
async function getIsOwned(agent: SolanaAgentKit, vault: string) {
try {
const { vaultClient, cleanUp } = await initClients(agent);
const vaultPublicKey = new PublicKey(vault);
const vaultDetails = await vaultClient.getVault(vaultPublicKey);
const isOwned = vaultDetails.manager.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}`);
}
}
/**
* Get a vaults address using the vault's name
* @param agent
* @param name
*/
export async function getVaultAddress(agent: SolanaAgentKit, name: string) {
const encodedName = encodeName(name);
try {
const { vaultClient, cleanUp } = await initClients(agent);
const vaultAddress = getVaultAddressSync(
vaultClient.program.programId,
encodedName,
);
await cleanUp();
return vaultAddress;
} catch (e) {
throw new Error(
// @ts-expect-error - error message is a string
`Failed to get vault address: ${e.message}`,
);
}
}
/**
Carry out a trade with a delegated vault
@param agent SolanaAgentKit instance
@param amount Amount to trade (in tokens)
@param symbol Symbol of the token to trade
@param action Action to take (e.g. "buy" or "sell")
@param type Type of trade (e.g. "market" or "limit")
@param vault Vault address
*/
export async function tradeDriftVault(
agent: SolanaAgentKit,
vault: string,
amount: number,
symbol: string,
action: "long" | "short",
type: "market" | "limit",
price?: number,
) {
try {
const { driftClient, cleanUp } = await initClients(agent, {
authority: new PublicKey(vault),
activeSubAccountId: 0,
subAccountIds: [0],
});
const [isOwned, driftLookupTableAccount] = await Promise.all([
getIsOwned(agent, vault),
driftClient.fetchMarketLookupTableAccount(),
]);
if (!isOwned) {
throw new Error(
"This vault is owned by someone else, so you can't trade with it",
);
}
const usdcSpotMarket = driftClient.getSpotMarketAccount(0);
if (!usdcSpotMarket) {
throw new Error("USDC-SPOT market not found");
}
const perpMarketIndexAndType = getMarketIndexAndType(
`${symbol.toUpperCase()}-PERP`,
);
const perpMarketAccount = driftClient.getPerpMarketAccount(
perpMarketIndexAndType.marketIndex,
);
if (!perpMarketIndexAndType || !perpMarketAccount) {
throw new Error(
"Invalid symbol: Drift doesn't have a market for this token",
);
}
const perpOracle = driftClient.getOracleDataForPerpMarket(
perpMarketAccount.marketIndex,
);
const oraclePriceNumber = convertToNumber(
perpOracle.price,
PRICE_PRECISION,
);
const baseAmount = amount / oraclePriceNumber;
const instructions: TransactionInstruction[] = [];
instructions.push(
ComputeBudgetProgram.setComputeUnitLimit({ units: 1400000 }),
);
if (type === "limit" || price) {
if (!price) {
throw new Error("Price is required for limit orders");
}
const instruction = await driftClient.getPlaceOrdersIx([
getOrderParams(
getLimitOrderParams({
price: numberToSafeBN(price, PRICE_PRECISION),
marketType: MarketType.PERP,
baseAssetAmount: numberToSafeBN(baseAmount, BASE_PRECISION),
direction:
action === "long"
? PositionDirection.LONG
: PositionDirection.SHORT,
marketIndex: perpMarketAccount.marketIndex,
postOnly: PostOnlyParams.SLIDE,
}),
),
]);
instructions.push(instruction);
} else {
// defaults to market order if type is not limit and price is not provided
const instruction = await driftClient.getPlaceOrdersIx([
getOrderParams(
getMarketOrderParams({
marketType: MarketType.PERP,
baseAssetAmount: numberToSafeBN(baseAmount, BASE_PRECISION),
direction:
action === "long"
? PositionDirection.LONG
: PositionDirection.SHORT,
marketIndex: perpMarketAccount.marketIndex,
}),
),
]);
instructions.push(instruction);
}
const latestBlockhash = await driftClient.connection.getLatestBlockhash();
const tx = await driftClient.txSender.sendVersionedTransaction(
await driftClient.txSender.getVersionedTransaction(
instructions,
[driftLookupTableAccount],
[],
driftClient.opts,
latestBlockhash,
),
);
await cleanUp();
return tx;
} catch (e) {
// @ts-expect-error - error message is a string
throw new Error(`Failed to trade with Drift vault: ${e.message}`);
}
}

2
src/tools/drift/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./drift";
export * from "./drift_vault";

View File

@@ -0,0 +1,118 @@
import { ComputeBudgetProgram } from "@solana/web3.js";
import { PoolConfig, Side } from "flash-sdk";
import { BN } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../../index";
import {
CLOSE_POSITION_CU,
marketSdkInfo,
marketTokenMap,
getNftTradingAccountInfo,
fetchOraclePrice,
createPerpClient,
get_flash_privilege,
} from "../../utils/flashUtils";
import { FlashCloseTradeParams } from "../../types";
/**
* Closes an existing position on Flash.Trade
* @param agent SolanaAgentKit instance
* @param params Trade parameters
* @returns Transaction signature
*/
export async function flashCloseTrade(
agent: SolanaAgentKit,
params: FlashCloseTradeParams,
): Promise<string> {
try {
const { token, side } = params;
// Get market ID from token and side using marketTokenMap
const tokenMarkets = marketTokenMap[token];
if (!tokenMarkets) {
throw new Error(`Token ${token} not supported for trading`);
}
const sideEntry = tokenMarkets[side];
if (!sideEntry) {
throw new Error(`${side} side not available for ${token}`);
}
const market = sideEntry.marketID;
// Validate market data using marketSdkInfo
const marketData = marketSdkInfo[market];
if (!marketData) {
throw new Error(`Invalid market configuration for ${token}/${side}`);
}
// Get token information
const [targetSymbol, collateralSymbol] = marketData.tokenPair.split("/");
// Fetch oracle prices
const [targetPrice] = await Promise.all([
fetchOraclePrice(targetSymbol),
fetchOraclePrice(collateralSymbol),
]);
// Initialize pool configuration and perpClient
const poolConfig = PoolConfig.fromIdsByName(
marketData.pool,
"mainnet-beta",
);
const perpClient = createPerpClient(agent.connection, agent.wallet);
// Calculate price after slippage
const slippageBpsBN = new BN(100); // 1% slippage
const sideEnum = side === "long" ? Side.Long : Side.Short;
const priceWithSlippage = perpClient.getPriceAfterSlippage(
false, // isEntry = false for closing position
slippageBpsBN,
targetPrice.price,
sideEnum,
);
// Get NFT trading account info
const tradingAccounts = await getNftTradingAccountInfo(
agent.wallet_address,
perpClient,
poolConfig,
collateralSymbol,
);
if (
!tradingAccounts.nftTradingAccountPk ||
!tradingAccounts.nftReferralAccountPK ||
!tradingAccounts.nftOwnerRebateTokenAccountPk
) {
throw new Error("Required NFT trading accounts not found");
}
// Build and send transaction
const { instructions, additionalSigners } = await perpClient.closePosition(
targetSymbol,
collateralSymbol,
priceWithSlippage,
sideEnum,
poolConfig,
get_flash_privilege(agent),
tradingAccounts.nftTradingAccountPk,
tradingAccounts.nftReferralAccountPK,
tradingAccounts.nftOwnerRebateTokenAccountPk,
);
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
units: CLOSE_POSITION_CU,
});
return await perpClient.sendTransaction(
[computeBudgetIx, ...instructions],
{
additionalSigners: additionalSigners,
alts: perpClient.addressLookupTables,
prioritizationFee: 5000000,
},
);
} catch (error) {
throw new Error(`Flash trade close failed: ${error}`);
}
}

View File

@@ -0,0 +1,251 @@
import { ComputeBudgetProgram } from "@solana/web3.js";
import {
PerpetualsClient,
OraclePrice,
PoolConfig,
Side,
CustodyAccount,
Custody,
} from "flash-sdk";
import { BN } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../../index";
import {
ALL_TOKENS,
marketSdkInfo,
marketTokenMap,
getNftTradingAccountInfo,
OPEN_POSITION_CU,
fetchOraclePrice,
createPerpClient,
get_flash_privilege,
} from "../../utils/flashUtils";
import { FlashTradeParams } from "../../types";
/**
* Opens a new position on Flash.Trade
* @param agent SolanaAgentKit instance
* @param params Trade parameters
* @returns Transaction signature
*/
export async function flashOpenTrade(
agent: SolanaAgentKit,
params: FlashTradeParams,
): Promise<string> {
try {
const { token, side, collateralUsd, leverage } = params;
// Get market ID from token and side using marketTokenMap
const tokenMarkets = marketTokenMap[token];
if (!tokenMarkets) {
throw new Error(`Token ${token} not supported for trading`);
}
const sideEntry = tokenMarkets[side];
if (!sideEntry) {
throw new Error(`${side} side not available for ${token}`);
}
const market = sideEntry.marketID;
// Validate market data using marketSdkInfo
const marketData = marketSdkInfo[market];
if (!marketData) {
throw new Error(`Invalid market configuration for ${token}/${side}`);
}
// Get token information
const [targetSymbol, collateralSymbol] = marketData.tokenPair.split("/");
const targetToken = ALL_TOKENS.find((t) => t.symbol === targetSymbol);
const collateralToken = ALL_TOKENS.find(
(t) => t.symbol === collateralSymbol,
);
if (!targetToken || !collateralToken) {
throw new Error(`Token not found for pair ${marketData.tokenPair}`);
}
// Fetch oracle prices
const [targetPrice, collateralPrice] = await Promise.all([
fetchOraclePrice(targetSymbol),
fetchOraclePrice(collateralSymbol),
]);
// Initialize pool configuration and perpClient
const poolConfig = PoolConfig.fromIdsByName(
marketData.pool,
"mainnet-beta",
);
const perpClient = createPerpClient(agent.connection, agent.wallet);
// Calculate position parameters
const leverageBN = new BN(leverage);
const collateralTokenPrice = convertPriceToNumber(collateralPrice.price);
const collateralAmount = calculateCollateralAmount(
collateralUsd,
collateralTokenPrice,
collateralToken.decimals,
);
// Get custody accounts
const { targetCustody, collateralCustody } = await fetchCustodyAccounts(
perpClient,
poolConfig,
targetSymbol,
collateralSymbol,
);
// Calculate position size
const positionSize = calculatePositionSize(
perpClient,
collateralAmount,
leverageBN,
targetToken,
collateralToken,
side,
targetPrice.price,
collateralPrice.price,
targetCustody,
collateralCustody,
);
// Get NFT trading account info
const tradingAccounts = await getNftTradingAccountInfo(
agent.wallet_address,
perpClient,
poolConfig,
collateralSymbol,
);
if (
!tradingAccounts.nftTradingAccountPk ||
!tradingAccounts.nftReferralAccountPK
) {
throw new Error("Required NFT trading accounts not found");
}
// Prepare transaction
const slippageBps = new BN(1000);
const priceWithSlippage = perpClient.getPriceAfterSlippage(
true,
slippageBps,
targetPrice.price,
side === "long" ? Side.Long : Side.Short,
);
// Build and send transaction
const { instructions, additionalSigners } = await perpClient.openPosition(
targetSymbol,
collateralSymbol,
priceWithSlippage,
collateralAmount,
positionSize,
side === "long" ? Side.Long : Side.Short,
poolConfig,
get_flash_privilege(agent),
tradingAccounts.nftTradingAccountPk,
tradingAccounts.nftReferralAccountPK,
tradingAccounts.nftOwnerRebateTokenAccountPk!,
false,
);
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitLimit({
units: OPEN_POSITION_CU,
});
return await perpClient.sendTransaction(
[computeBudgetIx, ...instructions],
{
additionalSigners: additionalSigners,
alts: perpClient.addressLookupTables,
prioritizationFee: 5000000,
},
);
} catch (error) {
throw new Error(`Flash trade failed: ${error}`);
}
}
// Helper functions
function convertPriceToNumber(oraclePrice: OraclePrice): number {
const price = parseInt(oraclePrice.price.toString("hex"), 16);
const exponent = parseInt(oraclePrice.exponent.toString("hex"), 16);
return price * Math.pow(10, exponent);
}
function calculateCollateralAmount(
usdAmount: number,
tokenPrice: number,
decimals: number,
): BN {
return new BN((usdAmount / tokenPrice) * Math.pow(10, decimals));
}
async function fetchCustodyAccounts(
perpClient: PerpetualsClient,
poolConfig: PoolConfig,
targetSymbol: string,
collateralSymbol: string,
) {
const targetConfig = poolConfig.custodies.find(
(c) => c.symbol === targetSymbol,
);
const collateralConfig = poolConfig.custodies.find(
(c) => c.symbol === collateralSymbol,
);
if (!targetConfig || !collateralConfig) {
throw new Error("Custody configuration not found");
}
const accounts = await perpClient.provider.connection.getMultipleAccountsInfo(
[targetConfig.custodyAccount, collateralConfig.custodyAccount],
);
if (!accounts[0] || !accounts[1]) {
throw new Error("Failed to fetch custody accounts");
}
return {
targetCustody: CustodyAccount.from(
targetConfig.custodyAccount,
perpClient.program.coder.accounts.decode<Custody>(
"custody",
accounts[0].data,
),
),
collateralCustody: CustodyAccount.from(
collateralConfig.custodyAccount,
perpClient.program.coder.accounts.decode<Custody>(
"custody",
accounts[1].data,
),
),
};
}
function calculatePositionSize(
perpClient: PerpetualsClient,
collateralAmount: BN,
leverage: BN,
targetToken: any,
collateralToken: any,
side: "long" | "short",
targetPrice: OraclePrice,
collateralPrice: OraclePrice,
targetCustody: CustodyAccount,
collateralCustody: CustodyAccount,
): BN {
return perpClient.getSizeAmountFromLeverageAndCollateral(
collateralAmount,
leverage.toString(),
targetToken,
collateralToken,
side === "long" ? Side.Long : Side.Short,
targetPrice,
targetPrice,
targetCustody,
collateralPrice,
collateralPrice,
collateralCustody,
);
}

2
src/tools/flash/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./flash_open_trade";
export * from "./flash_close_trade";

View File

@@ -1,6 +1,6 @@
import { VersionedTransaction } from "@solana/web3.js";
import { PublicKey } from "@solana/web3.js";
import { GibworkCreateTaskReponse, SolanaAgentKit } from "../index";
import { GibworkCreateTaskReponse, SolanaAgentKit } from "../../index";
/**
* Create an new task on Gibwork

View File

@@ -0,0 +1 @@
export * from "./create_gibwork_task";

View File

@@ -0,0 +1,57 @@
import { SolanaAgentKit } from "../../index";
import { PublicKey } from "@solana/web3.js";
/**
* Fetch assets by owner using the Helius Digital Asset Standard (DAS) API
* @param agent SolanaAgentKit instance
* @param ownerPublicKey Owner's Solana wallet PublicKey
* @param limit Number of assets to retrieve per request
* @returns Assets owned by the specified address
*/
export async function getAssetsByOwner(
agent: SolanaAgentKit,
ownerPublicKey: PublicKey,
limit: number,
): Promise<any> {
try {
const apiKey = agent.config.HELIUS_API_KEY;
if (!apiKey) {
throw new Error("HELIUS_API_KEY not found in environment variables");
}
const url = `https://mainnet.helius-rpc.com/?api-key=${apiKey}`;
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "get-assets",
method: "getAssetsByOwner",
params: {
ownerAddress: ownerPublicKey.toString(),
page: 3,
limit: limit,
displayOptions: {
showFungible: true,
},
},
}),
});
if (!response.ok) {
throw new Error(
`Failed to fetch: ${response.status} - ${response.statusText}`,
);
}
const data = await response.json();
return data.result.items;
} catch (error: any) {
console.error("Error retrieving assets: ", error.message);
throw new Error(`Assets retrieval failed: ${error.message}`);
}
}

View File

@@ -0,0 +1,44 @@
import { SolanaAgentKit } from "../../index";
/**
* Parse a Solana transaction using the Helius Enhanced Transactions API
* @param agent SolanaAgentKit instance
* @param transactionId The transaction ID to parse
* @returns Parsed transaction data
*/
export async function parseTransaction(
agent: SolanaAgentKit,
transactionId: string,
): Promise<any> {
try {
const apiKey = agent.config.HELIUS_API_KEY;
if (!apiKey) {
throw new Error("HELIUS_API_KEY not found in environment variables");
}
const url = `https://api.helius.xyz/v0/transactions/?api-key=${apiKey}`;
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
transactions: [transactionId],
}),
});
if (!response.ok) {
throw new Error(
`Failed to fetch: ${response.status} - ${response.statusText}`,
);
}
const data = await response.json();
return data;
} catch (error: any) {
console.error("Error parsing transaction: ", error.message);
throw new Error(`Transaction parsing failed: ${error.message}`);
}
}

View File

@@ -0,0 +1,132 @@
import { SolanaAgentKit } from "../../index";
import { HeliusWebhookResponse, HeliusWebhookIdResponse } from "../../index";
export async function create_HeliusWebhook(
agent: SolanaAgentKit,
accountAddresses: string[],
webhookURL: string,
): Promise<HeliusWebhookResponse> {
try {
const response = await fetch(
`https://api.helius.xyz/v0/webhooks?api-key=${agent.config.HELIUS_API_KEY}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
webhookURL,
transactionTypes: ["Any"],
accountAddresses,
webhookType: "enhanced",
txnStatus: "all",
}),
},
);
const data = await response.json();
return {
webhookURL: data.webhookURL,
webhookID: data.webhookID,
};
} catch (error: any) {
throw new Error(`Failed to create Webhook: ${error.message}`);
}
}
/**
* Retrieves a Helius Webhook by ID, returning only the specified fields.
*
* @param agent - An instance of SolanaAgentKit (with .config.HELIUS_API_KEY)
* @param webhookID - The unique ID of the webhook to retrieve
*
* @returns A HeliusWebhook object containing { wallet, webhookURL, transactionTypes, accountAddresses, webhookType }
*/
export async function getHeliusWebhook(
agent: SolanaAgentKit,
webhookID: string,
): Promise<HeliusWebhookIdResponse> {
try {
const apiKey = agent.config.HELIUS_API_KEY;
if (!apiKey) {
throw new Error("HELIUS_API_KEY is missing in agent.config");
}
const response = await fetch(
`https://api.helius.xyz/v0/webhooks/${webhookID}?api-key=${apiKey}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
},
);
if (!response.ok) {
throw new Error(
`Failed to fetch webhook with ID ${webhookID}. ` +
`Status Code: ${response.status}`,
);
}
const data = await response.json();
return {
wallet: data.wallet,
webhookURL: data.webhookURL,
transactionTypes: data.transactionTypes,
accountAddresses: data.accountAddresses,
webhookType: data.webhookType,
};
} catch (error: any) {
throw new Error(`Failed to get webhook by ID: ${error.message}`);
}
}
/**
* Deletes a Helius Webhook by its ID.
*
* @param agent - An instance of SolanaAgentKit (with .config.HELIUS_API_KEY)
* @param webhookID - The unique ID of the webhook to delete
*
* @returns The response body from the Helius API (which may contain status or other info)
*/
export async function deleteHeliusWebhook(
agent: SolanaAgentKit,
webhookID: string,
): Promise<any> {
try {
const apiKey = agent.config.HELIUS_API_KEY;
if (!apiKey) {
throw new Error("Missing Helius API key in agent.config.HELIUS_API_KEY");
}
const url = `https://api.helius.xyz/v0/webhooks/${webhookID}?api-key=${apiKey}`;
const response = await fetch(url, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(
`Failed to delete webhook: ${response.status} ${response.statusText}`,
);
}
if (response.status === 204) {
return { message: "Webhook deleted successfully (no content returned)" };
}
const contentLength = response.headers.get("Content-Length");
if (contentLength === "0" || !contentLength) {
return { message: "Webhook deleted successfully (empty body)" };
}
// Otherwise, parse as JSON
const data = await response.json();
return data;
} catch (error: any) {
console.error("Error deleting Helius Webhook:", error.message);
throw new Error(`Failed to delete Helius Webhook: ${error.message}`);
}
}

View File

@@ -0,0 +1,4 @@
export * from "./get_assets_by_owner";
export * from "./helius_transaction_parsing";
export * from "./helius_webhooks";
export * from "./send_transaction_with_priority";

View File

@@ -0,0 +1,174 @@
import { SolanaAgentKit, PriorityFeeResponse } from "../../index";
import {
SystemProgram,
Transaction,
sendAndConfirmTransaction,
ComputeBudgetProgram,
PublicKey,
LAMPORTS_PER_SOL,
} from "@solana/web3.js";
import {
getAssociatedTokenAddress,
createTransferInstruction,
getMint,
createAssociatedTokenAccountInstruction,
} from "@solana/spl-token";
import bs58 from "bs58";
/**
* Sends a transaction with an estimated priority fee using the provided SolanaAgentKit.
*
* @param agent An instance of SolanaAgentKit containing connection, wallet, etc.
* @param priorityLevel The priority level (e.g., "Min", "Low", "Medium", "High", "VeryHigh", or "UnsafeMax").
* @param amount The amount of SOL to send (in SOL, not lamports).
* @param to The recipient's PublicKey.
* @returns The transaction signature (string) once confirmed along with the fee used.
*/
export async function sendTransactionWithPriorityFee(
agent: SolanaAgentKit,
priorityLevel: string,
amount: number,
to: PublicKey,
splmintAddress?: PublicKey,
): Promise<{ transactionId: string; fee: number }> {
try {
if (!splmintAddress) {
const transaction = new Transaction();
const { blockhash, lastValidBlockHeight } =
await agent.connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.lastValidBlockHeight = lastValidBlockHeight;
transaction.feePayer = agent.wallet_address;
const transferIx = SystemProgram.transfer({
fromPubkey: agent.wallet_address,
toPubkey: to,
lamports: amount * LAMPORTS_PER_SOL,
});
transaction.add(transferIx);
transaction.sign(agent.wallet);
const response = await fetch(
`https://mainnet.helius-rpc.com/?api-key=${agent.config.HELIUS_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: "1",
method: "getPriorityFeeEstimate",
params: [
{
transaction: bs58.encode(transaction.serialize()),
options: { priorityLevel: priorityLevel },
},
],
} as PriorityFeeResponse),
},
);
const data = await response.json();
if (data.error) {
throw new Error("Error fetching priority fee:");
}
const feeEstimate: number = data.result.priorityFeeEstimate;
// Set the priority fee if applicable
const computePriceIx = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: feeEstimate,
});
transaction.add(computePriceIx);
// Send the transaction and confirm
const txSignature = await sendAndConfirmTransaction(
agent.connection,
transaction,
[agent.wallet],
);
return {
transactionId: txSignature,
fee: feeEstimate,
};
} else {
const fromAta = await getAssociatedTokenAddress(
splmintAddress,
agent.wallet_address,
);
const toAta = await getAssociatedTokenAddress(splmintAddress, to);
const mintInfo = await getMint(agent.connection, splmintAddress);
const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);
const transaction = new Transaction();
const { blockhash, lastValidBlockHeight } =
await agent.connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
transaction.lastValidBlockHeight = lastValidBlockHeight;
transaction.feePayer = agent.wallet_address;
const response = await fetch(
`https://mainnet.helius-rpc.com/?api-key=${agent.config.HELIUS_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: "1",
method: "getPriorityFeeEstimate",
params: [
{
transaction: bs58.encode(transaction.serialize()),
options: { priorityLevel: priorityLevel },
},
],
} as PriorityFeeResponse),
},
);
const data = await response.json();
if (data.error) {
throw new Error("Error fetching priority fee:");
}
const feeEstimate: number = data.result.priorityFeeEstimate;
transaction.add(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: feeEstimate,
}),
);
transaction.add(
createAssociatedTokenAccountInstruction(
agent.wallet_address,
toAta,
to,
splmintAddress,
),
);
transaction.add(
createTransferInstruction(
fromAta,
toAta,
agent.wallet_address,
adjustedAmount,
),
);
const txSignature = await sendAndConfirmTransaction(
agent.connection,
transaction,
[agent.wallet],
);
return {
transactionId: txSignature,
fee: feeEstimate,
};
}
} catch (error: any) {
throw new Error(`Failed to process transaction: ${error.message}`);
}
}

View File

@@ -1,64 +1,30 @@
export * from "./get_wallet_address";
export * from "./request_faucet_funds";
export * from "./get_wallet_address";
export * from "./request_faucet_funds";
export * from "./deploy_token";
export * from "./deploy_collection";
export * from "./get_balance";
export * from "./get_balance_other";
export * from "./mint_nft";
export * from "./transfer";
export * from "./trade";
export * from "./limit_order";
export * from "./batch_order";
export * from "./cancel_all_orders";
export * from "./withdraw_all";
export * from "./register_domain";
export * from "./resolve_sol_domain";
export * from "./get_primary_domain";
export * from "./launch_pumpfun_token";
export * from "./lend";
export * from "./get_tps";
export * from "./get_token_data";
export * from "./stake_with_jup";
export * from "./stake_with_solayer";
export * from "./fetch_price";
export * from "./send_compressed_airdrop";
export * from "./orca_close_position";
export * from "./orca_create_clmm";
export * from "./orca_create_single_sided_liquidity_pool";
export * from "./orca_fetch_positions";
export * from "./orca_open_centered_position_with_liquidity";
export * from "./orca_open_single_sided_position";
export * from "./get_all_domains_tlds";
export * from "./get_all_registered_all_domains";
export * from "./get_owned_domains_for_tld";
export * from "./get_main_all_domains_domain";
export * from "./get_owned_all_domains";
export * from "./resolve_domain";
export * from "./get_all_domains_tlds";
export * from "./get_all_registered_all_domains";
export * from "./get_owned_domains_for_tld";
export * from "./get_main_all_domains_domain";
export * from "./get_owned_all_domains";
export * from "./resolve_domain";
export * from "./raydium_create_ammV4";
export * from "./raydium_create_clmm";
export * from "./raydium_create_cpmm";
export * from "./openbook_create_market";
export * from "./manifest_create_market";
export * from "./pyth_fetch_price";
export * from "./create_gibwork_task";
export * from "./rock_paper_scissor";
export * from "./create_tiplinks";
export * from "./tensor_trade";
export * from "./adrena";
export * from "./sns";
export * from "./dexscreener";
export * from "./alldomains";
export * from "./flash";
export * from "./gibwork";
export * from "./jupiter";
export * from "./lulo";
export * from "./manifest";
export * from "./solana";
export * from "./agent";
export * from "./metaplex";
export * from "./openbook";
export * from "./orca";
export * from "./pumpfun";
export * from "./pyth";
export * from "./raydium";
export * from "./rugcheck";
export * from "./voltr_deposit_strategy";
export * from "./voltr_withdraw_strategy";
export * from "./voltr_get_position_values";
export * from "./drift";
export * from "./sendarcade";
export * from "./solayer";
export * from "./tensor";
export * from "./3land";
export * from "./tiplink";
export * from "./lightprotocol";
export * from "./squads";
export * from "./helius";
export * from "./voltr/voltr_deposit_strategy";
export * from "./voltr/voltr_withdraw_strategy";
export * from "./voltr/voltr_get_position_values";

View File

@@ -0,0 +1,3 @@
export * from "./fetch_price";
export * from "./stake_with_jup";
export * from "./trade";

View File

@@ -1,5 +1,5 @@
import { VersionedTransaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
/**
* Stake SOL with Jup validator

View File

@@ -1,11 +1,11 @@
import { VersionedTransaction, PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import {
TOKENS,
DEFAULT_OPTIONS,
JUP_API,
JUP_REFERRAL_ADDRESS,
} from "../constants";
} from "../../constants";
import { getMint } from "@solana/spl-token";
/**
* Swap tokens using Jupiter Exchange

View File

@@ -0,0 +1 @@
export * from "./send_compressed_airdrop";

View File

@@ -5,7 +5,7 @@ import {
PublicKey,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import {
buildAndSignTx,
calculateComputeUnitPrice,

View File

@@ -1,61 +0,0 @@
import {
PublicKey,
Transaction,
sendAndConfirmTransaction,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import {
ManifestClient,
WrapperPlaceOrderParamsExternal,
} from "@cks-systems/manifest-sdk";
import { OrderType } from "@cks-systems/manifest-sdk/client/ts/src/wrapper/types/OrderType";
/**
* Place limit orders using Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @param quantity Amount to trade in tokens
* @param side Buy or Sell
* @param price Price in tokens ie. SOL/USDC
* @returns Transaction signature
*/
export async function limitOrder(
agent: SolanaAgentKit,
marketId: PublicKey,
quantity: number,
side: string,
price: number,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const orderParams: WrapperPlaceOrderParamsExternal = {
numBaseTokens: quantity,
tokenPrice: price,
isBid: side === "Buy",
lastValidSlot: 0,
orderType: OrderType.Limit,
clientOrderId: Number(Math.random() * 1000),
};
const depositPlaceOrderIx: TransactionInstruction[] =
await mfxClient.placeOrderWithRequiredDepositIx(
agent.wallet.publicKey,
orderParams,
);
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...depositPlaceOrderIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Limit Order failed: ${error.message}`);
}
}

1
src/tools/lulo/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from "./lend";

View File

@@ -1,5 +1,5 @@
import { VersionedTransaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
/**
* Lend tokens for yields using Lulo

View File

@@ -0,0 +1 @@
export * from "./manifest_trade";

View File

@@ -1,35 +1,158 @@
import {
PublicKey,
Transaction,
sendAndConfirmTransaction,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import {
ManifestClient,
OrderType,
WrapperPlaceOrderParamsExternal,
} from "@cks-systems/manifest-sdk";
import { OrderType } from "@cks-systems/manifest-sdk/client/ts/src/wrapper/types/OrderType";
import {
Keypair,
PublicKey,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { BatchOrderPattern, OrderParams, SolanaAgentKit } from "../../index";
export interface OrderParams {
quantity: number;
side: string;
price: number;
export async function manifestCreateMarket(
agent: SolanaAgentKit,
baseMint: PublicKey,
quoteMint: PublicKey,
): Promise<string[]> {
const marketKeypair: Keypair = Keypair.generate();
const FIXED_MANIFEST_HEADER_SIZE: number = 256;
const createAccountIx: TransactionInstruction = SystemProgram.createAccount({
fromPubkey: agent.wallet.publicKey,
newAccountPubkey: marketKeypair.publicKey,
space: FIXED_MANIFEST_HEADER_SIZE,
lamports: await agent.connection.getMinimumBalanceForRentExemption(
FIXED_MANIFEST_HEADER_SIZE,
),
programId: new PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"),
});
const createMarketIx = ManifestClient["createMarketIx"](
agent.wallet.publicKey,
baseMint,
quoteMint,
marketKeypair.publicKey,
);
const tx: Transaction = new Transaction();
tx.add(createAccountIx);
tx.add(createMarketIx);
const signature = await sendAndConfirmTransaction(agent.connection, tx, [
agent.wallet,
marketKeypair,
]);
return [signature, marketKeypair.publicKey.toBase58()];
}
interface BatchOrderPattern {
side: string;
totalQuantity?: number;
priceRange?: {
min?: number;
max?: number;
};
spacing?: {
type: "percentage" | "fixed";
value: number;
};
numberOfOrders?: number;
individualQuantity?: number;
/**
* Place limit orders using Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @param quantity Amount to trade in tokens
* @param side Buy or Sell
* @param price Price in tokens ie. SOL/USDC
* @returns Transaction signature
*/
export async function limitOrder(
agent: SolanaAgentKit,
marketId: PublicKey,
quantity: number,
side: string,
price: number,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const orderParams: WrapperPlaceOrderParamsExternal = {
numBaseTokens: quantity,
tokenPrice: price,
isBid: side === "Buy",
lastValidSlot: 0,
orderType: OrderType.Limit,
clientOrderId: Number(Math.random() * 1000),
};
const depositPlaceOrderIx: TransactionInstruction[] =
await mfxClient.placeOrderWithRequiredDepositIx(
agent.wallet.publicKey,
orderParams,
);
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...depositPlaceOrderIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Limit Order failed: ${error.message}`);
}
}
/**
* Cancels all orders from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function cancelAllOrders(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const cancelAllOrdersIx = await mfxClient.cancelAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(cancelAllOrdersIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Cancel all orders failed: ${error.message}`);
}
}
/**
* Withdraws all funds from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function withdrawAll(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const withdrawAllIx = await mfxClient.withdrawAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...withdrawAllIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Withdraw all failed: ${error.message}`);
}
}
/**

View File

@@ -1,43 +0,0 @@
import { ManifestClient } from "@cks-systems/manifest-sdk";
import {
Keypair,
PublicKey,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
export async function manifestCreateMarket(
agent: SolanaAgentKit,
baseMint: PublicKey,
quoteMint: PublicKey,
): Promise<string[]> {
const marketKeypair: Keypair = Keypair.generate();
const FIXED_MANIFEST_HEADER_SIZE: number = 256;
const createAccountIx: TransactionInstruction = SystemProgram.createAccount({
fromPubkey: agent.wallet.publicKey,
newAccountPubkey: marketKeypair.publicKey,
space: FIXED_MANIFEST_HEADER_SIZE,
lamports: await agent.connection.getMinimumBalanceForRentExemption(
FIXED_MANIFEST_HEADER_SIZE,
),
programId: new PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"),
});
const createMarketIx = ManifestClient["createMarketIx"](
agent.wallet.publicKey,
baseMint,
quoteMint,
marketKeypair.publicKey,
);
const tx: Transaction = new Transaction();
tx.add(createAccountIx);
tx.add(createMarketIx);
const signature = await sendAndConfirmTransaction(agent.connection, tx, [
agent.wallet,
marketKeypair,
]);
return [signature, marketKeypair.publicKey.toBase58()];
}

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import {
generateSigner,
keypairIdentity,
@@ -9,7 +9,7 @@ import {
mplCore,
ruleSet,
} from "@metaplex-foundation/mpl-core";
import { CollectionOptions, CollectionDeployment } from "../types";
import { CollectionOptions, CollectionDeployment } from "../../types";
import {
fromWeb3JsKeypair,
toWeb3JsPublicKey,

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import { PublicKey } from "@solana/web3.js";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";

View File

@@ -0,0 +1,3 @@
export * from "./deploy_collection";
export * from "./mint_nft";
export * from "./deploy_token";

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";
import { create, mplCore } from "@metaplex-foundation/mpl-core";
import { fetchCollection } from "@metaplex-foundation/mpl-core";
@@ -9,7 +9,7 @@ import {
toWeb3JsPublicKey,
} from "@metaplex-foundation/umi-web3js-adapters";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import { MintCollectionNFTResponse } from "../types";
import { MintCollectionNFTResponse } from "../../types";
/**
* Mint a new NFT as part of an existing collection

View File

@@ -0,0 +1 @@
export * from "./openbook_create_market";

View File

@@ -5,7 +5,7 @@ import {
} from "@raydium-io/raydium-sdk-v2";
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
export async function openbookCreateMarket(
agent: SolanaAgentKit,

6
src/tools/orca/index.ts Normal file
View File

@@ -0,0 +1,6 @@
export * from "./orca_close_position";
export * from "./orca_create_clmm";
export * from "./orca_create_single_sided_liquidity_pool";
export * from "./orca_fetch_positions";
export * from "./orca_open_centered_position_with_liquidity";
export * from "./orca_open_single_sided_position";

View File

@@ -4,15 +4,15 @@ import {
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../../agent";
import { Wallet } from "../../utils/keypair";
import {
ORCA_WHIRLPOOL_PROGRAM_ID,
WhirlpoolContext,
buildWhirlpoolClient,
PDAUtil,
} from "@orca-so/whirlpools-sdk";
import { sendTx } from "../utils/send_tx";
import { sendTx } from "../../utils/send_tx";
import { Percentage } from "@orca-so/common-sdk";
/**

View File

@@ -4,8 +4,8 @@ import {
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../../agent";
import { Wallet } from "../../utils/keypair";
import { Decimal } from "decimal.js";
import {
ORCA_WHIRLPOOL_PROGRAM_ID,
@@ -14,7 +14,7 @@ import {
PoolUtil,
buildWhirlpoolClient,
} from "@orca-so/whirlpools-sdk";
import { sendTx } from "../utils/send_tx";
import { sendTx } from "../../utils/send_tx";
import { FEE_TIERS } from "./orca_create_single_sided_liquidity_pool";
/**

View File

@@ -4,8 +4,9 @@ import {
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { BN, Wallet } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../../agent";
import { BN } from "@coral-xyz/anchor";
import { Wallet } from "../../utils/keypair";
import { Decimal } from "decimal.js";
import {
PDAUtil,
@@ -36,7 +37,7 @@ import {
getAssociatedTokenAddressSync,
TOKEN_2022_PROGRAM_ID,
} from "@solana/spl-token";
import { sendTx } from "../utils/send_tx";
import { sendTx } from "../../utils/send_tx";
/**
* Maps fee tier bps to their corresponding tick spacing values in the Orca Whirlpool protocol.

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
import { Wallet } from "@coral-xyz/anchor";
import {
ORCA_WHIRLPOOL_PROGRAM_ID,

View File

@@ -5,8 +5,8 @@ import {
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../../agent";
import { Wallet } from "../../utils/keypair";
import { Decimal } from "decimal.js";
import {
ORCA_WHIRLPOOL_PROGRAM_ID,
@@ -18,7 +18,7 @@ import {
NO_TOKEN_EXTENSION_CONTEXT,
} from "@orca-so/whirlpools-sdk";
import { sendTx } from "../utils/send_tx";
import { sendTx } from "../../utils/send_tx";
import { Percentage } from "@orca-so/common-sdk";
import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
@@ -26,7 +26,7 @@ import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
* # Opens a Centered Liquidity Position in an Orca Whirlpool
*
* This function opens a centered liquidity position in a specified Orca Whirlpool. The user defines
* a basis point (bps) offset from the cuurent price of the pool to set the lower and upper bounds of the position.
* a basis point (bps) offset from the current price of the pool to set the lower and upper bounds of the position.
* The user also specifies the token mint and the amount to deposit. The required amount of the other token
* is calculated automatically.
*

View File

@@ -1,11 +1,12 @@
import {
Keypair,
PublicKey,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { Wallet } from "@coral-xyz/anchor";
import { SolanaAgentKit } from "../../agent";
import { Wallet } from "../../utils/keypair";
import { Decimal } from "decimal.js";
import {
ORCA_WHIRLPOOL_PROGRAM_ID,
@@ -16,7 +17,7 @@ import {
TokenExtensionContextForPool,
NO_TOKEN_EXTENSION_CONTEXT,
} from "@orca-so/whirlpools-sdk";
import { sendTx } from "../utils/send_tx";
import { sendTx } from "../../utils/send_tx";
import { Percentage } from "@orca-so/common-sdk";
import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
@@ -119,17 +120,17 @@ export async function orcaOpenSingleSidedPosition(
lowerTick,
upperTick,
]);
let txIds: string = "";
let instructions: TransactionInstruction[] = [];
let signers: Keypair[] = [];
if (txBuilderTickArrays !== null) {
const txPayloadTickArrays = await txBuilderTickArrays.build();
const txPayloadTickArraysDecompiled = TransactionMessage.decompile(
(txPayloadTickArrays.transaction as VersionedTransaction).message,
);
const instructions = txPayloadTickArraysDecompiled.instructions;
const signers = txPayloadTickArrays.signers as Keypair[];
const tickArrayTxId = await sendTx(agent, instructions, signers);
txIds += tickArrayTxId + ",";
instructions = instructions.concat(
txPayloadTickArraysDecompiled.instructions,
);
signers = signers.concat(txPayloadTickArrays.signers as Keypair[]);
}
const tokenExtensionCtx: TokenExtensionContextForPool = {
@@ -161,14 +162,13 @@ export async function orcaOpenSingleSidedPosition(
const txPayloadDecompiled = TransactionMessage.decompile(
(txPayload.transaction as VersionedTransaction).message,
);
const instructions = txPayloadDecompiled.instructions;
const signers = txPayload.signers as Keypair[];
instructions = instructions.concat(txPayloadDecompiled.instructions);
signers = signers.concat(txPayload.signers as Keypair[]);
const positionTxId = await sendTx(agent, instructions, signers);
txIds += positionTxId;
const txId = await sendTx(agent, instructions, signers);
return JSON.stringify({
transactionIds: txIds,
transactionIds: txId,
positionMint: positionMint.toString(),
});
} catch (error) {

View File

@@ -0,0 +1 @@
export * from "./launch_pumpfun_token";

View File

@@ -4,7 +4,7 @@ import {
PumpfunLaunchResponse,
PumpFunTokenOptions,
SolanaAgentKit,
} from "../index";
} from "../../index";
async function uploadMetadata(
tokenName: string,

1
src/tools/pyth/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from "./pyth_fetch_price";

View File

@@ -0,0 +1,93 @@
import BN from "bn.js";
import { PythPriceFeedIDItem } from "../../types";
/**
* Fetch the price feed ID for a given token symbol from Pyth
* @param tokenSymbol Token symbol
* @returns Price feed ID
*/
export async function fetchPythPriceFeedID(
tokenSymbol: string,
): Promise<string> {
try {
const stableHermesServiceUrl: string = "https://hermes.pyth.network";
const response = await fetch(
`${stableHermesServiceUrl}/v2/price_feeds?query=${tokenSymbol}&asset_type=crypto`,
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.length === 0) {
throw new Error(`No price feed found for ${tokenSymbol}`);
}
if (data.length > 1) {
const filteredData = data.filter(
(item: PythPriceFeedIDItem) =>
item.attributes.base.toLowerCase() === tokenSymbol.toLowerCase(),
);
if (filteredData.length === 0) {
throw new Error(`No price feed found for ${tokenSymbol}`);
}
return filteredData[0].id;
}
return data[0].id;
} catch (error: any) {
throw new Error(
`Fetching price feed ID from Pyth failed: ${error.message}`,
);
}
}
/**
* Fetch the price of a given price feed from Pyth
* @param priceFeedID Price feed ID
* @returns Latest price value from feed
*
* You can find priceFeedIDs here: https://www.pyth.network/developers/price-feed-ids#stable
*/
export async function fetchPythPrice(feedID: string): Promise<string> {
try {
const stableHermesServiceUrl: string = "https://hermes.pyth.network";
const response = await fetch(
`${stableHermesServiceUrl}/v2/updates/price/latest?ids[]=${feedID}`,
);
const data = await response.json();
const parsedData = data.parsed;
if (parsedData.length === 0) {
throw new Error(`No price data found for ${feedID}`);
}
const price = new BN(parsedData[0].price.price);
const exponent = parsedData[0].price.expo;
if (exponent < 0) {
const adjustedPrice = price.mul(new BN(100));
const divisor = new BN(10).pow(new BN(-exponent));
const scaledPrice = adjustedPrice.div(divisor);
const priceStr = scaledPrice.toString();
const formattedPrice = `${priceStr.slice(0, -2)}.${priceStr.slice(-2)}`;
return formattedPrice.startsWith(".")
? `0${formattedPrice}`
: formattedPrice;
}
const scaledPrice = price.div(new BN(10).pow(new BN(exponent)));
return scaledPrice.toString();
} catch (error: any) {
throw new Error(`Fetching price from Pyth failed: ${error.message}`);
}
}

View File

@@ -1,40 +0,0 @@
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
import BN from "bn.js";
/**
* Fetch the price of a given price feed from Pyth
* @param agent SolanaAgentKit instance
* @param priceFeedID Price feed ID
* @returns Latest price value from feed
*
* You can find priceFeedIDs here: https://www.pyth.network/developers/price-feed-ids#stable
*/
export async function pythFetchPrice(priceFeedID: string): Promise<string> {
// get Hermes service URL from https://docs.pyth.network/price-feeds/api-instances-and-providers/hermes
const stableHermesServiceUrl: string = "https://hermes.pyth.network";
const connection = new PriceServiceConnection(stableHermesServiceUrl);
const feeds = [priceFeedID];
try {
const currentPrice = await connection.getLatestPriceFeeds(feeds);
if (currentPrice === undefined) {
throw new Error("Price data not available for the given token.");
}
if (currentPrice.length === 0) {
throw new Error("Price data not available for the given token.");
}
// get price and exponent from price feed
const price = new BN(currentPrice[0].getPriceUnchecked().price);
const exponent = new BN(currentPrice[0].getPriceUnchecked().expo);
// convert to scaled price
const scaledPrice = price.div(new BN(10).pow(exponent));
return scaledPrice.toString();
} catch (error: any) {
throw new Error(`Fetching price from Pyth failed: ${error.message}`);
}
}

View File

@@ -0,0 +1,3 @@
export * from "./raydium_create_ammV4";
export * from "./raydium_create_clmm";
export * from "./raydium_create_cpmm";

View File

@@ -9,7 +9,7 @@ import {
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
export async function raydiumCreateAmmV4(
agent: SolanaAgentKit,

View File

@@ -7,7 +7,7 @@ import { MintLayout } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import Decimal from "decimal.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
export async function raydiumCreateClmm(
agent: SolanaAgentKit,

View File

@@ -7,7 +7,7 @@ import {
import { MintLayout } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
export async function raydiumCreateCpmm(
agent: SolanaAgentKit,

View File

@@ -0,0 +1 @@
export * from "./rugcheck";

View File

@@ -1,4 +1,4 @@
import { TokenCheck } from "../types";
import { TokenCheck } from "../../types";
const BASE_URL = "https://api.rugcheck.xyz/v1";

View File

@@ -0,0 +1 @@
export * from "./rock_paper_scissor";

View File

@@ -1,5 +1,5 @@
import { sendAndConfirmTransaction, Transaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
export async function rock_paper_scissor(
agent: SolanaAgentKit,

View File

@@ -1,7 +1,7 @@
import { getAllDomains } from "@bonfida/spl-name-service";
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
import { PublicKey } from "@solana/web3.js";
import { getAllDomainsTLDs } from "./get_all_domains_tlds";
import { getAllDomainsTLDs } from "../alldomains/get_all_domains_tlds";
/**
* Get all registered domains across all TLDs

View File

@@ -1,6 +1,6 @@
import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service";
import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../agent";
/**
* Retrieves the primary .sol domain associated with a given Solana public key.

5
src/tools/sns/index.ts Normal file
View File

@@ -0,0 +1,5 @@
export * from "./get_all_registered_all_domains";
export * from "./get_main_all_domains_domain";
export * from "./get_primary_domain";
export * from "./register_domain";
export * from "./resolve_sol_domain";

View File

@@ -1,8 +1,8 @@
import { registerDomainNameV2 } from "@bonfida/spl-name-service";
import { Transaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../agent";
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { TOKENS } from "../constants";
import { TOKENS } from "../../constants";
/**
* Register a .sol domain name using Bonfida Name Service

View File

@@ -1,6 +1,6 @@
import { resolve } from "@bonfida/spl-name-service";
import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
/**
* Resolves a .sol domain to a Solana PublicKey.

View File

@@ -0,0 +1,103 @@
import {
PublicKey,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../../agent";
import {
AccountLayout,
createCloseAccountInstruction,
TOKEN_2022_PROGRAM_ID,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
/**
* Close Empty SPL Token accounts of the agent
* @param agent SolanaAgentKit instance
* @returns transaction signature and total number of accounts closed
*/
export async function closeEmptyTokenAccounts(
agent: SolanaAgentKit,
): Promise<{ signature: string; size: number }> {
try {
const spl_token = await create_close_instruction(agent, TOKEN_PROGRAM_ID);
const token_2022 = await create_close_instruction(
agent,
TOKEN_2022_PROGRAM_ID,
);
const transaction = new Transaction();
const MAX_INSTRUCTIONS = 40; // 40 instructions can be processed in a single transaction without failing
spl_token
.slice(0, Math.min(MAX_INSTRUCTIONS, spl_token.length))
.forEach((instruction) => transaction.add(instruction));
token_2022
.slice(0, Math.max(0, MAX_INSTRUCTIONS - spl_token.length))
.forEach((instruction) => transaction.add(instruction));
const size = spl_token.length + token_2022.length;
if (size === 0) {
return {
signature: "",
size: 0,
};
}
const signature = await agent.connection.sendTransaction(transaction, [
agent.wallet,
]);
return { signature, size };
} catch (error) {
throw new Error(`Error closing empty token accounts: ${error}`);
}
}
/**
* creates the close instuctions of a spl token account
* @param agnet SolanaAgentKit instance
* @param token_program Token Program Id
* @returns close instuction array
*/
async function create_close_instruction(
agent: SolanaAgentKit,
token_program: PublicKey,
): Promise<TransactionInstruction[]> {
const instructions = [];
const ata_accounts = await agent.connection.getTokenAccountsByOwner(
agent.wallet_address,
{ programId: token_program },
"confirmed",
);
const tokens = ata_accounts.value;
const accountExceptions = [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
];
for (let i = 0; i < tokens.length; i++) {
const token_data = AccountLayout.decode(tokens[i].account.data);
if (
token_data.amount === BigInt(0) &&
!accountExceptions.includes(token_data.mint.toString())
) {
const closeInstruction = createCloseAccountInstruction(
ata_accounts.value[i].pubkey,
agent.wallet_address,
agent.wallet_address,
[],
token_program,
);
instructions.push(closeInstruction);
}
}
return instructions;
}

View File

@@ -1,5 +1,5 @@
import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
/**
* Get the balance of SOL or an SPL token for the agent's wallet

View File

@@ -3,7 +3,7 @@ import {
ParsedAccountData,
PublicKey,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
/**
* Get the balance of SOL or an SPL token for the specified wallet address (other than the agent's wallet)

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
export async function getTPS(agent: SolanaAgentKit): Promise<number> {
const perfSamples = await agent.connection.getRecentPerformanceSamples();

View File

@@ -0,0 +1,6 @@
export * from "./get_tps";
export * from "./request_faucet_funds";
export * from "./close_empty_token_accounts";
export * from "./transfer";
export * from "./get_balance";
export * from "./get_balance_other";

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
/**

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import {

View File

@@ -0,0 +1 @@
export * from "./stake_with_solayer";

View File

@@ -1,5 +1,5 @@
import { VersionedTransaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
/**
* Stake SOL with Solayer
@@ -27,7 +27,7 @@ export async function stakeWithSolayer(
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Staking request failed');
throw new Error(errorData.message || "Staking request failed");
}
const data = await response.json();
@@ -61,4 +61,4 @@ export async function stakeWithSolayer(
console.error(error);
throw new Error(`Solayer sSOL staking failed: ${error.message}`);
}
}
}

View File

@@ -0,0 +1,52 @@
import { SolanaAgentKit } from "../../index";
import * as multisig from "@sqds/multisig";
const { Multisig } = multisig.accounts;
/**
* Approves a proposal in a Solana multisig wallet.
*
* @param {SolanaAgentKit} agent - The Solana agent kit instance.
* @param {number | bigint} [transactionIndex] - The index of the transaction to approve. If not provided, the current transaction index will be used.
* @returns {Promise<string>} - A promise that resolves to the transaction ID of the approved proposal.
* @throws {Error} - Throws an error if the approval process fails.
*/
export async function multisig_approve_proposal(
agent: SolanaAgentKit,
transactionIndex?: number | bigint,
): Promise<string> {
try {
const createKey = agent.wallet;
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const multisigInfo = await Multisig.fromAccountAddress(
agent.connection,
multisigPda,
);
const currentTransactionIndex = Number(multisigInfo.transactionIndex);
if (!transactionIndex) {
transactionIndex = BigInt(currentTransactionIndex);
} else if (typeof transactionIndex !== "bigint") {
transactionIndex = BigInt(transactionIndex);
}
// const [proposalPda, proposalBump] = multisig.getProposalPda({
// multisigPda,
// transactionIndex,
// });
const multisigTx = multisig.transactions.proposalApprove({
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
feePayer: agent.wallet.publicKey,
multisigPda,
transactionIndex: transactionIndex,
member: agent.wallet.publicKey,
});
multisigTx.sign([agent.wallet]);
const tx = await agent.connection.sendRawTransaction(
multisigTx.serialize(),
);
return tx;
} catch (error: any) {
throw new Error(`Transfer failed: ${error}`);
}
}

View File

@@ -0,0 +1,61 @@
import * as multisig from "@sqds/multisig";
import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../../index";
/**
* Creates a new Squads multisig account.
*
* @param agent - The SolanaAgentKit instance containing the connection and wallet information.
* @param creator - The public key of the creator who will be a member of the multisig.
* @returns A promise that resolves to the transaction ID of the multisig creation transaction.
*
* @throws Will throw an error if the transaction fails.
*/
export async function create_squads_multisig(
agent: SolanaAgentKit,
creator: PublicKey,
): Promise<string> {
const connection = agent.connection;
const createKey = agent.wallet; // can be any keypair, using the agent wallet as only one multisig is required
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const programConfigPda = multisig.getProgramConfigPda({})[0];
const programConfig =
await multisig.accounts.ProgramConfig.fromAccountAddress(
connection,
programConfigPda,
);
const configTreasury = programConfig.treasury;
const tx = multisig.transactions.multisigCreateV2({
blockhash: (await connection.getLatestBlockhash()).blockhash,
treasury: configTreasury,
createKey: createKey.publicKey,
creator: agent.wallet.publicKey,
multisigPda,
configAuthority: null,
timeLock: 0,
threshold: 2,
rentCollector: null,
members: [
{
key: agent.wallet.publicKey,
permissions: multisig.types.Permissions.all(),
},
{
key: creator,
permissions: multisig.types.Permissions.all(),
},
],
});
tx.sign([agent.wallet, createKey]);
const txId = connection.sendRawTransaction(tx.serialize());
return txId;
}

View File

@@ -0,0 +1,48 @@
import { SolanaAgentKit } from "../../index";
import * as multisig from "@sqds/multisig";
const { Multisig } = multisig.accounts;
/**
* Creates a proposal for a multisig transaction.
*
* @param {SolanaAgentKit} agent - The Solana agent kit instance.
* @param {number | bigint} [transactionIndex] - Optional transaction index. If not provided, the current transaction index will be used.
* @returns {Promise<string>} - The transaction ID of the created proposal.
* @throws {Error} - Throws an error if the proposal creation fails.
*/
export async function multisig_create_proposal(
agent: SolanaAgentKit,
transactionIndex?: number | bigint,
): Promise<string> {
try {
const createKey = agent.wallet;
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const multisigInfo = await Multisig.fromAccountAddress(
agent.connection,
multisigPda,
);
const currentTransactionIndex = Number(multisigInfo.transactionIndex);
if (!transactionIndex) {
transactionIndex = BigInt(currentTransactionIndex);
} else if (typeof transactionIndex !== "bigint") {
transactionIndex = BigInt(transactionIndex);
}
const multisigTx = multisig.transactions.proposalCreate({
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
feePayer: agent.wallet_address,
multisigPda,
transactionIndex,
creator: agent.wallet_address,
});
multisigTx.sign([agent.wallet]);
const tx = await agent.connection.sendRawTransaction(
multisigTx.serialize(),
);
return tx;
} catch (error: any) {
throw new Error(`Transfer failed: ${error}`);
}
}

View File

@@ -0,0 +1,91 @@
import { SolanaAgentKit } from "../../index";
import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
getAssociatedTokenAddress,
createTransferInstruction,
getMint,
createAssociatedTokenAccountInstruction,
} from "@solana/spl-token";
import * as multisig from "@sqds/multisig";
/**
* Transfer SOL or SPL tokens to a multisig vault.
* @param agent SolanaAgentKit instance
* @param amount Amount to transfer
* @param vaultIndex Optional vault index, default is 0
* @param mint Optional mint address for SPL tokens
* @returns Transaction signature
*/
export async function multisig_deposit_to_treasury(
agent: SolanaAgentKit,
amount: number,
vaultIndex?: number,
mint?: PublicKey,
): Promise<string> {
try {
let tx: string;
if (!vaultIndex) {
vaultIndex = 0;
}
const createKey = agent.wallet;
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const [vaultPda] = multisig.getVaultPda({
multisigPda,
index: vaultIndex,
});
const to = vaultPda;
if (!mint) {
// Transfer native SOL
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: agent.wallet_address,
toPubkey: to,
lamports: amount * LAMPORTS_PER_SOL,
}),
);
tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
} else {
// Transfer SPL token
const fromAta = await getAssociatedTokenAddress(
mint,
agent.wallet_address,
);
const transaction = new Transaction();
const toAta = await getAssociatedTokenAddress(mint, to, true);
const toTokenAccountInfo = await agent.connection.getAccountInfo(toAta);
// Create associated token account if it doesn't exist
if (!toTokenAccountInfo) {
transaction.add(
createAssociatedTokenAccountInstruction(
agent.wallet_address,
toAta,
to,
mint,
),
);
}
// Get mint info to determine decimals
const mintInfo = await getMint(agent.connection, mint);
const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);
transaction.add(
createTransferInstruction(
fromAta,
toAta,
agent.wallet_address,
adjustedAmount,
),
);
tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
}
return tx;
} catch (error: any) {
throw new Error(`Transfer failed: ${error}`);
}
}

View File

@@ -0,0 +1,49 @@
import { SolanaAgentKit } from "../../index";
import * as multisig from "@sqds/multisig";
const { Multisig } = multisig.accounts;
/**
* Executes a transaction on the Solana blockchain using the provided agent.
*
* @param {SolanaAgentKit} agent - The Solana agent kit instance containing the wallet and connection.
* @param {number | bigint} [transactionIndex] - Optional transaction index to execute. If not provided, the current transaction index from the multisig account will be used.
* @returns {Promise<string>} - A promise that resolves to the transaction signature string.
* @throws {Error} - Throws an error if the transaction execution fails.
*/
export async function multisig_execute_proposal(
agent: SolanaAgentKit,
transactionIndex?: number | bigint,
): Promise<string> {
try {
const createKey = agent.wallet;
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const multisigInfo = await Multisig.fromAccountAddress(
agent.connection,
multisigPda,
);
const currentTransactionIndex = Number(multisigInfo.transactionIndex);
if (!transactionIndex) {
transactionIndex = BigInt(currentTransactionIndex);
} else if (typeof transactionIndex !== "bigint") {
transactionIndex = BigInt(transactionIndex);
}
const multisigTx = await multisig.transactions.vaultTransactionExecute({
connection: agent.connection,
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
feePayer: agent.wallet.publicKey,
multisigPda,
transactionIndex,
member: agent.wallet.publicKey,
});
multisigTx.sign([agent.wallet]);
const tx = await agent.connection.sendRawTransaction(
multisigTx.serialize(),
);
return tx;
} catch (error: any) {
throw new Error(`Transfer failed: ${error}`);
}
}

View File

@@ -0,0 +1,7 @@
export * from "./create_multisig";
export * from "./create_proposal";
export * from "./approve_proposal";
export * from "./deposit_to_treasury";
export * from "./execute_proposal";
export * from "./reject_proposal";
export * from "./transfer_from_treasury";

View File

@@ -0,0 +1,52 @@
import { SolanaAgentKit } from "../../index";
import * as multisig from "@sqds/multisig";
const { Multisig } = multisig.accounts;
/**
* Rejects a proposal in a Solana multisig setup.
*
* @param agent - The SolanaAgentKit instance containing the wallet and connection.
* @param transactionIndex - Optional. The index of the transaction to reject. If not provided, the current transaction index will be used.
* @returns A promise that resolves to the transaction ID of the rejection transaction.
* @throws Will throw an error if the transaction fails.
*/
export async function multisig_reject_proposal(
agent: SolanaAgentKit,
transactionIndex?: number | bigint,
): Promise<string> {
try {
const createKey = agent.wallet;
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const multisigInfo = await Multisig.fromAccountAddress(
agent.connection,
multisigPda,
);
const currentTransactionIndex = Number(multisigInfo.transactionIndex);
if (!transactionIndex) {
transactionIndex = BigInt(currentTransactionIndex);
} else if (typeof transactionIndex !== "bigint") {
transactionIndex = BigInt(transactionIndex);
}
// const [proposalPda, proposalBump] = multisig.getProposalPda({
// multisigPda,
// transactionIndex,
// });
const multisigTx = multisig.transactions.proposalReject({
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
feePayer: agent.wallet.publicKey,
multisigPda,
transactionIndex: transactionIndex,
member: agent.wallet.publicKey,
});
multisigTx.sign([agent.wallet]);
const tx = await agent.connection.sendRawTransaction(
multisigTx.serialize(),
);
return tx;
} catch (error: any) {
throw new Error(`Transfer failed: ${error}`);
}
}

View File

@@ -0,0 +1,98 @@
import { SolanaAgentKit } from "../../index";
import {
PublicKey,
SystemProgram,
TransactionInstruction,
TransactionMessage,
} from "@solana/web3.js";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
getAssociatedTokenAddress,
createTransferInstruction,
getMint,
} from "@solana/spl-token";
import * as multisig from "@sqds/multisig";
const { Multisig } = multisig.accounts;
/**
* Transfer SOL or SPL tokens to a recipient from a multisig vault.
* @param agent - SolanaAgentKit instance.
* @param amount - Amount to transfer.
* @param to - Recipient's public key.
* @param vaultIndex - Optional vault index, default is 0.
* @param mint - Optional mint address for SPL tokens.
* @returns Transaction signature.
*/
export async function multisig_transfer_from_treasury(
agent: SolanaAgentKit,
amount: number,
to: PublicKey,
vaultIndex: number = 0,
mint?: PublicKey,
): Promise<string> {
try {
let transferInstruction: TransactionInstruction;
const createKey = agent.wallet;
const [multisigPda] = multisig.getMultisigPda({
createKey: createKey.publicKey,
});
const multisigInfo = await Multisig.fromAccountAddress(
agent.connection,
multisigPda,
);
const currentTransactionIndex = Number(multisigInfo.transactionIndex);
const transactionIndex = BigInt(currentTransactionIndex + 1);
const [vaultPda] = multisig.getVaultPda({
multisigPda,
index: vaultIndex,
});
if (!mint) {
// Transfer native SOL
transferInstruction = SystemProgram.transfer({
fromPubkey: agent.wallet_address,
toPubkey: to,
lamports: amount * LAMPORTS_PER_SOL,
});
} else {
// Transfer SPL token
const fromAta = await getAssociatedTokenAddress(mint, vaultPda, true);
const toAta = await getAssociatedTokenAddress(mint, to, true);
const mintInfo = await getMint(agent.connection, mint);
const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);
transferInstruction = createTransferInstruction(
fromAta,
toAta,
agent.wallet_address,
adjustedAmount,
);
}
const transferMessage = new TransactionMessage({
payerKey: vaultPda,
recentBlockhash: (await agent.connection.getLatestBlockhash()).blockhash,
instructions: [transferInstruction],
});
const multisigTx = multisig.transactions.vaultTransactionCreate({
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
feePayer: agent.wallet_address,
multisigPda,
transactionIndex,
creator: agent.wallet_address,
vaultIndex: 0,
ephemeralSigners: 0,
transactionMessage: transferMessage,
});
multisigTx.sign([agent.wallet]);
const tx = await agent.connection.sendRawTransaction(
multisigTx.serialize(),
);
return tx;
} catch (error: any) {
throw new Error(`Transfer failed: ${error}`);
}
}

View File

@@ -0,0 +1 @@
export * from "./tensor_trade";

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
import { TensorSwapSDK } from "@tensor-oss/tensorswap-sdk";
import { PublicKey, Transaction } from "@solana/web3.js";
import { AnchorProvider, Wallet } from "@coral-xyz/anchor";

View File

@@ -13,7 +13,7 @@ import {
getMint,
createAssociatedTokenAccountInstruction,
} from "@solana/spl-token";
import { SolanaAgentKit } from "../index";
import { SolanaAgentKit } from "../../index";
const MINIMUM_SOL_BALANCE = 0.003 * LAMPORTS_PER_SOL;

View File

@@ -0,0 +1 @@
export * from "./create_tiplinks";

3
src/tools/voltr/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from "./voltr_deposit_strategy";
export * from "./voltr_withdraw_strategy";
export * from "./voltr_get_position_values";

View File

@@ -1,5 +1,5 @@
import { TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
import {
PublicKey,
sendAndConfirmTransaction,

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
import { PublicKey } from "@solana/web3.js";
import { VoltrClient } from "@voltr/vault-sdk";

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../../agent";
import {
PublicKey,
sendAndConfirmTransaction,

View File

@@ -1,37 +0,0 @@
import {
PublicKey,
sendAndConfirmTransaction,
Transaction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { ManifestClient } from "@cks-systems/manifest-sdk";
/**
* Withdraws all funds from Manifest
* @param agent SolanaAgentKit instance
* @param marketId Public key for the manifest market
* @returns Transaction signature
*/
export async function withdrawAll(
agent: SolanaAgentKit,
marketId: PublicKey,
): Promise<string> {
try {
const mfxClient = await ManifestClient.getClientForMarket(
agent.connection,
marketId,
agent.wallet,
);
const withdrawAllIx = await mfxClient.withdrawAllIx();
const signature = await sendAndConfirmTransaction(
agent.connection,
new Transaction().add(...withdrawAllIx),
[agent.wallet],
);
return signature;
} catch (error: any) {
throw new Error(`Withdraw all failed: ${error.message}`);
}
}