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+
1617use solana_program:: sysvar:: clock:: Clock ;
1718
1819use 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:: * ;
1923use crate :: state:: lp_pool:: { AMM_MAP_PDA_SEED , CONSTITUENT_TARGET_WEIGHT_PDA_SEED } ;
2024
2125pub 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+ }
0 commit comments