From 7d2b1260124040c9c948224f834dd4ece8e81745 Mon Sep 17 00:00:00 2001 From: wphan Date: Tue, 7 May 2024 13:09:35 -0700 Subject: [PATCH] misc cli updates, fix tests, clippy --- programs/drift_vaults/src/state/events.rs | 9 +- programs/drift_vaults/src/state/mod.rs | 1 - tests/driftVaults.ts | 4 + tests/testHelpers.ts | 3 + ts/sdk/.gitignore | 1 + ts/sdk/cli/commands/applyProfitShare.ts | 42 +++--- ts/sdk/cli/commands/forceWithdraw.ts | 2 +- ts/sdk/cli/commands/managerUpdateVault.ts | 13 +- ts/sdk/cli/commands/vaultInvariantChecks.ts | 142 +++++++++++++++++--- ts/sdk/cli/utils.ts | 17 ++- ts/sdk/package.json | 2 +- ts/sdk/src/vaultClient.ts | 53 +++++++- ts/sdk/yarn.lock | 25 +--- 13 files changed, 229 insertions(+), 85 deletions(-) diff --git a/programs/drift_vaults/src/state/events.rs b/programs/drift_vaults/src/state/events.rs index 129f7587..1e6a6df0 100644 --- a/programs/drift_vaults/src/state/events.rs +++ b/programs/drift_vaults/src/state/events.rs @@ -35,17 +35,12 @@ pub struct VaultDepositorRecord { pub management_fee_shares: i64, } -#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Eq)] +#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Eq, Default)] pub enum VaultDepositorAction { + #[default] Deposit, WithdrawRequest, CancelWithdrawRequest, Withdraw, FeePayment, } - -impl Default for VaultDepositorAction { - fn default() -> Self { - VaultDepositorAction::Deposit - } -} diff --git a/programs/drift_vaults/src/state/mod.rs b/programs/drift_vaults/src/state/mod.rs index a1db1c83..1adf5018 100644 --- a/programs/drift_vaults/src/state/mod.rs +++ b/programs/drift_vaults/src/state/mod.rs @@ -1,5 +1,4 @@ pub use account_maps::*; -pub use events::*; pub use traits::*; pub use vault::*; pub use vault_depositor::*; diff --git a/tests/driftVaults.ts b/tests/driftVaults.ts index 60b9ac14..5bac3893 100644 --- a/tests/driftVaults.ts +++ b/tests/driftVaults.ts @@ -49,6 +49,10 @@ describe('driftVaults', () => { const adminClient = new AdminClient({ connection, wallet: provider.wallet, + accountSubscription: { + type: 'websocket', + resubTimeoutMs: 30_000, + }, }); const vaultClient = new VaultClient({ diff --git a/tests/testHelpers.ts b/tests/testHelpers.ts index fc4c733b..b4e19565 100644 --- a/tests/testHelpers.ts +++ b/tests/testHelpers.ts @@ -36,6 +36,7 @@ import { QUOTE_PRECISION, User, OracleSource, + MarketStatus, } from '@drift-labs/sdk'; export async function mockOracle( @@ -824,6 +825,7 @@ export async function initializeQuoteSpotMarket( marketIndex, new BN(10 ** 10).mul(QUOTE_PRECISION) ); + await admin.updateSpotMarketStatus(marketIndex, MarketStatus.ACTIVE); } export async function initializeSolSpotMarket( @@ -868,5 +870,6 @@ export async function initializeSolSpotMarket( marketIndex, new BN(10 ** 10).mul(QUOTE_PRECISION) ); + await admin.updateSpotMarketStatus(marketIndex, MarketStatus.ACTIVE); return txSig; } diff --git a/ts/sdk/.gitignore b/ts/sdk/.gitignore index 78fc4889..ff692989 100644 --- a/ts/sdk/.gitignore +++ b/ts/sdk/.gitignore @@ -1,3 +1,4 @@ .env lib node_modules +*.txt diff --git a/ts/sdk/cli/commands/applyProfitShare.ts b/ts/sdk/cli/commands/applyProfitShare.ts index 952b0381..c8abb213 100644 --- a/ts/sdk/cli/commands/applyProfitShare.ts +++ b/ts/sdk/cli/commands/applyProfitShare.ts @@ -1,4 +1,4 @@ -import { ComputeBudgetProgram, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js"; +import { ComputeBudgetProgram, PublicKey, TransactionInstruction, VersionedTransactionResponse } from "@solana/web3.js"; import { OptionValues, Command @@ -21,11 +21,11 @@ export const applyProfitShare = async (program: Command, cmdOpts: OptionValues) const { driftVault, driftClient, - } = await getCommandContext(program, true); + } = await getCommandContext(program, true, true); const vault = await driftVault.getVault(vaultAddress); const vdWithNoWithdrawRequests = await driftVault.getAllVaultDepositorsWithNoWithdrawRequest(vaultAddress); - const vaultEquity = await driftVault.calculateVaultEquity({ vault }); + const vaultEquitySpot = await driftVault.calculateVaultEquityInDepositAsset({ vault }); const spotMarket = driftClient.getSpotMarketAccount(vault.spotMarketIndex); if (!spotMarket) { @@ -36,7 +36,7 @@ export const applyProfitShare = async (program: Command, cmdOpts: OptionValues) const thresholdBN = numberToSafeBN(thresholdNumber, spotMarketPrecision); let pendingProfitShareToRealize = ZERO; const vdWithPendingProfitShare = vdWithNoWithdrawRequests.filter((vd: ProgramAccount) => { - const pendingProfitShares = calculateApplyProfitShare(vd.account, vaultEquity, vault); + const pendingProfitShares = calculateApplyProfitShare(vd.account, vaultEquitySpot, vault); const doRealize = pendingProfitShares.profitShareAmount.gt(thresholdBN); if (doRealize) { pendingProfitShareToRealize = pendingProfitShareToRealize.add(pendingProfitShares.profitShareAmount); @@ -49,7 +49,7 @@ export const applyProfitShare = async (program: Command, cmdOpts: OptionValues) console.log(`${vdWithPendingProfitShare.length}/${vdWithNoWithdrawRequests.length} depositors have pending profit shares above threshold ${cmdOpts.threshold} (${thresholdBN.toString()})`); console.log(`Applying profit share for ${vdWithPendingProfitShare.length} depositors.`); - const chunkSize = 5; + const chunkSize = 1; const ixChunks: Array> = []; for (let i = 0; i < vdWithPendingProfitShare.length; i += chunkSize) { const chunk = vdWithPendingProfitShare.slice(i, i + chunkSize); @@ -65,22 +65,30 @@ export const applyProfitShare = async (program: Command, cmdOpts: OptionValues) const ixs = ixChunks[i]; console.log(`Sending chunk ${i + 1}/${ixChunks.length}`); try { - ixs.unshift(ComputeBudgetProgram.setComputeUnitLimit({ - units: 1_400_000, - })); ixs.unshift(ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: 100, + microLamports: 10000, + })); + ixs.unshift(ComputeBudgetProgram.setComputeUnitLimit({ + units: 170_000, })); - const tx = new Transaction(); - tx.add(...ixs); - const { txSig } = await driftVault.driftClient.sendTransaction( - tx, - [], - driftVault.driftClient.opts - ); + const tx = await driftClient.txSender.getVersionedTransaction(ixs, [], undefined, undefined); + + let attempt = 0; + let txResp: VersionedTransactionResponse | null = null; + while (txResp === null) { + attempt++; + const { txSig } = await driftClient.txSender.sendVersionedTransaction( + tx, + ); + console.log(`[${i}]: https://solscan.io/tx/${txSig} (attempt ${attempt})`); + + await new Promise(resolve => setTimeout(resolve, 1000)); + + txResp = await driftClient.connection.getTransaction(txSig, { commitment: 'confirmed', maxSupportedTransactionVersion: 0 }); + } + console.log(txResp); - console.log(`[${i}]: https://solscan.io/tx/${txSig}`); } catch (e) { console.error(e); diff --git a/ts/sdk/cli/commands/forceWithdraw.ts b/ts/sdk/cli/commands/forceWithdraw.ts index a11b68e9..5ee5f293 100644 --- a/ts/sdk/cli/commands/forceWithdraw.ts +++ b/ts/sdk/cli/commands/forceWithdraw.ts @@ -48,6 +48,6 @@ export const forceWithdraw = async (program: Command, cmdOpts: OptionValues) => driftVault } = await getCommandContext(program, true); - const tx = await driftVault.forceWithdraw(vaultDepositorAddress, false); + const tx = await driftVault.forceWithdraw(vaultDepositorAddress); console.log(`Forced withdraw from vault: ${tx}`); }; \ No newline at end of file diff --git a/ts/sdk/cli/commands/managerUpdateVault.ts b/ts/sdk/cli/commands/managerUpdateVault.ts index 1b925cc9..1c27797e 100644 --- a/ts/sdk/cli/commands/managerUpdateVault.ts +++ b/ts/sdk/cli/commands/managerUpdateVault.ts @@ -1,4 +1,4 @@ -import { ComputeBudgetProgram, PublicKey } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; import { OptionValues, Command @@ -119,19 +119,10 @@ export const managerUpdateVault = async (program: Command, cmdOpts: OptionValues permissioned, }; - const preIxs = [ - ComputeBudgetProgram.setComputeUnitLimit({ - units: 600_000, - }), - ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: 100_000, - }), - ]; - let done = false; while (!done) { try { - const tx = await driftVault.managerUpdateVault(vaultAddress, newParams, preIxs, { maxRetries: 0 }); + const tx = await driftVault.managerUpdateVault(vaultAddress, newParams); console.log(`Updated vault params as vault manager: https://solana.fm/tx/${tx}`); done = true; break; diff --git a/ts/sdk/cli/commands/vaultInvariantChecks.ts b/ts/sdk/cli/commands/vaultInvariantChecks.ts index 010b8808..f425614a 100644 --- a/ts/sdk/cli/commands/vaultInvariantChecks.ts +++ b/ts/sdk/cli/commands/vaultInvariantChecks.ts @@ -4,7 +4,7 @@ import { Command } from "commander"; import { getCommandContext } from "../utils"; -import { BN, QUOTE_PRECISION, ZERO, convertToNumber } from "@drift-labs/sdk"; +import { BN, PERCENTAGE_PRECISION, PRICE_PRECISION, QUOTE_PRECISION, User, ZERO, convertToNumber, decodeName } from "@drift-labs/sdk"; import { calculateApplyProfitShare, } from "../../src/math"; @@ -21,7 +21,8 @@ export const vaultInvariantChecks = async (program: Command, cmdOpts: OptionValu } const { - driftVault + driftVault, + driftClient, } = await getCommandContext(program, true); /* @@ -32,34 +33,58 @@ export const vaultInvariantChecks = async (program: Command, cmdOpts: OptionValu const vault = await driftVault.getVault(vaultAddress); + const spotMarket = driftVault.driftClient.getSpotMarketAccount(vault.spotMarketIndex); + const spotPrecision = new BN(10).pow(new BN(spotMarket!.decimals)); + const spotOracle = driftVault.driftClient.getOracleDataForSpotMarket(vault.spotMarketIndex); + const spotOraclePriceNum = convertToNumber(spotOracle.price, PRICE_PRECISION); + const spotSymbol = decodeName(spotMarket!.name); + + const user = new User({ + // accountSubscription, + driftClient, + userAccountPublicKey: vault.user, + }); + await user.subscribe(); + const vaultEquity = await driftVault.calculateVaultEquity({ vault, }); - const spotMarket = driftVault.driftClient.getSpotMarketAccount(vault.spotMarketIndex); - const spotPrecision = new BN(10).pow(new BN(spotMarket!.decimals)); + const vaultEquitySpot = vaultEquity.mul(spotPrecision).div(spotOracle.price); const allVaultDepositors = await driftVault.getAllVaultDepositors(vaultAddress); const approxSlot = await driftVault.driftClient.connection.getSlot(); const now = Date.now(); - let nonZeroDepositors = allVaultDepositors.filter(vd => vd.account.vaultShares.gt(new BN(0))); - nonZeroDepositors = nonZeroDepositors.sort((a, b) => b.account.vaultShares.cmp(a.account.vaultShares)); + // let nonZeroDepositors = allVaultDepositors.filter(vd => vd.account.vaultShares.gt(new BN(0))); + // nonZeroDepositors = nonZeroDepositors.sort((a, b) => b.account.vaultShares.cmp(a.account.vaultShares)); let totalUserShares = new BN(0); + let totalUserProfits = 0; + let totalUserCumProfits = 0; let totalUserProfitSharePaid = new BN(0); let totalUserProfitShareSharesPaid = new BN(0); let totalPendingProfitShareAmount = new BN(0); let totalPendingProfitShareShares = new BN(0); + let totalUserNetDeposits = new BN(0); console.log(`Vault ${vaultAddress} vd and invariant check at approx slot: ${approxSlot}, date: ${new Date(now).toLocaleString()}`); - console.log(`Depositors with 0 shares: ${allVaultDepositors.length - nonZeroDepositors.length}/${allVaultDepositors.length}`); - for (const vd of nonZeroDepositors) { + let nonZero = 0; + let usersWithoutPendingProfitShare = 0; + let usersWithPendingProfitShare = 0; + const sortedVd = allVaultDepositors.sort((a, b) => b.account.vaultShares.cmp(a.account.vaultShares)); + const checkAuths: Array = []; + for (const vd of sortedVd) { const vdAccount = vd.account as VaultDepositor; + if (vdAccount.vaultShares.gt(new BN(0))) { + nonZero++; + } + totalUserNetDeposits = totalUserNetDeposits.add(vdAccount.netDeposits); + totalUserShares = totalUserShares.add(vdAccount.vaultShares); const vdAuth = vdAccount.authority.toBase58(); const vdPct = vdAccount.vaultShares.toNumber() / vault.totalShares.toNumber(); - console.log(`- ${vdAuth} has ${vdAccount.vaultShares.toNumber()} shares (${(vdPct * 100.0).toFixed(2)}% of vault)`); + console.log(`- ${vdAuth} has ${vdAccount.vaultShares.toNumber()} shares (${(vdPct * 100.0)}% of vault)`); if (!vdAccount.lastWithdrawRequest.shares.eq(new BN(0))) { const withdrawRequested = vdAccount.lastWithdrawRequest.ts.toNumber(); @@ -77,13 +102,47 @@ export const vaultInvariantChecks = async (program: Command, cmdOpts: OptionValu totalUserProfitSharePaid = totalUserProfitSharePaid.add(vdAccount.profitShareFeePaid); totalUserProfitShareSharesPaid = totalUserProfitShareSharesPaid.add(vdAccount.cumulativeProfitShareAmount); - const pendingProfitShares = calculateApplyProfitShare(vdAccount, vaultEquity, vault); + const pendingProfitShares = calculateApplyProfitShare(vdAccount, vaultEquitySpot, vault); + const pendingProfitShareAmtNum = convertToNumber(pendingProfitShares.profitShareAmount, spotPrecision); if (pendingProfitShares.profitShareAmount.gt(ZERO)) { totalPendingProfitShareAmount = totalPendingProfitShareAmount.add(pendingProfitShares.profitShareAmount); totalPendingProfitShareShares = totalPendingProfitShareShares.add(pendingProfitShares.profitShareShares); - console.log(` - pending profit share amount: $${convertToNumber(pendingProfitShares.profitShareAmount, spotPrecision)}`); + console.log(` - pending profit share amount: ${convertToNumber(pendingProfitShares.profitShareAmount, spotPrecision)}`); + usersWithPendingProfitShare++; + } else { + console.log(` - no pending profit share`); + usersWithoutPendingProfitShare++; + } + + const userShareValue = vaultEquitySpot.mul(vdAccount.vaultShares).div(vault.totalShares); + const userShareValueNum = convertToNumber(userShareValue, spotPrecision); + const netDepositsNum = convertToNumber(vdAccount.netDeposits, spotPrecision); + const vdProfits = userShareValueNum - netDepositsNum; + const profitSharePaid = convertToNumber(vdAccount.profitShareFeePaid, spotPrecision); + const cumProfitShareNum = convertToNumber(vdAccount.cumulativeProfitShareAmount, spotPrecision); + totalUserProfits += vdProfits; + totalUserCumProfits += cumProfitShareNum; + console.log(` - net deposits: ${netDepositsNum}`); + console.log(` - vd profit: ${vdProfits}`); + console.log(` - cumProfitshareAmt: ${cumProfitShareNum}`); + console.log(` - profitShareFeePaid: ${convertToNumber(vdAccount.profitShareFeePaid, spotPrecision)}`); + const inclProfitShare = (profitSharePaid + pendingProfitShareAmtNum) / (cumProfitShareNum + pendingProfitShareAmtNum) * 100.0; + console.log(` - pftSharePaidPct (excl pend): ${(profitSharePaid / cumProfitShareNum * 100.0).toFixed(2)}%`); + console.log(` - pftSharePaidPct (incl pend): ${(inclProfitShare).toFixed(2)}% `); + if (inclProfitShare < 29.9 && inclProfitShare > 0) { + console.log(` ^^^ weird: ${inclProfitShare}`); + checkAuths.push(vdAuth); + } + + if (vdAccount.vaultSharesBase !== 0) { + console.log(` - Nonzero vault shares base: ${vdAccount.vaultSharesBase} `); } } + console.log(`Check these auths:\n${checkAuths.join("\n")}`); + console.log(`Depositors with 0 shares: ${allVaultDepositors.length - nonZero} /${allVaultDepositors.length}`); + console.log(`Depositors with pending profit share: ${usersWithPendingProfitShare}`); + console.log(`Depositors without pending profit share: ${usersWithoutPendingProfitShare}`); + console.log(`==== Vault Depositor Shares == vault.user_shares ====`); console.log(`total vd shares: ${totalUserShares.toString()}`); console.log(`total vault usershares: ${vault.userShares.toString()}`); @@ -97,8 +156,27 @@ export const vaultInvariantChecks = async (program: Command, cmdOpts: OptionValu console.log(``); console.log(`==== Pending profit shares to realize ====`); - console.log(`${convertToNumber(totalPendingProfitShareAmount, spotPrecision)}`); - console.log(`csv: ${cmdOpts.csv}`); + const totalPendingProfitShareAmountNum = convertToNumber(totalPendingProfitShareAmount, spotPrecision); + const totalUserProfitSharePaidNum = convertToNumber(totalUserProfitSharePaid, spotPrecision); + console.log(`amount: ${totalPendingProfitShareAmountNum}`); + console.log(`shares: ${totalPendingProfitShareShares.toNumber()}`); + // console.log(`csv: ${cmdOpts.csv}`); + + console.log(``); + console.log(`==== Agg user profit share paid ====`); + console.log(`vd total profit (incl unrealized profit share): ${totalUserProfits}`); + console.log(`vd total cum profits (before pending profit share): ${totalUserCumProfits}`); + console.log(`vd total profit share paid): ${totalUserProfitSharePaidNum}`); + console.log(`vd total pending profit share: ${totalPendingProfitShareAmountNum}`); + console.log(`vd total net deposits: ${convertToNumber(totalUserNetDeposits, spotPrecision)}`); + const driftUserDeposits = user.getUserAccount().totalDeposits; + const driftUserWithdraws = user.getUserAccount().totalWithdraws; + const driftUserSocialLoss = user.getUserAccount().totalSocialLoss; + console.log(`vd drift user net deposits: ${convertToNumber(driftUserDeposits.sub(driftUserWithdraws).sub(driftUserSocialLoss), spotPrecision)}`); + console.log(` vd drift user deps: ${convertToNumber(driftUserDeposits, spotPrecision)}`); + console.log(` vd drift user with: ${convertToNumber(driftUserWithdraws, spotPrecision)}`); + console.log(` vd drift user scls: ${convertToNumber(driftUserSocialLoss, spotPrecision)}`); + console.log(``); console.log(`==== Manager share ====`); @@ -107,8 +185,36 @@ export const vaultInvariantChecks = async (program: Command, cmdOpts: OptionValu const managerSharePct = managerShares.toNumber() / vault.totalShares.toNumber(); const managerShareWithPendingPct = managerShares.add(totalPendingProfitShareShares).toNumber() / vault.totalShares.toNumber(); console.log(` Manager shares: ${managerShares.toString()} (${(managerSharePct * 100.0).toFixed(4)}%)`); - const vaultEquityNum = convertToNumber(vaultEquity, QUOTE_PRECISION); - console.log(`vaultEquity (USDC): $${vaultEquityNum}`); - console.log(`manager share (w/o pending) (USDC): $${managerSharePct * vaultEquityNum}`); - console.log(`manager share (with pending) (USDC): $${managerShareWithPendingPct * vaultEquityNum}`); -}; \ No newline at end of file + const vaultEquityNum = convertToNumber(vaultEquity, spotPrecision); + const vaultEquitySpotNum = convertToNumber(vaultEquitySpot, spotPrecision); + const vaultPnlNum = convertToNumber(user.getTotalAllTimePnl(), QUOTE_PRECISION); + console.log(`vaultEquity (USDC): $${vaultEquityNum}`); + console.log(`vaultEquity (deposit asset): ${vaultEquitySpotNum}`); + const managerValueWoPending = managerSharePct * vaultEquitySpotNum; + const managerValueWithPending = managerShareWithPendingPct * vaultEquitySpotNum; + console.log(`manager share (w/o pending) (deposit asset): ${managerValueWoPending} (share: ${managerValueWoPending / vaultPnlNum * 100.0}%)`); + console.log(`manager share (with pending) (deposit asset): ${managerValueWithPending} (share: ${managerValueWithPending / vaultPnlNum * 100.0}%)`); + + console.log(``); + const profitSharePct = vault.profitShare / PERCENTAGE_PRECISION.toNumber(); + const vdPnlBeforeProfitShare = convertToNumber(totalUserProfitSharePaid, spotPrecision) / profitSharePct; + console.log(`back out vault pnl: (userPnl + managerShareValue): ${totalUserProfits} + ${managerSharePct * vaultEquitySpotNum} = ${totalUserProfits + managerSharePct * vaultEquitySpotNum}`); + console.log(`vaultDepositors pnl (before profit share): ${vdPnlBeforeProfitShare}`); + + console.log(`vault PnL (spot): ${vaultEquitySpotNum - convertToNumber(vault.netDeposits, spotPrecision)}`); + console.log(`vault PnL (USD) ${vaultPnlNum}`); + console.log(`vault PnL (spot) ${vaultPnlNum / spotOraclePriceNum}`); + + console.log(``); + console.log(`==== ${decodeName(vault.name)} Profit Summary ====`); + console.log(`Depositors' total PnL: ${totalUserProfits} ${spotSymbol}`); + console.log(`Depositors' profit share paid to date: ${totalUserProfitSharePaidNum} ${spotSymbol}`); + console.log(`Unrealized profit share: ${totalPendingProfitShareAmountNum} ${spotSymbol}`); + console.log(`Vault manager net deposits: ${convertToNumber(vault.managerNetDeposits, spotPrecision)} ${spotSymbol}`); + console.log(`Vault manager profit share received: ${convertToNumber(vault.managerTotalProfitShare, spotPrecision)} ${spotSymbol}`); + console.log(`Vault manager share value: ${managerValueWithPending} ${spotSymbol} (share of vault: ${managerValueWithPending / vaultPnlNum * 100.0}%)`); + if (spotSymbol !== 'USDC') { + console.log(`Vault manager share value: ${managerValueWithPending * spotOraclePriceNum} USDC`); + } +}; + diff --git a/ts/sdk/cli/utils.ts b/ts/sdk/cli/utils.ts index 10a8685e..fa05c28d 100644 --- a/ts/sdk/cli/utils.ts +++ b/ts/sdk/cli/utils.ts @@ -1,4 +1,4 @@ -import { BASE_PRECISION, BN, DriftClient, OraclePriceData, PRICE_PRECISION, QUOTE_PRECISION, SpotMarketAccount, TEN, User, Wallet, convertToNumber, getSignedTokenAmount, getTokenAmount, loadKeypair } from "@drift-labs/sdk"; +import { BASE_PRECISION, BN, DriftClient, OraclePriceData, PRICE_PRECISION, QUOTE_PRECISION, SpotMarketAccount, TEN, User, Wallet, WhileValidTxSender, convertToNumber, getSignedTokenAmount, getTokenAmount, loadKeypair } from "@drift-labs/sdk"; import { VAULT_PROGRAM_ID, Vault, VaultClient, VaultDepositor, decodeName } from "../src"; import { Command } from "commander"; import { Connection, Keypair } from "@solana/web3.js"; @@ -67,6 +67,7 @@ export async function printVault(slot: number, driftClient: DriftClient, vault: userAccountPublicKey: vault.user, }); await user.subscribe(); + for (const spotPos of user.getActiveSpotPositions()) { const sm = driftClient.getSpotMarketAccount(spotPos.marketIndex)!; const prec = TEN.pow(new BN(sm.decimals)); @@ -74,6 +75,7 @@ export async function printVault(slot: number, driftClient: DriftClient, vault: const bal = getSignedTokenAmount(getTokenAmount(spotPos.scaledBalance, sm, spotPos.balanceType), spotPos.balanceType); console.log(`Spot Position: ${spotPos.marketIndex}, ${convertToNumber(bal, prec)} ${sym}`); } + for (const perpPos of user.getActivePerpPositions()) { console.log(`Perp Position: ${perpPos.marketIndex}, base: ${convertToNumber(perpPos.baseAssetAmount, BASE_PRECISION)}, quote: ${convertToNumber(perpPos.quoteAssetAmount, QUOTE_PRECISION)}`); const upnl = user.getUnrealizedPNL(true, perpPos.marketIndex); @@ -82,7 +84,9 @@ export async function printVault(slot: number, driftClient: DriftClient, vault: console.log(`vaultEquity (${spotSymbol}): ${vaultEquitySpot}`); console.log(`manager share (${spotSymbol}): ${managerSharePct * vaultEquitySpot}`); - console.log(`vault PnL (${spotSymbol}): ${vaultEquitySpot - netDepositsNum}`); + console.log(`vault PnL (${spotSymbol}): ${vaultEquitySpot - netDepositsNum}`); + console.log(`vault PnL (USD) ${convertToNumber(user.getTotalAllTimePnl(), QUOTE_PRECISION)}`); + console.log(`vault PnL (spot) ${convertToNumber(user.getTotalAllTimePnl(), QUOTE_PRECISION) / oraclePriceNum}`); return { managerShares, @@ -134,6 +138,7 @@ export async function getCommandContext(program: Command, needToSign: boolean): const connection = new Connection(opts.url, { commitment: opts.commitment, }); + const driftClient = new DriftClient({ connection, wallet, @@ -143,6 +148,14 @@ export async function getCommandContext(program: Command, needToSign: boolean): skipPreflight: false, preflightCommitment: opts.commitment, }, + txSender: new WhileValidTxSender({ + connection, + wallet, + opts: { + maxRetries: 0, + }, + retrySleep: 1000, + }), }); await driftClient.subscribe(); diff --git a/ts/sdk/package.json b/ts/sdk/package.json index 882f5e62..8cb0b80e 100644 --- a/ts/sdk/package.json +++ b/ts/sdk/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@coral-xyz/anchor": "^0.26.0", - "@drift-labs/competitions-sdk": "0.2.302", + "@drift-labs/competitions-sdk": "0.2.303", "@drift-labs/sdk": "2.82.0-beta.12", "@solana/web3.js": "1.73.2", "commander": "^11.0.0", diff --git a/ts/sdk/src/vaultClient.ts b/ts/sdk/src/vaultClient.ts index a533ee38..18bd1dcd 100644 --- a/ts/sdk/src/vaultClient.ts +++ b/ts/sdk/src/vaultClient.ts @@ -6,6 +6,7 @@ import { getUserAccountPublicKey, getUserAccountPublicKeySync, getUserStatsAccountPublicKey, + TEN, UserMap, } from '@drift-labs/sdk'; import { BorshAccountsCoder, Program, ProgramAccount } from '@coral-xyz/anchor'; @@ -184,7 +185,7 @@ export class VaultClient { /** * * @param vault pubkey - * @returns vault equity, in QUOTE_PRECISION + * @returns vault equity, in USDC */ public async calculateVaultEquity(params: { address?: PublicKey; @@ -208,6 +209,38 @@ export class VaultClient { return netSpotValue.add(unrealizedPnl); } + /** + * + * @param vault pubkey + * @returns vault equity, in spot deposit asset + */ + public async calculateVaultEquityInDepositAsset(params: { + address?: PublicKey; + vault?: Vault; + }): Promise { + let vaultAccount: Vault; + if (params.address !== undefined) { + // @ts-ignore + vaultAccount = await this.program.account.vault.fetch(params.address); + } else if (params.vault !== undefined) { + vaultAccount = params.vault; + } else { + throw new Error('Must supply address or vault'); + } + const vaultEquity = await this.calculateVaultEquity({ + vault: vaultAccount, + }); + const spotMarket = this.driftClient.getSpotMarketAccount( + vaultAccount.spotMarketIndex + ); + const spotOracle = this.driftClient.getOracleDataForSpotMarket( + vaultAccount.spotMarketIndex + ); + const spotPrecision = TEN.pow(new BN(spotMarket!.decimals)); + + return vaultEquity.mul(spotPrecision).div(spotOracle.price); + } + public async initializeVault(params: { name: number[]; spotMarketIndex: number; @@ -526,13 +559,16 @@ export class VaultClient { permissioned: boolean | null; } ): Promise { - return await this.program.methods - .updateVault(params) - .accounts({ + const ix = this.program.instruction.updateVault(params, { + accounts: { vault, manager: this.driftClient.wallet.publicKey, - }) - .rpc(); + }, + }); + return this.createAndSendTxn([ix], { + cuLimit: 600_000, + cuPriceMicroLamports: 10_000, + }); } public async getApplyProfitShareIx( @@ -941,7 +977,10 @@ export class VaultClient { .forceWithdraw() .preInstructions([ ComputeBudgetProgram.setComputeUnitLimit({ - units: 400_000, + units: 500_000, + }), + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: 50_000, }), ]) .accounts(accounts) diff --git a/ts/sdk/yarn.lock b/ts/sdk/yarn.lock index eae10a58..68ef82ed 100644 --- a/ts/sdk/yarn.lock +++ b/ts/sdk/yarn.lock @@ -109,13 +109,13 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@drift-labs/competitions-sdk@0.2.302": - version "0.2.302" - resolved "https://registry.yarnpkg.com/@drift-labs/competitions-sdk/-/competitions-sdk-0.2.302.tgz#dc1172ea99bce283130f88ecec660b2cc46d8ae5" - integrity sha512-P9Wc+SUuaDkZpdWnd+qcRyppLhQvPxq4urNmK1+x2q0BVzXSQLDqfmlrhnY3+/l6qk4Tby17kw6oWONCuCn8Ig== +"@drift-labs/competitions-sdk@0.2.303": + version "0.2.303" + resolved "https://registry.yarnpkg.com/@drift-labs/competitions-sdk/-/competitions-sdk-0.2.303.tgz#8d23b6d7899a0598f53711c965fba0295afdd93a" + integrity sha512-c3WkxLT2zvHze6WUXlX9JsBGiQ1hHgSBDz5nBsRTEYLWPiHgswv7TQvQ3sU831Ux+Mnkk+prsr9KJPOKDK7yDg== dependencies: "@coral-xyz/anchor" "^0.26.0" - "@drift-labs/sdk" "2.82.0-beta.11" + "@drift-labs/sdk" "2.82.0-beta.12" "@solana/web3.js" "1.73.2" "@switchboard-xyz/solana.js" "^2.7.1" cerializr "^3.1.4" @@ -124,21 +124,6 @@ ts-node "^10.9.1" typescript "^5.1.6" -"@drift-labs/sdk@2.82.0-beta.11": - version "2.82.0-beta.11" - resolved "https://registry.yarnpkg.com/@drift-labs/sdk/-/sdk-2.82.0-beta.11.tgz#3d233db9ab4aefd61048f2e044c891dd409f82c5" - integrity sha512-bY/6OW7gzmfe3WoF5y+CR2e/8svffIfuY3gksVleazRt2d+Ll2Y8Czy+L4+djU0jyFMeME0WQ/8tDmxApwdXZA== - dependencies: - "@coral-xyz/anchor" "0.28.1-beta.2" - "@ellipsis-labs/phoenix-sdk" "^1.4.2" - "@project-serum/serum" "^0.13.38" - "@pythnetwork/client" "2.5.3" - "@solana/spl-token" "^0.3.7" - "@solana/web3.js" "1.91.7" - strict-event-emitter-types "^2.0.0" - uuid "^8.3.2" - zstddec "^0.1.0" - "@drift-labs/sdk@2.82.0-beta.12": version "2.82.0-beta.12" resolved "https://registry.yarnpkg.com/@drift-labs/sdk/-/sdk-2.82.0-beta.12.tgz#851236430458a9be43d625f88d5ac636605a6a2a"