Skip to content

deposit/borrow working and changing positions #1652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 175 additions & 3 deletions programs/drift/src/instructions/lp_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};

use crate::{
controller::{
self,
spot_balance::update_spot_balances,
lp,
token::{burn_tokens, mint_tokens},
},
error::ErrorCode,
get_then_update_id,
ids::admin_hot_wallet,
math::{
self,
casting::Cast,
constants::{
BASE_PRECISION_I128, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64,
Expand All @@ -19,7 +23,7 @@ use crate::{
oracle::{is_oracle_valid_for_action, oracle_validity, DriftAction},
safe_math::SafeMath,
},
msg,
math_error, msg, safe_decrement, safe_increment,
state::{
constituent_map::{ConstituentMap, ConstituentSet},
events::{LPMintRedeemRecord, LPSwapRecord},
Expand All @@ -29,9 +33,9 @@ use crate::{
},
oracle::OraclePriceData,
oracle_map::OracleMap,
perp_market::{AmmCacheFixed, CacheInfo, AMM_POSITIONS_CACHE},
perp_market::{AmmCacheFixed, CacheInfo, MarketStatus, AMM_POSITIONS_CACHE},
perp_market_map::MarketSet,
spot_market::SpotMarket,
spot_market::{SpotBalanceType, SpotMarket},
spot_market_map::get_writable_spot_market_set_from_many,
state::State,
user::MarketType,
Expand Down Expand Up @@ -1058,6 +1062,174 @@ pub fn handle_update_constituent_oracle_info<'c: 'info, 'info>(
Ok(())
}

pub fn handle_deposit_to_program_vault<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, DepositWithdrawProgramVault<'info>>,
amount: u64,
) -> Result<()> {
let clock = Clock::get()?;

let mut spot_market = ctx.accounts.spot_market.load_mut()?;
let mut constituent = ctx.accounts.constituent.load_mut()?;
let spot_market_vault = &ctx.accounts.spot_market_vault;
let oracle_id = spot_market.oracle_id();
let mut oracle_map = OracleMap::load_one(
&ctx.accounts.oracle,
clock.slot,
Some(ctx.accounts.state.oracle_guard_rails),
)?;

if amount == 0 {
return Err(ErrorCode::InsufficientDeposit.into());
}

let oracle_data = oracle_map.get_price_data(&oracle_id)?;
let oracle_data_slot = clock.slot - oracle_data.delay.max(0i64).cast::<u64>()?;
if constituent.last_oracle_slot < oracle_data_slot {
constituent.last_oracle_price = oracle_data.price;
constituent.last_oracle_slot = oracle_data_slot;
}

controller::spot_balance::update_spot_market_cumulative_interest(
&mut spot_market,
Some(&oracle_data),
clock.unix_timestamp,
)?;

controller::token::send_from_program_vault(
&ctx.accounts.token_program,
&ctx.accounts.constituent_token_account,
&spot_market_vault,
&ctx.accounts.drift_signer,
ctx.accounts.state.signer_nonce,
amount,
&Some(*ctx.accounts.mint.clone()),
)?;

// Adjust BLPosition for the new deposits
let spot_position = &mut constituent.spot_balance;
update_spot_balances(
amount as u128,
&SpotBalanceType::Deposit,
&mut spot_market,
spot_position,
false,
)?;

safe_increment!(spot_position.cumulative_deposits, amount.cast()?);

ctx.accounts.spot_market_vault.reload()?;
spot_market.validate_max_token_deposits_and_borrows(false)?;

Ok(())
}

pub fn handle_withdraw_from_program_vault<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, DepositWithdrawProgramVault<'info>>,
amount: u64,
) -> Result<()> {
let state = &ctx.accounts.state;
let clock = Clock::get()?;

let mut spot_market = ctx.accounts.spot_market.load_mut()?;
let mut constituent = ctx.accounts.constituent.load_mut()?;
let spot_market_vault = &ctx.accounts.spot_market_vault;
let oracle_id = spot_market.oracle_id();
let mut oracle_map = OracleMap::load_one(
&ctx.accounts.oracle,
clock.slot,
Some(ctx.accounts.state.oracle_guard_rails),
)?;

if amount == 0 {
return Err(ErrorCode::InsufficientDeposit.into());
}

let oracle_data = oracle_map.get_price_data(&oracle_id)?;
let oracle_data_slot = clock.slot - oracle_data.delay.max(0i64).cast::<u64>()?;
if constituent.last_oracle_slot < oracle_data_slot {
constituent.last_oracle_price = oracle_data.price;
constituent.last_oracle_slot = oracle_data_slot;
}

controller::spot_balance::update_spot_market_cumulative_interest(
&mut spot_market,
Some(&oracle_data),
clock.unix_timestamp,
)?;

controller::token::send_from_program_vault(
&ctx.accounts.token_program,
&spot_market_vault,
&ctx.accounts.constituent_token_account,
&ctx.accounts.drift_signer,
state.signer_nonce,
amount,
&Some(*ctx.accounts.mint.clone()),
)?;

// Adjust BLPosition for the new deposits
let spot_position = &mut constituent.spot_balance;
update_spot_balances(
amount as u128,
&SpotBalanceType::Borrow,
&mut spot_market,
spot_position,
true,
)?;

safe_decrement!(spot_position.cumulative_deposits, amount.cast()?);

ctx.accounts.spot_market_vault.reload()?;
math::spot_withdraw::validate_spot_market_vault_amount(
&spot_market,
ctx.accounts.spot_market_vault.amount,
)?;

spot_market.validate_max_token_deposits_and_borrows(true)?;

Ok(())
}

#[derive(Accounts)]
pub struct DepositWithdrawProgramVault<'info> {
pub state: Box<Account<'info, State>>,
#[account(
mut,
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
)]
pub admin: Signer<'info>,
/// CHECK: program signer
pub drift_signer: AccountInfo<'info>,
#[account(mut)]
pub constituent: AccountLoader<'info, Constituent>,
#[account(
mut,
address = constituent.load()?.token_vault,
constraint = &constituent.load()?.mint.eq(&constituent_token_account.mint),
token::authority = drift_signer
)]
pub constituent_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
owner = crate::ID,
constraint = spot_market.load()?.market_index == constituent.load()?.spot_market_index
)]
pub spot_market: AccountLoader<'info, SpotMarket>,
#[account(
mut,
address = spot_market.load()?.vault,
token::authority = drift_signer,
)]
pub spot_market_vault: Box<InterfaceAccount<'info, TokenAccount>>,
pub token_program: Interface<'info, TokenInterface>,
#[account(
address = spot_market.load()?.mint,
)]
pub mint: Box<InterfaceAccount<'info, Mint>>,
/// CHECK: checked when loading oracle in oracle map
pub oracle: AccountInfo<'info>,
}

#[derive(Accounts)]
pub struct UpdateConstituentOracleInfo<'info> {
pub state: Box<Account<'info, State>>,
Expand Down
14 changes: 14 additions & 0 deletions programs/drift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,20 @@ pub mod drift {
) -> Result<()> {
handle_update_constituent_oracle_info(ctx)
}

pub fn deposit_to_program_vault<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, DepositWithdrawProgramVault<'info>>,
amount: u64,
) -> Result<()> {
handle_deposit_to_program_vault(ctx, amount)
}

pub fn withdraw_from_program_vault<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, DepositWithdrawProgramVault<'info>>,
amount: u64,
) -> Result<()> {
handle_withdraw_from_program_vault(ctx, amount)
}
}

#[cfg(not(feature = "no-entrypoint"))]
Expand Down
101 changes: 101 additions & 0 deletions sdk/src/adminClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5025,4 +5025,105 @@ export class AdminClient extends DriftClient {

return { ixs, lookupTables };
}

public async depositWithdrawToProgramVault(
lpPoolName: number[],
depositMarketIndex: number,
borrowMarketIndex: number,
amountToDeposit: BN,
amountToBorrow: BN
): Promise<TransactionSignature> {
const { depositIx, withdrawIx } =
await this.getDepositWithdrawToProgramVaultIxs(
lpPoolName,
depositMarketIndex,
borrowMarketIndex,
amountToDeposit,
amountToBorrow
);

const tx = await this.buildTransaction([depositIx, withdrawIx]);
const { txSig } = await this.sendTransaction(tx, [], this.opts);
return txSig;
}

public async getDepositWithdrawToProgramVaultIxs(
lpPoolName: number[],
depositMarketIndex: number,
borrowMarketIndex: number,
amountToDeposit: BN,
amountToBorrow: BN
): Promise<{
depositIx: TransactionInstruction;
withdrawIx: TransactionInstruction;
}> {
const lpPool = getLpPoolPublicKey(this.program.programId, lpPoolName);
const depositSpotMarket = this.getSpotMarketAccount(depositMarketIndex);
const withdrawSpotMarket = this.getSpotMarketAccount(borrowMarketIndex);

const depositTokenProgram =
this.getTokenProgramForSpotMarket(depositSpotMarket);
const withdrawTokenProgram =
this.getTokenProgramForSpotMarket(withdrawSpotMarket);

const depositConstituent = getConstituentPublicKey(
this.program.programId,
lpPool,
depositMarketIndex
);
const withdrawConstituent = getConstituentPublicKey(
this.program.programId,
lpPool,
borrowMarketIndex
);

const depositConstituentTokenAccount = getConstituentVaultPublicKey(
this.program.programId,
lpPool,
depositMarketIndex
);
const withdrawConstituentTokenAccount = getConstituentVaultPublicKey(
this.program.programId,
lpPool,
borrowMarketIndex
);

const depositIx = this.program.instruction.depositToProgramVault(
amountToDeposit,
{
accounts: {
state: await this.getStatePublicKey(),
admin: this.wallet.publicKey,
constituent: depositConstituent,
constituentTokenAccount: depositConstituentTokenAccount,
spotMarket: depositSpotMarket.pubkey,
spotMarketVault: depositSpotMarket.vault,
tokenProgram: depositTokenProgram,
mint: depositSpotMarket.mint,
driftSigner: getDriftSignerPublicKey(this.program.programId),
oracle: depositSpotMarket.oracle,
},
}
);

const withdrawIx = this.program.instruction.withdrawFromProgramVault(
amountToBorrow,
{
accounts: {
state: await this.getStatePublicKey(),
admin: this.wallet.publicKey,
constituent: withdrawConstituent,
constituentTokenAccount: withdrawConstituentTokenAccount,
spotMarket: withdrawSpotMarket.pubkey,
spotMarketVault: withdrawSpotMarket.vault,
tokenProgram: withdrawTokenProgram,
mint: withdrawSpotMarket.mint,
driftSigner: getDriftSignerPublicKey(this.program.programId),
oracle: withdrawSpotMarket.oracle,
},
}
);

return { depositIx, withdrawIx };
}
}
Loading