Skip to content

Commit 04ce24b

Browse files
committed
add lp_swap ix
1 parent 3a6f0f7 commit 04ce24b

File tree

4 files changed

+279
-81
lines changed

4 files changed

+279
-81
lines changed

programs/drift/src/instructions/lp_pool.rs

Lines changed: 200 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
use anchor_lang::{prelude::*, Accounts, Key, Result, ToAccountInfo};
2-
3-
use crate::{
4-
error::ErrorCode,
5-
math::oracle::{is_oracle_valid_for_action, DriftAction},
6-
msg,
7-
state::{
8-
lp_pool::{AmmConstituentDatum, AmmConstituentMappingFixed, LPPool, WeightValidationFlags},
9-
perp_market_map::MarketSet,
10-
state::State,
11-
user::MarketType,
12-
zero_copy::{AccountZeroCopy, ZeroCopyLoader},
13-
},
14-
validate,
1+
use anchor_lang::{prelude::*, Accounts, Key, Result};
2+
use anchor_spl::token_interface::{TokenAccount, TokenInterface, Mint};
3+
4+
use crate::error::ErrorCode;
5+
use crate::math::{oracle::{is_oracle_valid_for_action, DriftAction}, safe_math::SafeMath};
6+
use crate::msg;
7+
use crate::state::spot_market::SpotBalanceType;
8+
use crate:: state::{
9+
lp_pool::{AmmConstituentDatum, AmmConstituentMappingFixed, LPPool, WeightValidationFlags, Constituent},
10+
perp_market_map::MarketSet,
11+
state::State,
12+
user::MarketType,
13+
zero_copy::{AccountZeroCopy, ZeroCopyLoader},
1514
};
15+
use crate::validate;
16+
1617
use solana_program::sysvar::clock::Clock;
1718

1819
use super::optional_accounts::{load_maps, AccountMaps};
20+
use crate::controller::spot_balance::update_spot_market_cumulative_interest;
21+
use crate::controller::token::{receive, send_from_program_vault};
22+
use crate::instructions::constraints::*;
1923
use crate::state::lp_pool::{AMM_MAP_PDA_SEED, CONSTITUENT_TARGET_WEIGHT_PDA_SEED};
2024

2125
pub fn handle_update_constituent_target_weights<'c: 'info, 'info>(
@@ -100,6 +104,134 @@ pub fn handle_update_constituent_target_weights<'c: 'info, 'info>(
100104
Ok(())
101105
}
102106

107+
#[access_control(
108+
fill_not_paused(&ctx.accounts.state)
109+
)]
110+
pub fn handle_lp_pool_swap<'c: 'info, 'info>(
111+
ctx: Context<'_, '_, 'c, 'info, LPSwap<'info>>,
112+
in_market_index: u16,
113+
out_market_index: u16,
114+
in_amount: u64,
115+
min_out_amount: u64,
116+
) -> Result<()> {
117+
118+
validate!(
119+
in_market_index != out_market_index,
120+
ErrorCode::InvalidSpotMarketAccount,
121+
"In and out spot market indices cannot be the same"
122+
)?;
123+
124+
let slot = Clock::get()?.slot;
125+
let now = Clock::get()?.unix_timestamp;
126+
let state = &ctx.accounts.state;
127+
let lp_pool = &ctx.accounts.lp_pool.load()?;
128+
129+
let mut in_constituent = ctx.accounts.in_constituent.load_mut()?;
130+
let mut out_constituent = ctx.accounts.out_constituent.load_mut()?;
131+
132+
let in_constituent_token_account = &ctx.accounts.constituent_in_token_account;
133+
let out_constituent_token_account = &ctx.accounts.constituent_out_token_account;
134+
135+
let constituent_target_weights = ctx.accounts.constituent_target_weights.load_zc()?;
136+
137+
let AccountMaps {
138+
perp_market_map,
139+
spot_market_map,
140+
mut oracle_map,
141+
} = load_maps(
142+
&mut ctx.remaining_accounts.iter().peekable(),
143+
&MarketSet::new(),
144+
&MarketSet::new(),
145+
slot,
146+
Some(state.oracle_guard_rails),
147+
)?;
148+
149+
let mut in_spot_market = spot_market_map.get_ref_mut(&in_market_index)?;
150+
let mut out_spot_market = spot_market_map.get_ref_mut(&out_market_index)?;
151+
152+
let in_oracle_id = in_spot_market.oracle_id();
153+
let out_oracle_id = out_spot_market.oracle_id();
154+
155+
let in_oracle = *oracle_map.get_price_data(&in_oracle_id)?;
156+
let out_oracle = *oracle_map.get_price_data(&out_oracle_id)?;
157+
158+
update_spot_market_cumulative_interest(
159+
&mut in_spot_market,
160+
Some(&in_oracle),
161+
now,
162+
)?;
163+
164+
update_spot_market_cumulative_interest(
165+
&mut out_spot_market,
166+
Some(&out_oracle),
167+
now,
168+
)?;
169+
170+
let in_constituent_balance = in_constituent.get_full_balance(in_constituent_token_account.amount)?;
171+
msg!("in_constituent: {}, in_constituent_balance: {}", in_constituent.constituent_index, in_constituent_balance);
172+
let out_constituent_balance = out_constituent.get_full_balance(out_constituent_token_account.amount)?;
173+
msg!("out_constituent: {}, out_constituent_balance: {}", out_constituent.constituent_index, out_constituent_balance);
174+
175+
let in_target_weight = constituent_target_weights.get_target_weight(in_constituent.constituent_index)?;
176+
let out_target_weight = constituent_target_weights.get_target_weight(out_constituent.constituent_index)?;
177+
178+
let (in_amount, out_amount, in_fee, out_fee) = lp_pool.get_swap_amount(
179+
&in_oracle, &out_oracle,
180+
&in_constituent, &out_constituent,
181+
&in_spot_market, &out_spot_market,
182+
in_constituent_token_account.amount, out_constituent_token_account.amount,
183+
in_target_weight, out_target_weight,
184+
in_amount)?;
185+
let out_amount_net_fees = if out_fee > 0 {
186+
out_amount.safe_sub(out_fee.unsigned_abs() as u64)?
187+
} else {
188+
out_amount.safe_add(out_fee.unsigned_abs() as u64)?
189+
};
190+
191+
validate!(
192+
out_amount_net_fees >= min_out_amount,
193+
ErrorCode::SlippageOutsideLimit,
194+
format!("Slippage outside limit: out_amount_net_fees({}) < min_out_amount({})", out_amount_net_fees, min_out_amount).as_str()
195+
)?;
196+
197+
in_constituent.record_swap_fees(in_fee)?;
198+
out_constituent.record_swap_fees(out_fee)?;
199+
200+
201+
// interactions: CPIs
202+
203+
let (transfer_from_vault, transfer_from_constituent) = out_constituent.get_amount_from_vaults_to_withdraw(out_constituent_token_account.amount, out_amount_net_fees)?;
204+
205+
// transfer in from user token account to token vault
206+
receive(
207+
&ctx.accounts.token_program,
208+
&ctx.accounts.user_in_token_account,
209+
&ctx.accounts.constituent_in_token_account,
210+
&ctx.accounts.authority,
211+
in_amount,
212+
&Some((*ctx.accounts.in_market_mint).clone()),
213+
)?;
214+
ctx.accounts.constituent_in_token_account.reload()?;
215+
216+
// transfer out from token vault to constituent token account
217+
if transfer_from_vault > 0 {
218+
send_from_program_vault(
219+
&ctx.accounts.token_program,
220+
&ctx.accounts.out_spot_market_vault,
221+
&ctx.accounts.constituent_out_token_account,
222+
&ctx.accounts.drift_signer,
223+
state.signer_nonce,
224+
transfer_from_vault,
225+
&Some((*ctx.accounts.out_market_mint).clone()),
226+
)?;
227+
}
228+
ctx.accounts.constituent_out_token_account.reload()?;
229+
230+
// transfer out from constituent token account to user token account
231+
232+
Ok(())
233+
}
234+
103235
#[derive(Accounts)]
104236
#[instruction(
105237
lp_pool_name: [u8; 32],
@@ -127,3 +259,57 @@ pub struct UpdateConstituentTargetWeights<'info> {
127259
)]
128260
pub lp_pool: AccountLoader<'info, LPPool>,
129261
}
262+
263+
#[derive(Accounts)]
264+
pub struct LPSwap<'info> {
265+
/// CHECK: forced drift_signer
266+
pub drift_signer: AccountInfo<'info>,
267+
pub state: Box<Account<'info, State>>,
268+
pub lp_pool: AccountLoader<'info, LPPool>,
269+
#[account(
270+
mut,
271+
seeds = [CONSTITUENT_TARGET_WEIGHT_PDA_SEED.as_ref(), lp_pool.key().as_ref()],
272+
bump,
273+
)]
274+
/// CHECK: checked in ConstituentTargetWeightsZeroCopy checks
275+
pub constituent_target_weights: AccountInfo<'info>,
276+
277+
#[account(mut)]
278+
pub constituent_in_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
279+
#[account(mut)]
280+
pub constituent_out_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
281+
282+
#[account(mut)]
283+
pub user_in_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
284+
#[account(mut)]
285+
pub user_out_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
286+
287+
#[account(
288+
mut,
289+
constraint = &in_spot_market_vault.mint.eq(&in_market_mint.key())
290+
)]
291+
pub in_spot_market_vault: Box<InterfaceAccount<'info, TokenAccount>>,
292+
#[account(
293+
mut,
294+
constraint = &out_spot_market_vault.mint.eq(&out_market_mint.key())
295+
)]
296+
pub out_spot_market_vault: Box<InterfaceAccount<'info, TokenAccount>>,
297+
298+
#[account(mut)]
299+
pub in_constituent: AccountLoader<'info, Constituent>,
300+
#[account(mut)]
301+
pub out_constituent: AccountLoader<'info, Constituent>,
302+
303+
#[account(
304+
constraint = in_market_mint.key() == in_constituent.load()?.mint,
305+
)]
306+
pub in_market_mint: Box<InterfaceAccount<'info, Mint>>,
307+
#[account(
308+
constraint = out_market_mint.key() == out_constituent.load()?.mint,
309+
)]
310+
pub out_market_mint: Box<InterfaceAccount<'info, Mint>>,
311+
312+
pub authority: Signer<'info>,
313+
314+
pub token_program: Interface<'info, TokenInterface>,
315+
}

programs/drift/src/instructions/user.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4027,19 +4027,6 @@ pub fn handle_end_swap<'c: 'info, 'info>(
40274027
Ok(())
40284028
}
40294029

4030-
#[access_control(
4031-
fill_not_paused(&ctx.accounts.state)
4032-
)]
4033-
pub fn handle_lp_pool_swap<'c: 'info, 'info>(
4034-
ctx: Context<'_, '_, 'c, 'info, LPSwap<'info>>,
4035-
in_market_index: u16,
4036-
out_market_index: u16,
4037-
limit_price: Option<u64>,
4038-
reduce_only: Option<SwapReduceOnly>,
4039-
) -> Result<()> {
4040-
Ok(())
4041-
}
4042-
40434030
#[derive(Accounts)]
40444031
#[instruction(
40454032
sub_account_id: u16,
@@ -4702,23 +4689,3 @@ pub struct UpdateUserProtectedMakerMode<'info> {
47024689
#[account(mut)]
47034690
pub protected_maker_mode_config: AccountLoader<'info, ProtectedMakerModeConfig>,
47044691
}
4705-
4706-
#[derive(Accounts)]
4707-
pub struct LPSwap<'info> {
4708-
pub state: Box<Account<'info, State>>,
4709-
pub lp_pool: AccountLoader<'info, LPPool>,
4710-
4711-
#[account(mut)]
4712-
pub in_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
4713-
#[account(mut)]
4714-
pub out_token_account: Box<InterfaceAccount<'info, TokenAccount>>,
4715-
4716-
#[account(mut)]
4717-
pub in_constituent: AccountLoader<'info, Constituent>,
4718-
#[account(mut)]
4719-
pub out_constituent: AccountLoader<'info, Constituent>,
4720-
4721-
pub authority: Signer<'info>,
4722-
4723-
pub token_program: Interface<'info, TokenInterface>,
4724-
}

programs/drift/src/state/events.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,3 +672,34 @@ pub fn emit_buffers<T: AnchorSerialize + Discriminator>(
672672

673673
Ok(())
674674
}
675+
676+
#[event]
677+
#[derive(Default)]
678+
pub struct LPSwapRecord {
679+
pub ts: i64,
680+
pub authority: Pubkey,
681+
/// precision: out market mint precision, gross fees
682+
pub amount_out: u64,
683+
/// precision: in market mint precision, gross fees
684+
pub amount_in: u64,
685+
/// precision: fee on amount_out, in market mint precision
686+
pub fee_out: i64,
687+
/// precision: fee on amount_in, out market mint precision
688+
pub fee_in: i64,
689+
// out spot market index
690+
pub out_spot_market_index: u16,
691+
// in spot market index
692+
pub in_spot_market_index: u16,
693+
// out constituent index
694+
pub out_constituent_index: u16,
695+
// in constituent index
696+
pub in_constituent_index: u16,
697+
/// precision: PRICE_PRECISION
698+
pub out_oracle_price: i64,
699+
/// precision: PRICE_PRECISION
700+
pub in_oracle_price: i64,
701+
/// out token mint
702+
pub mint_out: u64,
703+
/// in token mint
704+
pub mint_in: u64,
705+
}

0 commit comments

Comments
 (0)