Skip to content

Commit

Permalink
program: add deposit into spot market vault (#1159)
Browse files Browse the repository at this point in the history
* program: add token 2022 support

* add test

* add test for swap

* try fix order race condition

* prettify

* do transfer_checked

* add token mint logic to drift client

* add mock pyusd for devent

* program: add deposit_into_spot_market_vault

* CHANGELOG

* program: fix build with InterfaceAccount

* check deposits paused, check max token deposits, check signer is hot wallet

* some tweaks for test

* CHANGELOG
  • Loading branch information
crispheaney authored Jul 30, 2024
1 parent 095086d commit bf1d2be
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- program: add deposit into spot market vault ([#1159](https://github.com/drift-labs/protocol-v2/pull/1159))
- program: add liquidation via fill ([#1106](https://github.com/drift-labs/protocol-v2/pull/1106))
- program: add switchboard on demand integration ([#1154](https://github.com/drift-labs/protocol-v2/pull/1154))
- program: add support for token 2022 ([#1125](https://github.com/drift-labs/protocol-v2/pull/1125))
Expand Down
112 changes: 110 additions & 2 deletions programs/drift/src/instructions/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ use crate::math::orders::is_multiple_of_step_size;
use crate::math::repeg::get_total_fee_lower_bound;
use crate::math::safe_math::SafeMath;
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::math_error;
use crate::optional_accounts::get_token_mint;
use crate::state::events::CurveRecord;
use crate::state::events::{CurveRecord, SpotMarketVaultDepositRecord};
use crate::state::fulfillment_params::openbook_v2::{
OpenbookV2Context, OpenbookV2FulfillmentConfig,
};
Expand Down Expand Up @@ -68,6 +68,7 @@ use crate::{get_then_update_id, EPOCH_DURATION};
use crate::{load, FEE_ADJUSTMENT_MAX};
use crate::{load_mut, PTYH_PRICE_FEED_SEED_PREFIX};
use crate::{math, safe_decrement, safe_increment};
use crate::{math_error, SPOT_BALANCE_PRECISION};

pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {
let (drift_signer, drift_signer_nonce) =
Expand Down Expand Up @@ -1673,6 +1674,91 @@ pub fn handle_deposit_into_perp_market_fee_pool<'c: 'info, 'info>(
Ok(())
}

#[access_control(
deposit_not_paused(&ctx.accounts.state)
spot_market_valid(&ctx.accounts.spot_market)
)]
pub fn handle_deposit_into_spot_market_vault<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, DepositIntoSpotMarketVault<'info>>,
amount: u64,
) -> Result<()> {
let spot_market = &mut load_mut!(ctx.accounts.spot_market)?;

validate!(
!spot_market.is_operation_paused(SpotOperation::Deposit),
ErrorCode::DefaultError,
"spot market deposits paused"
)?;

let remaining_accounts_iter = &mut ctx.remaining_accounts.iter().peekable();

let mint = get_token_mint(remaining_accounts_iter)?;

msg!(
"depositing {} into spot market {} vault",
amount,
spot_market.market_index
);

let deposit_token_amount_before = spot_market.get_deposits()?;

let deposit_token_amount_after = deposit_token_amount_before.safe_add(amount.cast()?)?;

validate!(
deposit_token_amount_after > deposit_token_amount_before,
ErrorCode::DefaultError,
"new_deposit_token_amount ({}) <= deposit_token_amount ({})",
deposit_token_amount_after,
deposit_token_amount_before
)?;

let token_precision = spot_market.get_precision();

let cumulative_deposit_interest_before = spot_market.cumulative_deposit_interest;

let cumulative_deposit_interest_after = deposit_token_amount_after
.safe_mul(SPOT_CUMULATIVE_INTEREST_PRECISION)?
.safe_div(spot_market.deposit_balance)?
.safe_mul(SPOT_BALANCE_PRECISION)?
.safe_div(token_precision.cast()?)?;

validate!(
cumulative_deposit_interest_after > cumulative_deposit_interest_before,
ErrorCode::DefaultError,
"cumulative_deposit_interest_after ({}) <= cumulative_deposit_interest_before ({})",
cumulative_deposit_interest_after,
cumulative_deposit_interest_before
)?;

spot_market.cumulative_deposit_interest = cumulative_deposit_interest_after;

controller::token::receive(
&ctx.accounts.token_program,
&ctx.accounts.source_vault,
&ctx.accounts.spot_market_vault,
&ctx.accounts.admin.to_account_info(),
amount,
&mint,
)?;

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

spot_market.validate_max_token_deposits_and_borrows()?;

emit!(SpotMarketVaultDepositRecord {
ts: Clock::get()?.unix_timestamp,
market_index: spot_market.market_index,
deposit_balance: spot_market.deposit_balance,
cumulative_deposit_interest_before,
cumulative_deposit_interest_after,
deposit_token_amount_before: deposit_token_amount_before.cast()?,
amount
});

Ok(())
}

#[access_control(
perp_market_valid(&ctx.accounts.perp_market)
valid_oracle_for_perp_market(&ctx.accounts.oracle, &ctx.accounts.perp_market)
Expand Down Expand Up @@ -4277,6 +4363,28 @@ pub struct DepositIntoMarketFeePool<'info> {
pub token_program: Interface<'info, TokenInterface>,
}

#[derive(Accounts)]
pub struct DepositIntoSpotMarketVault<'info> {
pub state: Box<Account<'info, State>>,
#[account(mut)]
pub spot_market: AccountLoader<'info, SpotMarket>,
#[account(
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
)]
pub admin: Signer<'info>,
#[account(
mut,
token::authority = admin
)]
pub source_vault: Box<InterfaceAccount<'info, TokenAccount>>,
#[account(
mut,
constraint = spot_market.load()?.vault == spot_market_vault.key()
)]
pub spot_market_vault: Box<InterfaceAccount<'info, TokenAccount>>,
pub token_program: Interface<'info, TokenInterface>,
}

#[derive(Accounts)]
pub struct RepegCurve<'info> {
#[account(
Expand Down
7 changes: 7 additions & 0 deletions programs/drift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,13 @@ pub mod drift {
handle_deposit_into_perp_market_fee_pool(ctx, amount)
}

pub fn deposit_into_spot_market_vault<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, DepositIntoSpotMarketVault<'info>>,
amount: u64,
) -> Result<()> {
handle_deposit_into_spot_market_vault(ctx, amount)
}

pub fn deposit_into_spot_market_revenue_pool<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, RevenuePoolDeposit<'info>>,
amount: u64,
Expand Down
14 changes: 14 additions & 0 deletions programs/drift/src/state/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,20 @@ pub struct SwapRecord {
pub fee: u64,
}

#[event]
pub struct SpotMarketVaultDepositRecord {
pub ts: i64,
pub market_index: u16,
/// precision: SPOT_BALANCE_PRECISION
pub deposit_balance: u128,
/// precision: SPOT_CUMULATIVE_INTEREST_PRECISION
pub cumulative_deposit_interest_before: u128,
/// precision: SPOT_CUMULATIVE_INTEREST_PRECISION
pub cumulative_deposit_interest_after: u128,
pub deposit_token_amount_before: u64,
pub amount: u64,
}

pub fn emit_stack<T: AnchorSerialize + Discriminator, const N: usize>(event: T) -> DriftResult {
let mut data_buf = [0u8; N];
let mut out_buf = [0u8; N];
Expand Down
43 changes: 43 additions & 0 deletions sdk/src/adminClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,49 @@ export class AdminClient extends DriftClient {
});
}

public async depositIntoSpotMarketVault(
spotMarketIndex: number,
amount: BN,
sourceVault: PublicKey
): Promise<TransactionSignature> {
const depositIntoPerpMarketFeePoolIx =
await this.getDepositIntoSpotMarketVaultIx(
spotMarketIndex,
amount,
sourceVault
);

const tx = await this.buildTransaction(depositIntoPerpMarketFeePoolIx);

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

return txSig;
}

public async getDepositIntoSpotMarketVaultIx(
spotMarketIndex: number,
amount: BN,
sourceVault: PublicKey
): Promise<TransactionInstruction> {
const spotMarket = this.getSpotMarketAccount(spotMarketIndex);

const remainingAccounts = [];
this.addTokenMintToRemainingAccounts(spotMarket, remainingAccounts);
const tokenProgram = this.getTokenProgramForSpotMarket(spotMarket);
return await this.program.instruction.depositIntoSpotMarketVault(amount, {
accounts: {
admin: this.isSubscribed
? this.getStateAccount().admin
: this.wallet.publicKey,
state: await this.getStatePublicKey(),
sourceVault,
spotMarket: spotMarket.pubkey,
spotMarketVault: spotMarket.vault,
tokenProgram,
},
});
}

public async updateAdmin(admin: PublicKey): Promise<TransactionSignature> {
const updateAdminIx = await this.getUpdateAdminIx(admin);

Expand Down
6 changes: 5 additions & 1 deletion sdk/src/events/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
InsuranceFundStakeRecord,
CurveRecord,
SwapRecord,
SpotMarketVaultDepositRecord,
} from '../index';
import { EventEmitter } from 'events';

Expand Down Expand Up @@ -47,6 +48,7 @@ export const DefaultEventSubscriptionOptions: EventSubscriptionOptions = {
'InsuranceFundStakeRecord',
'CurveRecord',
'SwapRecord',
'SpotMarketVaultDepositRecord',
],
maxEventsPerType: 4096,
orderBy: 'blockchain',
Expand Down Expand Up @@ -89,6 +91,7 @@ export type EventMap = {
InsuranceFundStakeRecord: Event<InsuranceFundStakeRecord>;
CurveRecord: Event<CurveRecord>;
SwapRecord: Event<SwapRecord>;
SpotMarketVaultDepositRecord: Event<SpotMarketVaultDepositRecord>;
};

export type EventType = keyof EventMap;
Expand All @@ -107,7 +110,8 @@ export type DriftEvent =
| Event<SpotInterestRecord>
| Event<InsuranceFundStakeRecord>
| Event<CurveRecord>
| Event<SwapRecord>;
| Event<SwapRecord>
| Event<SpotMarketVaultDepositRecord>;

export interface EventSubscriberEvents {
newEvent: (event: WrappedEvent<EventType>) => void;
Expand Down
81 changes: 81 additions & 0 deletions sdk/src/idl/drift.json
Original file line number Diff line number Diff line change
Expand Up @@ -3634,6 +3634,47 @@
}
]
},
{
"name": "depositIntoSpotMarketVault",
"accounts": [
{
"name": "state",
"isMut": false,
"isSigner": false
},
{
"name": "spotMarket",
"isMut": true,
"isSigner": false
},
{
"name": "admin",
"isMut": false,
"isSigner": true
},
{
"name": "sourceVault",
"isMut": true,
"isSigner": false
},
{
"name": "spotMarketVault",
"isMut": true,
"isSigner": false
},
{
"name": "tokenProgram",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "amount",
"type": "u64"
}
]
},
{
"name": "depositIntoSpotMarketRevenuePool",
"accounts": [
Expand Down Expand Up @@ -11400,6 +11441,46 @@
"index": false
}
]
},
{
"name": "SpotMarketVaultDepositRecord",
"fields": [
{
"name": "ts",
"type": "i64",
"index": false
},
{
"name": "marketIndex",
"type": "u16",
"index": false
},
{
"name": "depositBalance",
"type": "u128",
"index": false
},
{
"name": "cumulativeDepositInterestBefore",
"type": "u128",
"index": false
},
{
"name": "cumulativeDepositInterestAfter",
"type": "u128",
"index": false
},
{
"name": "depositTokenAmountBefore",
"type": "u64",
"index": false
},
{
"name": "amount",
"type": "u64",
"index": false
}
]
}
],
"errors": [
Expand Down
10 changes: 10 additions & 0 deletions sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,16 @@ export type SwapRecord = {
fee: BN;
};

export type SpotMarketVaultDepositRecord = {
ts: BN;
marketIndex: number;
depositBalance: BN;
cumulativeDepositInterestBefore: BN;
cumulativeDepositInterestAfter: BN;
depositTokenAmountBefore: BN;
amount: BN;
};

export type StateAccount = {
admin: PublicKey;
exchangeStatus: number;
Expand Down
1 change: 1 addition & 0 deletions test-scripts/run-anchor-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ test_files=(
cancelAllOrders.ts
curve.ts
deleteInitializedSpotMarket.ts
depositIntoSpotMarketVault.ts
driftClient.ts
fillSpot.ts
insuranceFundStake.ts
Expand Down
2 changes: 1 addition & 1 deletion test-scripts/single-anchor-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fi

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

test_files=(liquidatePerpWithFill.ts)
test_files=(depositIntoSpotMarketVault.ts)

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

0 comments on commit bf1d2be

Please sign in to comment.