Skip to content

program: add admin_deposit #1591

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 8 commits into from
Apr 29, 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
182 changes: 180 additions & 2 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ use crate::math::spot_balance::get_token_amount;
use crate::math::spot_withdraw::validate_spot_market_vault_amount;
use crate::math::{amm, bn};
use crate::optional_accounts::get_token_mint;
use crate::state::events::{CurveRecord, SpotMarketVaultDepositRecord};
use crate::state::events::{
CurveRecord, DepositDirection, DepositExplanation, DepositRecord, SpotMarketVaultDepositRecord,
};
use crate::state::fulfillment_params::openbook_v2::{
OpenbookV2Context, OpenbookV2FulfillmentConfig,
};
Expand All @@ -54,7 +56,7 @@ use crate::state::paused_operations::{InsuranceFundOperation, PerpOperation, Spo
use crate::state::perp_market::{
ContractTier, ContractType, InsuranceClaim, MarketStatus, PerpMarket, PoolBalance, AMM,
};
use crate::state::perp_market_map::get_writable_perp_market_set;
use crate::state::perp_market_map::{get_writable_perp_market_set, MarketSet};
use crate::state::protected_maker_mode_config::ProtectedMakerModeConfig;
use crate::state::pyth_lazer_oracle::{PythLazerOracle, PYTH_LAZER_ORACLE_SEED};
use crate::state::spot_market::{
Expand Down Expand Up @@ -4438,6 +4440,156 @@ pub fn handle_update_protected_maker_mode_config(
Ok(())
}

#[access_control(
deposit_not_paused(&ctx.accounts.state)
)]
pub fn handle_admin_deposit<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, AdminDeposit<'info>>,
market_index: u16,
amount: u64,
) -> Result<()> {
let user_key = ctx.accounts.user.key();
let user = &mut load_mut!(ctx.accounts.user)?;

let state = &ctx.accounts.state;
let clock = Clock::get()?;
let now = clock.unix_timestamp;
let slot = clock.slot;

let remaining_accounts_iter = &mut ctx.remaining_accounts.iter().peekable();
let AccountMaps {
perp_market_map: _,
spot_market_map,
mut oracle_map,
} = load_maps(
remaining_accounts_iter,
&MarketSet::new(),
&get_writable_spot_market_set(market_index),
clock.slot,
Some(state.oracle_guard_rails),
)?;

let mint = get_token_mint(remaining_accounts_iter)?;

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

validate!(!user.is_bankrupt(), ErrorCode::UserBankrupt)?;

let mut spot_market = spot_market_map.get_ref_mut(&market_index)?;
let oracle_price_data = *oracle_map.get_price_data(&spot_market.oracle_id())?;

validate!(
user.pool_id == spot_market.pool_id,
ErrorCode::InvalidPoolId,
"user pool id ({}) != market pool id ({})",
user.pool_id,
spot_market.pool_id
)?;

validate!(
!matches!(spot_market.status, MarketStatus::Initialized),
ErrorCode::MarketBeingInitialized,
"Market is being initialized"
)?;

controller::spot_balance::update_spot_market_cumulative_interest(
&mut spot_market,
Some(&oracle_price_data),
now,
)?;

let position_index = user.force_get_spot_position_index(spot_market.market_index)?;

// if reduce only, have to compare ix amount to current borrow amount
let amount = if (spot_market.is_reduce_only())
&& user.spot_positions[position_index].balance_type == SpotBalanceType::Borrow
{
user.spot_positions[position_index]
.get_token_amount(&spot_market)?
.cast::<u64>()?
.min(amount)
} else {
amount
};

let total_deposits_after = user.total_deposits;
let total_withdraws_after = user.total_withdraws;

let spot_position = &mut user.spot_positions[position_index];
controller::spot_position::update_spot_balances_and_cumulative_deposits(
amount as u128,
&SpotBalanceType::Deposit,
&mut spot_market,
spot_position,
false,
None,
)?;

let token_amount = spot_position.get_token_amount(&spot_market)?;
if token_amount == 0 {
validate!(
spot_position.scaled_balance == 0,
ErrorCode::InvalidSpotPosition,
"deposit left user with invalid position. scaled balance = {} token amount = {}",
spot_position.scaled_balance,
token_amount
)?;
}

if spot_position.balance_type == SpotBalanceType::Deposit && spot_position.scaled_balance > 0 {
validate!(
matches!(spot_market.status, MarketStatus::Active),
ErrorCode::MarketActionPaused,
"spot_market not active",
)?;
}

drop(spot_market);

user.update_last_active_slot(slot);

let spot_market = &mut spot_market_map.get_ref_mut(&market_index)?;

controller::token::receive(
&ctx.accounts.token_program,
&ctx.accounts.admin_token_account,
&ctx.accounts.spot_market_vault,
&ctx.accounts.admin,
amount,
&mint,
)?;
ctx.accounts.spot_market_vault.reload()?;
validate_spot_market_vault_amount(spot_market, ctx.accounts.spot_market_vault.amount)?;

let deposit_record_id = get_then_update_id!(spot_market, next_deposit_record_id);
let oracle_price = oracle_price_data.price;
let deposit_record = DepositRecord {
ts: now,
deposit_record_id,
user_authority: user.authority,
user: user_key,
direction: DepositDirection::Deposit,
amount,
oracle_price,
market_deposit_balance: spot_market.deposit_balance,
market_withdraw_balance: spot_market.borrow_balance,
market_cumulative_deposit_interest: spot_market.cumulative_deposit_interest,
market_cumulative_borrow_interest: spot_market.cumulative_borrow_interest,
total_deposits_after,
total_withdraws_after,
market_index,
explanation: DepositExplanation::Reward,
transfer_user: None,
};
emit!(deposit_record);

spot_market.validate_max_token_deposits_and_borrows(false)?;

Ok(())
}

#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(mut)]
Expand Down Expand Up @@ -5163,3 +5315,29 @@ pub struct UpdateProtectedMakerModeConfig<'info> {
)]
pub state: Box<Account<'info, State>>,
}

#[derive(Accounts)]
#[instruction(market_index: u16,)]
pub struct AdminDeposit<'info> {
pub state: Box<Account<'info, State>>,
#[account(mut)]
pub user: AccountLoader<'info, User>,
#[account(
mut,
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
)]
pub admin: Signer<'info>,
#[account(
mut,
seeds = [b"spot_market_vault".as_ref(), market_index.to_le_bytes().as_ref()],
bump,
)]
pub spot_market_vault: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
constraint = &spot_market_vault.mint.eq(&admin_token_account.mint),
token::authority = admin.key()
)]
pub admin_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
pub token_program: Interface<'info, TokenInterface>,
}
8 changes: 8 additions & 0 deletions programs/drift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,14 @@ pub mod drift {
) -> Result<()> {
handle_update_protected_maker_mode_config(ctx, max_users, reduce_only, current_users)
}

pub fn admin_deposit<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, AdminDeposit<'info>>,
market_index: u16,
amount: u64,
) -> Result<()> {
handle_admin_deposit(ctx, market_index, amount)
}
}

#[cfg(not(feature = "no-entrypoint"))]
Expand Down
1 change: 1 addition & 0 deletions programs/drift/src/state/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub enum DepositExplanation {
Transfer,
Borrow,
RepayBorrow,
Reward,
}

#[event]
Expand Down
48 changes: 48 additions & 0 deletions sdk/src/adminClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4224,4 +4224,52 @@ export class AdminClient extends DriftClient {
}
);
}

public async adminDeposit(
marketIndex: number,
amount: BN,
depositUserAccount: PublicKey,
adminTokenAccount?: PublicKey
): Promise<TransactionSignature> {
const ix = await this.getAdminDepositIx(
marketIndex,
amount,
depositUserAccount,
adminTokenAccount
);
const tx = await this.buildTransaction(ix);
const { txSig } = await this.sendTransaction(tx, [], this.opts);
return txSig;
}

public async getAdminDepositIx(
marketIndex: number,
amount: BN,
depositUserAccount: PublicKey,
adminTokenAccount?: PublicKey
): Promise<TransactionInstruction> {
const state = await this.getStatePublicKey();

const spotMarketVault = await getSpotMarketVaultPublicKey(
this.program.programId,
marketIndex
);

return this.program.instruction.adminDeposit(marketIndex, amount, {
remainingAccounts: this.getRemainingAccounts({
userAccounts: [],
writableSpotMarketIndexes: [marketIndex],
}),
accounts: {
state,
user: depositUserAccount,
admin: this.wallet.publicKey,
spotMarketVault,
adminTokenAccount:
adminTokenAccount ??
(await this.getAssociatedTokenAccount(marketIndex)),
tokenProgram: TOKEN_PROGRAM_ID,
},
});
}
}
48 changes: 48 additions & 0 deletions sdk/src/idl/drift.json
Original file line number Diff line number Diff line change
Expand Up @@ -7096,6 +7096,51 @@
}
}
]
},
{
"name": "adminDeposit",
"accounts": [
{
"name": "state",
"isMut": false,
"isSigner": false
},
{
"name": "user",
"isMut": true,
"isSigner": false
},
{
"name": "admin",
"isMut": true,
"isSigner": true
},
{
"name": "spotMarketVault",
"isMut": true,
"isSigner": false
},
{
"name": "adminTokenAccount",
"isMut": true,
"isSigner": false
},
{
"name": "tokenProgram",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "marketIndex",
"type": "u16"
},
{
"name": "amount",
"type": "u64"
}
]
}
],
"accounts": [
Expand Down Expand Up @@ -11398,6 +11443,9 @@
},
{
"name": "RepayBorrow"
},
{
"name": "Reward"
}
]
}
Expand Down
1 change: 1 addition & 0 deletions sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ export class DepositExplanation {
static readonly TRANSFER = { transfer: {} };
static readonly BORROW = { borrow: {} };
static readonly REPAY_BORROW = { repayBorrow: {} };
static readonly REWARD = { reward: {} };
}

export class SettlePnlExplanation {
Expand Down
4 changes: 2 additions & 2 deletions test-scripts/single-anchor-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ fi

export ANCHOR_WALLET=~/.config/solana/id.json

test_files=(fuelSweep.ts)
test_files=(adminDeposit.ts)

for test_file in ${test_files[@]}; do
ts-mocha -t 300000 ./tests/${test_file}
done
done
Loading