diff --git a/controller/core/src/instructions/swap/v2/mod.rs b/controller/core/src/instructions/swap/v2/mod.rs index dd66041..187812a 100644 --- a/controller/core/src/instructions/swap/v2/mod.rs +++ b/controller/core/src/instructions/swap/v2/mod.rs @@ -57,6 +57,68 @@ pub const IX_PRE_IS_WRITER: IxPreAccFlags = IxPreAccFlags::memset(true) pub const IX_PRE_IS_SIGNER: IxPreAccFlags = IxPreAccFlags::memset(false).const_with_signer(true); +/// Utils +impl IxPreAccs { + /// For easier usage with type-aliases + #[inline] + pub const fn new(a: [T; IX_PRE_ACCS_LEN]) -> Self { + Self(a) + } + + #[inline] + pub const fn switch_inp_out(&mut self) -> &mut Self { + let this = self.0.as_mut_slice(); + let idxs = [ + [IX_PRE_ACCS_IDX_INP_MINT, IX_PRE_ACCS_IDX_OUT_MINT], + [IX_PRE_ACCS_IDX_INP_ACC, IX_PRE_ACCS_IDX_OUT_ACC], + [ + IX_PRE_ACCS_IDX_INP_TOKEN_PROGRAM, + IX_PRE_ACCS_IDX_OUT_TOKEN_PROGRAM, + ], + [ + IX_PRE_ACCS_IDX_INP_POOL_RESERVES, + IX_PRE_ACCS_IDX_OUT_POOL_RESERVES, + ], + ]; + + let mut i = 0; + while i < idxs.len() { + let [x, y] = idxs[i]; + this.swap(x, y); + + i += 1; + } + + self + } + + #[inline] + pub const fn with_switched_inp_out(mut self) -> Self { + self.switch_inp_out(); + self + } +} + +/// The accounts for one of the tokens (inp or out) involved in a swap +#[generic_array_struct(builder pub)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct SwapEntryAccs { + pub mint: T, + pub acc: T, + pub token_program: T, + + /// Set to LP mint if `mint = LP mint` + pub pool_reserves: T, +} + +impl SwapEntryAccs { + #[inline] + pub const fn memset(val: T) -> Self { + Self([val; SWAP_ENTRY_ACCS_LEN]) + } +} + // v1 conversions impl IxPreAccs { diff --git a/controller/core/src/svc.rs b/controller/core/src/svc.rs index b564a8e..546a1fe 100644 --- a/controller/core/src/svc.rs +++ b/controller/core/src/svc.rs @@ -107,20 +107,19 @@ impl InfCalc { /// `None` if pool is insolvent for LPers (lp_due is negative) #[inline] pub const fn lp_due_over_supply(&self) -> Option>> { - let r = match self.pool_lamports.lp_due_checked() { + let lp_due = match self.pool_lamports.lp_due_checked() { None => return None, - Some(n) => Ratio { - n, + Some(x) => x, + }; + let r = if self.mint_supply == 0 || (lp_due == 0 && *self.pool_lamports.withheld() == 0) { + Ratio::::ONE + } else { + Ratio { + n: lp_due, d: self.mint_supply, - }, + } }; - Some(Floor( - if r.d == 0 || (r.n == 0 && *self.pool_lamports.withheld() == 0) { - Ratio::::ONE - } else { - r - }, - )) + Some(Floor(r)) } } @@ -258,9 +257,17 @@ mod tests { proptest! { #[test] - fn lp_due_over_supply_is_never_zero(calc in any_calc()) { + fn lp_due_over_supply_zero_cases(calc in any_calc()) { let r = calc.lp_due_over_supply().unwrap(); - assert!(!r.0.is_zero()); + + // this is the only case where r should be 0 + if *calc.pool_lamports.withheld() != 0 + && calc.pool_lamports.lp_due_checked().unwrap() == 0 + { + assert!(r.0.is_zero()); + } else { + assert!(!r.0.is_zero()); + } } } diff --git a/controller/program/tests/tests/swap/common/accounts.rs b/controller/program/tests/tests/swap/common/accounts.rs index 6c05894..a427a30 100644 --- a/controller/program/tests/tests/swap/common/accounts.rs +++ b/controller/program/tests/tests/swap/common/accounts.rs @@ -1,5 +1,21 @@ +use core::mem::take; + +use generic_array_struct::generic_array_struct; use inf1_core::instructions::swap::IxAccs; -use inf1_test_utils::{fill_mock_prog_accs, AccountMap}; +use inf1_ctl_jiminy::{ + instructions::swap::v2::{ + exact_in::NewSwapExactInV2IxPreAccsBuilder, IxPreKeysOwned, NewSwapEntryAccsBuilder, + SwapEntryAccs, + }, + keys::{LST_STATE_LIST_ID, POOL_STATE_ID}, +}; +use inf1_pp_core::pair::Pair; +use inf1_test_utils::{ + fill_mock_prog_accs, lst_state_list_account, mock_mint, mock_sys_acc, mock_token_acc, raw_mint, + raw_token_acc, AccountMap, LstStateListData, VerPoolState, +}; +use solana_account::Account; +use solana_pubkey::Pubkey; pub fn fill_swap_prog_accs( am: &mut AccountMap, @@ -12,3 +28,122 @@ pub fn fill_swap_prog_accs( ) { fill_mock_prog_accs(am, [*inp_calc_prog, *out_calc_prog, *pricing_prog]); } + +#[generic_array_struct(builder pub)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SwapTokenU64s { + pub reserves_bal: T, + pub acc_bal: T, + pub mint_supply: T, +} + +#[generic_array_struct(builder pub)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SwapTokenAddrs { + pub mint: T, + pub acc: T, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SwapTokenArg { + pub u64s: SwapTokenU64s, + pub addrs: SwapTokenAddrs, +} + +type SwapTokenArgVals = SwapTokenArg; + +/// Assumes +/// - both mints are tokenkeg mints +/// - both tokens are 9 decimals +pub fn swap_pre_accs( + signer: &[u8; 32], + ps: &VerPoolState, + lsl: &LstStateListData, + args: &Pair, +) -> (IxPreKeysOwned, AccountMap) { + let mut pair = args.map(|args| { + if args.addrs.mint() == ps.lp_token_mint() { + lp_accs(ps, signer, &args) + } else { + lst_accs(lsl, signer, &args) + } + }); + let accounts = NewSwapExactInV2IxPreAccsBuilder::start() + .with_signer(((*signer).into(), mock_sys_acc(1_000_000_000))) + .with_pool_state((POOL_STATE_ID.into(), ps.into_account())) + .with_lst_state_list(( + LST_STATE_LIST_ID.into(), + lst_state_list_account(lsl.lst_state_list.clone()), + )) + // move instead of clone + .with_inp_mint(take(pair.inp.mint_mut())) + .with_inp_acc(take(pair.inp.acc_mut())) + .with_inp_token_program(take(pair.inp.token_program_mut())) + .with_inp_pool_reserves(take(pair.inp.pool_reserves_mut())) + .with_out_mint(take(pair.out.mint_mut())) + .with_out_acc(take(pair.out.acc_mut())) + .with_out_token_program(take(pair.out.token_program_mut())) + .with_out_pool_reserves(take(pair.out.pool_reserves_mut())) + .build(); + let ix_prefix = IxPreKeysOwned::new(accounts.0.each_ref().map(|(pk, _)| pk.to_bytes())); + + (ix_prefix, accounts.0.into_iter().collect()) +} + +fn lp_accs( + ps: &VerPoolState, + signer: &[u8; 32], + SwapTokenArg { u64s, addrs }: &SwapTokenArgVals, +) -> SwapEntryAccs<(Pubkey, Account)> { + if u64s.reserves_bal() != u64s.mint_supply() { + panic!( + "reserves_bal {} != mint_supply {}. Set both to eq.", + u64s.reserves_bal(), + u64s.mint_supply() + ); + } + let lp_mint = ( + Pubkey::new_from_array(*ps.lp_token_mint()), + mock_mint(raw_mint(Some(POOL_STATE_ID), None, *u64s.mint_supply(), 9)), + ); + let acc = ( + Pubkey::new_from_array(*addrs.acc()), + mock_token_acc(raw_token_acc(*ps.lp_token_mint(), *signer, *u64s.acc_bal())), + ); + NewSwapEntryAccsBuilder::start() + .with_mint(lp_mint.clone()) + .with_acc(acc) + .with_pool_reserves(lp_mint) + .with_token_program(mollusk_svm_programs_token::token::keyed_account()) + .build() +} + +fn lst_accs( + lsl: &LstStateListData, + signer: &[u8; 32], + SwapTokenArg { u64s, addrs }: &SwapTokenArgVals, +) -> SwapEntryAccs<(Pubkey, Account)> { + let mint = ( + Pubkey::new_from_array(*addrs.mint()), + // dont-care abt mint and freeze auths + mock_mint(raw_mint(None, None, *u64s.mint_supply(), 9)), + ); + let acc = ( + Pubkey::new_from_array(*addrs.acc()), + mock_token_acc(raw_token_acc(*addrs.mint(), *signer, *u64s.acc_bal())), + ); + let reserves = ( + lsl.all_pool_reserves[addrs.mint()].into(), + mock_token_acc(raw_token_acc( + *addrs.mint(), + POOL_STATE_ID, + *u64s.reserves_bal(), + )), + ); + NewSwapEntryAccsBuilder::start() + .with_mint(mint) + .with_acc(acc) + .with_pool_reserves(reserves) + .with_token_program(mollusk_svm_programs_token::token::keyed_account()) + .build() +} diff --git a/controller/program/tests/tests/swap/common/asserts.rs b/controller/program/tests/tests/swap/common/asserts.rs index 41b5e98..8858d97 100644 --- a/controller/program/tests/tests/swap/common/asserts.rs +++ b/controller/program/tests/tests/swap/common/asserts.rs @@ -9,7 +9,6 @@ use inf1_ctl_jiminy::{ snap::{Snap, SnapU64}, }, }; -use inf1_pp_ag_core::instructions::{PriceExactInAccsAg, PriceExactOutAccsAg}; use inf1_std::quote::{ swap::{exact_in::quote_exact_in, exact_out::quote_exact_out}, @@ -17,82 +16,69 @@ use inf1_std::quote::{ }; use inf1_test_utils::{ acc_bef_aft, assert_diffs_lst_state_list, assert_diffs_pool_state_v2, assert_token_acc_diffs, - get_lst_state_list, get_mint_supply, token_acc_bal_diff_changed, AccountMap, Diff, - DiffsPoolStateV2, LstStateListChanges, + get_mint_supply, token_acc_bal_diff_changed, AccountMap, Diff, DiffsPoolStateV2, + LstStateListChanges, }; use sanctum_spl_token_jiminy::sanctum_spl_token_core::state::account::RawTokenAccount; use sanctum_u64_ratio::Ratio; use solana_pubkey::Pubkey; -use crate::{ - common::assert_lp_solvent_invar, - tests::swap::common::{derive_pp_exact_in, derive_pp_exact_out, derive_qa_hla}, -}; +use crate::{common::assert_lp_solvent_invar, tests::swap::common::derive_qa_prog_accs}; use super::super::V2Args; pub fn assert_correct_swap_exact_in_v2( bef: &AccountMap, aft: &AccountMap, - args: &V2Args, + args: &V2Args, curr_epoch: u64, curr_slot: u64, ) -> Quote { - let ps_aft = - PoolStateV2Packed::of_acc_data(&aft[&(*args.accs.ix_prefix.pool_state()).into()].data) - .unwrap() - .into_pool_state_v2(); - let list_aft = get_lst_state_list(&aft[&(*args.accs.ix_prefix.lst_state_list()).into()].data); - - let pricing = derive_pp_exact_in(bef, &args.accs); - let (qa, ps_aft_header_la, list_aft_header_la) = - derive_qa_hla(bef, args, curr_epoch, curr_slot, pricing); + let (ps, list, qa) = derive_qa_prog_accs(bef, aft, args, curr_epoch, curr_slot); let quote = quote_exact_in(&qa).unwrap(); - - if args.inp_lst_index == u32::MAX || args.out_lst_index == u32::MAX { - let inf_mint = if args.inp_lst_index == u32::MAX { - args.accs.ix_prefix.inp_mint() - } else { - args.accs.ix_prefix.out_mint() - }; - let inf_supply_snap = - Snap([bef, aft].map(|am| get_mint_supply(&am[&(*inf_mint).into()].data))); - assert_swap_token_movements(bef, aft, &args.accs.ix_prefix, "e); - assert_accs_liq( - [&ps_aft_header_la, &ps_aft], - [&list_aft_header_la, &list_aft], - "e, - ); - assert_rr_liq(&ps_aft_header_la, &ps_aft, &inf_supply_snap); - } else { - assert_swap_token_movements(bef, aft, &args.accs.ix_prefix, "e); - assert_accs_swap( - [&ps_aft_header_la, &ps_aft], - [&list_aft_header_la, &list_aft], - "e, - ); - } + assert_correct_swap_v2( + bef, + aft, + args, + ps.each_ref(), + list.each_ref().map(AsRef::as_ref), + "e, + ); + // slippage limit should have been respected + assert!(quote.out >= args.limit); quote } pub fn assert_correct_swap_exact_out_v2( bef: &AccountMap, aft: &AccountMap, - args: &V2Args, + args: &V2Args, curr_epoch: u64, curr_slot: u64, ) -> Quote { - let ps_aft = - PoolStateV2Packed::of_acc_data(&aft[&(*args.accs.ix_prefix.pool_state()).into()].data) - .unwrap() - .into_pool_state_v2(); - let list_aft = get_lst_state_list(&aft[&(*args.accs.ix_prefix.lst_state_list()).into()].data); - - let pricing = derive_pp_exact_out(bef, &args.accs); - let (qa, ps_aft_header_la, list_aft_header_la) = - derive_qa_hla(bef, args, curr_epoch, curr_slot, pricing); + let (ps, list, qa) = derive_qa_prog_accs(bef, aft, args, curr_epoch, curr_slot); let quote = quote_exact_out(&qa).unwrap(); + assert_correct_swap_v2( + bef, + aft, + args, + ps.each_ref(), + list.each_ref().map(AsRef::as_ref), + "e, + ); + // slippage limit should have been respected + assert!(quote.inp <= args.limit); + quote +} +fn assert_correct_swap_v2( + bef: &AccountMap, + aft: &AccountMap, + args: &V2Args, + ps: [&PoolStateV2; 2], + list: [&[LstState]; 2], + quote: &Quote, +) { if args.inp_lst_index == u32::MAX || args.out_lst_index == u32::MAX { let inf_mint = if args.inp_lst_index == u32::MAX { args.accs.ix_prefix.inp_mint() @@ -101,24 +87,16 @@ pub fn assert_correct_swap_exact_out_v2( }; let inf_supply_snap = Snap([bef, aft].map(|am| get_mint_supply(&am[&(*inf_mint).into()].data))); - assert_swap_token_movements(bef, aft, &args.accs.ix_prefix, "e); - assert_accs_liq( - [&ps_aft_header_la, &ps_aft], - [&list_aft_header_la, &list_aft], - "e, - ); - assert_rr_liq(&ps_aft_header_la, &ps_aft, &inf_supply_snap); + assert_swap_token_movements(bef, aft, &args.accs.ix_prefix, quote); + assert_accs_liq(ps, list, quote); + assert_rr_liq(ps, &inf_supply_snap); } else { - assert_swap_token_movements(bef, aft, &args.accs.ix_prefix, "e); - assert_accs_swap( - [&ps_aft_header_la, &ps_aft], - [&list_aft_header_la, &list_aft], - "e, - ); + assert_swap_token_movements(bef, aft, &args.accs.ix_prefix, quote); + assert_accs_swap(ps, list, quote); } - quote } +/// Assert that tokens have moved according to `quote` fn assert_swap_token_movements( bef: &AccountMap, aft: &AccountMap, @@ -325,7 +303,7 @@ fn assert_accs_liq( } /// assert redemption rate of INF did not decrease after add/remove liq -fn assert_rr_liq(aft_header_lookahead: &PoolStateV2, aft: &PoolStateV2, inf_supply: &SnapU64) { +fn assert_rr_liq([aft_header_lookahead, aft]: [&PoolStateV2; 2], inf_supply: &SnapU64) { let [bef_svl, aft_svl] = [aft_header_lookahead, aft].map(PoolSvLamports::from_pool_state_v2); let [[bef_total_ratio, bef_lp_ratio], [aft_total_ratio, aft_lp_ratio]] = diff --git a/controller/program/tests/tests/swap/common/derives.rs b/controller/program/tests/tests/swap/common/derives.rs index 0501b5f..955600f 100644 --- a/controller/program/tests/tests/swap/common/derives.rs +++ b/controller/program/tests/tests/swap/common/derives.rs @@ -1,11 +1,10 @@ use inf1_ctl_jiminy::{ - accounts::pool_state::PoolStateV2, instructions::swap::v2::IxPreAccs, svc::InfCalc, + accounts::pool_state::{PoolStateV2, PoolStateV2Packed}, + instructions::swap::v2::IxPreAccs, + svc::InfCalc, typedefs::lst_state::LstState, }; -use inf1_pp_ag_core::{ - instructions::{PriceExactInAccsAg, PriceExactOutAccsAg}, - PricingAg, -}; +use inf1_pp_ag_core::PricingAg; use inf1_pp_core::pair::Pair; use inf1_pp_flatslab_std::{ accounts::Slab, instructions::pricing::FlatSlabPpAccs, pricing::FlatSlabSwapPricing, @@ -17,24 +16,41 @@ use inf1_test_utils::{ }; use solana_pubkey::Pubkey; -use crate::common::{derive_svc_no_inf, header_lookahead, lst_state_lookahead, Cbs}; +use crate::{ + common::{derive_svc_no_inf, header_lookahead, lst_state_lookahead, Cbs}, + tests::swap::{PricingSwapAg, QuoteArgsAg}, +}; use super::super::{V2Accs, V2Args}; -/// Derive quote args and header lookahead -pub fn derive_qa_hla( +pub fn derive_qa_prog_accs( + bef: &AccountMap, + aft: &AccountMap, + args: &V2Args, + curr_epoch: u64, + curr_slot: u64, +) -> ([PoolStateV2; 2], [Vec; 2], QuoteArgsAg) { + let ps_aft = + PoolStateV2Packed::of_acc_data(&aft[&(*args.accs.ix_prefix.pool_state()).into()].data) + .unwrap() + .into_pool_state_v2(); + let list_aft = get_lst_state_list(&aft[&(*args.accs.ix_prefix.lst_state_list()).into()].data); + let (qa, ps_aft_header_la, list_aft_header_la) = + derive_qa_hla(bef, args, curr_epoch, curr_slot); + ( + [ps_aft_header_la, ps_aft], + [list_aft_header_la, list_aft], + qa, + ) +} + +pub fn derive_qa_hla( am: &AccountMap, - args: &V2Args, + args: &V2Args, curr_epoch: u64, curr_slot: u64, - // passthrough to generalize - // across both ExactIn and ExactOut - pricing: P, -) -> ( - QuoteArgs, - PoolStateV2, - Vec, -) { +) -> (QuoteArgsAg, PoolStateV2, Vec) { + let pricing = derive_pp(am, &args.accs); let ((inp_calc, out_calc, ps_aft_header_la, list_aft_header_la), out_reserves) = if args .inp_lst_index == u32::MAX @@ -71,9 +87,9 @@ pub fn derive_qa_hla( /// `_cahla` - `calcs and header lookahead` /// Returns (inp_calc, out_calc, ps_header_lookahead) -fn derive_swap_cahla

( +fn derive_swap_cahla( am: &AccountMap, - args: &V2Args

, + args: &V2Args, curr_epoch: u64, curr_slot: u64, ) -> (SvcCalcAg, SvcCalcAg, PoolStateV2, Vec) { @@ -101,9 +117,9 @@ fn derive_swap_cahla

( (inp_calc, out_calc, ps, list) } -fn derive_add_liq_cahla

( +fn derive_add_liq_cahla( am: &AccountMap, - args: &V2Args

, + args: &V2Args, curr_epoch: u64, curr_slot: u64, ) -> (SvcCalcAg, SvcCalcAg, PoolStateV2, Vec) { @@ -130,9 +146,9 @@ fn derive_add_liq_cahla

( ) } -fn derive_rem_liq_cahla

( +fn derive_rem_liq_cahla( am: &AccountMap, - args: &V2Args

, + args: &V2Args, curr_epoch: u64, curr_slot: u64, ) -> (SvcCalcAg, SvcCalcAg, PoolStateV2, Vec) { @@ -180,31 +196,14 @@ fn ps_header_lookahead( header_lookahead(ps, calcs, curr_slot) } -pub fn derive_pp_exact_in( - am: &AccountMap, - accs: &V2Accs, -) -> FlatSlabSwapPricing { - match accs.pricing { - PricingAg::FlatSlab(p) => flatslab_pricing(am, accs, &p), - PricingAg::FlatFee(_) => todo!(), - } -} - -pub fn derive_pp_exact_out( - am: &AccountMap, - accs: &V2Accs, -) -> FlatSlabSwapPricing { +pub fn derive_pp(am: &AccountMap, accs: &V2Accs) -> PricingSwapAg { match accs.pricing { - PricingAg::FlatSlab(p) => flatslab_pricing(am, accs, &p), + PricingAg::FlatSlab(p) => PricingAg::FlatSlab(flatslab_pricing(am, accs, &p)), PricingAg::FlatFee(_) => todo!(), } } -fn flatslab_pricing( - am: &AccountMap, - accs: &V2Accs, - p: &FlatSlabPpAccs, -) -> FlatSlabSwapPricing { +fn flatslab_pricing(am: &AccountMap, accs: &V2Accs, p: &FlatSlabPpAccs) -> FlatSlabSwapPricing { Slab::of_acc_data(&am[&(*p.0.slab()).into()].data) .unwrap() .entries() diff --git a/controller/program/tests/tests/swap/common/strats.rs b/controller/program/tests/tests/swap/common/strats.rs index 3741119..9735bad 100644 --- a/controller/program/tests/tests/swap/common/strats.rs +++ b/controller/program/tests/tests/swap/common/strats.rs @@ -1,15 +1,35 @@ use inf1_ctl_jiminy::{ - accounts::{lst_state_list::LstStatePackedList, pool_state::PoolStateV2}, + accounts::{ + lst_state_list::LstStatePackedList, + pool_state::{PoolStateV2, PoolStateV2Addrs}, + }, + svc::InfDummyCalcAccs, typedefs::pool_sv::PoolSvMutRefs, }; -use inf1_svc_ag_core::inf1_svc_wsol_core; +use inf1_pp_ag_core::{PricingAg, PricingAgTy}; +use inf1_pp_core::pair::Pair; +use inf1_svc_ag_core::{ + inf1_svc_wsol_core::{self, instructions::sol_val_calc::WsolCalcAccs}, + instructions::SvcCalcAccsAg, + SvcAg, SvcAgTy, +}; use inf1_test_utils::{ - any_lst_state, any_lst_state_list, any_pool_state_v2, pool_sv_lamports_solvent_strat, + any_lst_state, any_lst_state_list, any_pool_state_v2, bals_from_supply, n_distinct_normal_pks, + pool_state_v2_u64s_with_last_release_slot_bef_incl, pool_state_v2_u8_bools_normal_strat, + pool_sv_lamports_solvent_strat, reasonable_flatslab_strat_for_mints, AccountMap, AnyLstStateArgs, LstStateListData, LstStatePks, NewLstStatePksBuilder, PoolStateV2FtaStrat, - WSOL_MINT, + VerPS, MAX_REASONABLE_FLATSLAB_PRICING, MAX_WSOL_BALANCE, WSOL_MINT, }; use proptest::prelude::*; +use crate::tests::swap::{ + common::{ + fill_swap_prog_accs, swap_pre_accs, NewSwapTokenAddrsBuilder, NewSwapTokenU64sBuilder, + SwapTokenArg, + }, + V2Accs, V2Args, +}; + pub fn wsol_lst_state_pks() -> LstStatePks>> { LstStatePks( NewLstStatePksBuilder::start() @@ -58,3 +78,141 @@ pub fn swap_prog_accs_strat( (idxs, lsl, ps) }) } + +/// Returns `(curr_slot, args, account_map)` +pub fn wsol_add_liq_zero_inf_exact_in_strat() -> impl Strategy { + bals_from_supply(u64::MAX) + .prop_filter("inp_amt should be > 0", |([_sol_val, inp_amt], _)| { + *inp_amt > 0 + }) + .prop_map(|(bals, _)| bals) + .prop_flat_map(wsol_add_liq_zero_inf_exact_in_inner) +} + +/// # Params +/// - `sol_val` SOL value of pool's current wsol holdings (assumes wsol is the only mint in the pool) +/// - `inp_amt` swap input amount ie args.amount +fn wsol_add_liq_zero_inf_exact_in_inner( + [sol_val, inp_amt]: [u64; 2], +) -> impl Strategy { + any::() + .prop_flat_map(move |curr_slot| { + ( + n_distinct_normal_pks(), + swap_prog_accs_strat( + [AnyLstStateArgs { + pks: wsol_lst_state_pks(), + sol_value: Some(Just(sol_val).boxed()), + is_input_disabled: Some(Just(false).boxed()), + ..Default::default() + }], + PoolStateV2FtaStrat { + u64s: pool_state_v2_u64s_with_last_release_slot_bef_incl( + Default::default(), + curr_slot, + ), + u8_bools: pool_state_v2_u8_bools_normal_strat(), + addrs: PoolStateV2Addrs::default().with_pricing_program(Some( + Just(*PricingAgTy::FlatSlab(()).program_id()).boxed(), + )), + ..Default::default() + }, + ) + .prop_flat_map(|([idx], lsl, ps)| { + ( + reasonable_flatslab_strat_for_mints( + [ps.lp_token_mint, WSOL_MINT.to_bytes()] + .into_iter() + .collect(), + ), + Just((idx, lsl, ps)), + ) + }), + Just(curr_slot), + Just((sol_val, inp_amt)), + ) + }) + .prop_map( + |( + [signer, inp_acc, out_acc], + ((pp_accs, pp_am), (idx, lsl, ps)), + curr_slot, + (wsol_sol_val, inp_amt), + )| { + let (ix_prefix, ix_prefix_am) = swap_pre_accs( + &signer, + &VerPS::V2(ps), + &lsl, + &Pair { + inp: SwapTokenArg { + u64s: NewSwapTokenU64sBuilder::start() + // just give user max token balance so that + // he is able to pay for any quote + .with_acc_bal(MAX_WSOL_BALANCE) + .with_mint_supply(u64::MAX) + .with_reserves_bal(wsol_sol_val) + .build(), + addrs: NewSwapTokenAddrsBuilder::start() + .with_acc(inp_acc) + .with_mint(WSOL_MINT.to_bytes()) + .build(), + }, + out: SwapTokenArg { + u64s: NewSwapTokenU64sBuilder::start() + .with_acc_bal(0) + // always 0 LP mint supply + .with_mint_supply(0) + .with_reserves_bal(0) + .build(), + addrs: NewSwapTokenAddrsBuilder::start() + .with_acc(out_acc) + .with_mint(ps.lp_token_mint) + .build(), + }, + }, + ); + + let accs = V2Accs { + ix_prefix, + inp_calc_prog: *SvcAgTy::Wsol(()).svc_program_id(), + inp_calc: SvcAg::Wsol(WsolCalcAccs), + out_calc_prog: inf1_ctl_jiminy::ID, + out_calc: SvcCalcAccsAg::Inf(InfDummyCalcAccs), + pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), + pricing: PricingAg::FlatSlab(pp_accs), + }; + let args = V2Args { + inp_lst_index: idx.try_into().unwrap(), + out_lst_index: u32::MAX, + limit: 0, + amount: inp_amt, + accs, + }; + + let mut bef = ix_prefix_am.into_iter().chain(pp_am).collect(); + fill_swap_prog_accs(&mut bef, &accs); + + (curr_slot, args, bef) + }, + ) +} + +/// Returns `(curr_slot, args, account_map)` +pub fn wsol_add_liq_zero_inf_exact_out_strat() -> impl Strategy { + let ppr = MAX_REASONABLE_FLATSLAB_PRICING.out_ratio().unwrap(); + let max_out_amt = ppr.apply(u64::MAX).unwrap(); + + (1..=max_out_amt) + .prop_flat_map(move |out_amt| { + ( + 0..=u64::MAX - *ppr.reverse(out_amt).unwrap().end(), + Just(out_amt), + ) + }) + .prop_map(|(sol_val, out_amt)| [sol_val, out_amt]) + .prop_flat_map(wsol_add_liq_zero_inf_exact_in_inner) + .prop_map(|(curr_slot, mut args, bef)| { + args.limit = u64::MAX; + (curr_slot, args, bef) + }) +} diff --git a/controller/program/tests/tests/swap/mod.rs b/controller/program/tests/tests/swap/mod.rs index 7953c99..f1d3c34 100644 --- a/controller/program/tests/tests/swap/mod.rs +++ b/controller/program/tests/tests/swap/mod.rs @@ -2,19 +2,40 @@ use inf1_ctl_jiminy::instructions::{ liquidity as ctl_liq, swap::{v1 as ctl_v1, v2 as ctl_v2}, }; -use inf1_pp_flatslab_std::instructions::pricing::FlatSlabPpAccs; -use inf1_std::instructions::{liquidity, swap}; -use inf1_svc_ag_core::instructions::SvcCalcAccsAg; +use inf1_pp_ag_core::{ + inf1_pp_flatfee_core::{ + instructions::pricing::price::FlatFeePriceAccs, pricing::price::FlatFeeSwapPricing, + }, + PricingAg, +}; +use inf1_pp_flatslab_std::{instructions::pricing::FlatSlabPpAccs, pricing::FlatSlabSwapPricing}; +use inf1_std::{ + instructions::{liquidity, swap}, + quote::swap::QuoteArgs, +}; +use inf1_svc_ag_core::{calc::SvcCalcAg, instructions::SvcCalcAccsAg}; mod common; mod v1; mod v2; -type V1Accs

= swap::IxAccs<[u8; 32], ctl_v1::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, P>; -type V1Args

= swap::IxArgs<[u8; 32], ctl_v1::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, P>; +/// impls both PriceExactInAccs and PriceExactOutAccs (but not deprectated LP interfaces) +type PricingSwapAccsAg = PricingAg; + +/// impls both PriceExactIn and PriceExactOut (but not deprectated LP interfaces) +type PricingSwapAg = PricingAg; + +type QuoteArgsAg = QuoteArgs; + +type V1Accs = + swap::IxAccs<[u8; 32], ctl_v1::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, PricingSwapAccsAg>; +type V1Args = + swap::IxArgs<[u8; 32], ctl_v1::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, PricingSwapAccsAg>; -type V2Accs

= swap::IxAccs<[u8; 32], ctl_v2::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, P>; -type V2Args

= swap::IxArgs<[u8; 32], ctl_v2::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, P>; +type V2Accs = + swap::IxAccs<[u8; 32], ctl_v2::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, PricingSwapAccsAg>; +type V2Args = + swap::IxArgs<[u8; 32], ctl_v2::IxPreKeysOwned, SvcCalcAccsAg, SvcCalcAccsAg, PricingSwapAccsAg>; // only flatslab has uniform interface across all 4 pp instructions type LiqAccs = liquidity::IxAccs<[u8; 32], ctl_liq::IxPreKeysOwned, SvcCalcAccsAg, FlatSlabPpAccs>; diff --git a/controller/program/tests/tests/swap/v1/add_liq.rs b/controller/program/tests/tests/swap/v1/add_liq.rs index 8577d4f..787866f 100644 --- a/controller/program/tests/tests/swap/v1/add_liq.rs +++ b/controller/program/tests/tests/swap/v1/add_liq.rs @@ -64,7 +64,7 @@ fn args_to_v2( pricing, }, }: LiqArgs, -) -> V2Args { +) -> V2Args { V2Args { inp_lst_index: lst_index, out_lst_index: u32::MAX, diff --git a/controller/program/tests/tests/swap/v1/exact_in.rs b/controller/program/tests/tests/swap/v1/exact_in.rs index 5936613..d9122d8 100644 --- a/controller/program/tests/tests/swap/v1/exact_in.rs +++ b/controller/program/tests/tests/swap/v1/exact_in.rs @@ -27,9 +27,7 @@ use crate::{ }, }; -type Args = V1Args; - -fn to_ix(args: &Args) -> Instruction { +fn to_ix(args: &V1Args) -> Instruction { let accounts = keys_signer_writable_to_metas( swap_exact_in_ix_keys_owned(&args.accs).seq(), swap_exact_in_ix_is_signer(&args.accs).seq(), @@ -45,7 +43,7 @@ fn to_ix(args: &Args) -> Instruction { /// Returns `None` if expected_err is `Some` fn swap_exact_in_test( svm: &Mollusk, - args: &Args, + args: &V1Args, bef: &AccountMap, expected_err: Option>, ) -> Option { diff --git a/controller/program/tests/tests/swap/v1/exact_out.rs b/controller/program/tests/tests/swap/v1/exact_out.rs index 915060e..06ca480 100644 --- a/controller/program/tests/tests/swap/v1/exact_out.rs +++ b/controller/program/tests/tests/swap/v1/exact_out.rs @@ -27,9 +27,7 @@ use crate::{ }, }; -type Args = V1Args; - -fn to_ix(args: &Args) -> Instruction { +fn to_ix(args: &V1Args) -> Instruction { let accounts = keys_signer_writable_to_metas( swap_exact_out_ix_keys_owned(&args.accs).seq(), swap_exact_out_ix_is_signer(&args.accs).seq(), @@ -45,7 +43,7 @@ fn to_ix(args: &Args) -> Instruction { /// Returns `None` if expected_err is `Some` fn swap_exact_out_test( svm: &Mollusk, - args: &Args, + args: &V1Args, bef: &AccountMap, expected_err: Option>, ) -> Option { diff --git a/controller/program/tests/tests/swap/v1/mod.rs b/controller/program/tests/tests/swap/v1/mod.rs index f978e35..732e2d2 100644 --- a/controller/program/tests/tests/swap/v1/mod.rs +++ b/controller/program/tests/tests/swap/v1/mod.rs @@ -11,7 +11,7 @@ mod exact_in; mod exact_out; mod rem_liq; -fn args_to_v2

( +fn args_to_v2( V1Args { inp_lst_index, out_lst_index, @@ -27,8 +27,8 @@ fn args_to_v2

( pricing_prog, pricing, }, - }: V1Args

, -) -> V2Args

{ + }: V1Args, +) -> V2Args { V2Args { inp_lst_index, out_lst_index, diff --git a/controller/program/tests/tests/swap/v1/rem_liq.rs b/controller/program/tests/tests/swap/v1/rem_liq.rs index 2242caf..54b167d 100644 --- a/controller/program/tests/tests/swap/v1/rem_liq.rs +++ b/controller/program/tests/tests/swap/v1/rem_liq.rs @@ -52,7 +52,7 @@ fn args_to_v2( pricing, }, }: LiqArgs, -) -> V2Args { +) -> V2Args { V2Args { inp_lst_index: u32::MAX, out_lst_index: lst_index, diff --git a/controller/program/tests/tests/swap/v2/exact_in/add_liq.rs b/controller/program/tests/tests/swap/v2/exact_in/add_liq.rs index 9300af4..ace1594 100644 --- a/controller/program/tests/tests/swap/v2/exact_in/add_liq.rs +++ b/controller/program/tests/tests/swap/v2/exact_in/add_liq.rs @@ -1,35 +1,27 @@ use expect_test::expect; use inf1_ctl_jiminy::{ - accounts::pool_state::PoolStateV2Addrs, instructions::swap::v2::{exact_in::NewSwapExactInV2IxPreAccsBuilder, IxPreAccs}, - keys::{LST_STATE_LIST_ID, POOL_STATE_ID}, svc::InfDummyCalcAccs, }; use inf1_pp_ag_core::{PricingAg, PricingAgTy}; use inf1_std::quote::Quote; -use inf1_svc_ag_core::{ - inf1_svc_wsol_core::instructions::sol_val_calc::WsolCalcAccs, instructions::SvcCalcAccsAg, - SvcAg, SvcAgTy, -}; +use inf1_svc_ag_core::{instructions::SvcCalcAccsAg, SvcAg, SvcAgTy}; use inf1_test_utils::{ - bals_from_supply, flatslab_fixture_suf_accs, jupsol_fixture_svc_suf_accs, - lst_state_list_account, mock_mint, mock_sys_acc, mock_token_acc, mollusk_with_clock_override, - n_distinct_normal_pks, pool_state_v2_account, - pool_state_v2_u64s_with_last_release_slot_bef_incl, pool_state_v2_u8_bools_normal_strat, - raw_mint, raw_token_acc, reasonable_flatslab_strat_for_mints, silence_mollusk_logs, AccountMap, - AnyLstStateArgs, ClockArgs, ClockU64s, KeyedUiAccount, PoolStateV2FtaStrat, - JUPSOL_FIXTURE_LST_IDX, WSOL_MINT, + flatslab_fixture_suf_accs, jupsol_fixture_svc_suf_accs, mollusk_with_clock_override, + silence_mollusk_logs, ClockArgs, ClockU64s, KeyedUiAccount, JUPSOL_FIXTURE_LST_IDX, }; use jiminy_cpi::program_error::ProgramError; use proptest::prelude::*; -use solana_pubkey::Pubkey; use crate::{ common::{SVM, SVM_MUT}, - tests::swap::common::{fill_swap_prog_accs, swap_prog_accs_strat, wsol_lst_state_pks}, + tests::swap::{ + common::{fill_swap_prog_accs, wsol_add_liq_zero_inf_exact_in_strat}, + V2Accs, V2Args, + }, }; -use super::{swap_exact_in_v2_test, Accs, Args}; +use super::swap_exact_in_v2_test; #[test] fn swap_exact_in_v2_jupsol_add_liq_fixture() { @@ -58,7 +50,7 @@ fn swap_exact_in_v2_jupsol_add_liq_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (inp_accs, inp_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -67,7 +59,7 @@ fn swap_exact_in_v2_jupsol_add_liq_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: u32::MAX, limit: 0, @@ -91,118 +83,10 @@ fn swap_exact_in_v2_jupsol_add_liq_fixture() { .assert_debug_eq(&(inp, out, fee)); } -fn add_liq_wsol_zero_inf_strat() -> impl Strategy { - let sol_val_and_inp_amt = bals_from_supply::<2>(u64::MAX).prop_map(|(bals, _)| bals); - - (any::(), sol_val_and_inp_amt) - .prop_flat_map(|(curr_slot, [sol_val, inp_amt])| { - ( - n_distinct_normal_pks(), - swap_prog_accs_strat( - [AnyLstStateArgs { - pks: wsol_lst_state_pks(), - sol_value: Some(Just(sol_val).boxed()), - is_input_disabled: Some(Just(false).boxed()), - ..Default::default() - }], - PoolStateV2FtaStrat { - u64s: pool_state_v2_u64s_with_last_release_slot_bef_incl( - Default::default(), - curr_slot, - ), - u8_bools: pool_state_v2_u8_bools_normal_strat(), - addrs: PoolStateV2Addrs::default().with_pricing_program(Some( - Just(*PricingAgTy::FlatSlab(()).program_id()).boxed(), - )), - ..Default::default() - }, - ) - .prop_flat_map(|([idx], lsl, ps)| { - ( - reasonable_flatslab_strat_for_mints( - [ps.lp_token_mint, WSOL_MINT.to_bytes()] - .into_iter() - .collect(), - ), - Just((idx, lsl, ps)), - ) - }), - Just(curr_slot), - Just((sol_val, inp_amt)), - ) - }) - .prop_map( - |( - [signer, inp_acc, out_acc], - ((pp_accs, pp_am), (idx, lsl, ps)), - curr_slot, - (wsol_sol_val, inp_amt), - )| { - let lp_mint = ( - Pubkey::new_from_array(ps.lp_token_mint), - // always 0 supply - mock_mint(raw_mint(Some(POOL_STATE_ID), None, 0, 9)), - ); - let accounts = NewSwapExactInV2IxPreAccsBuilder::start() - .with_signer((signer.into(), mock_sys_acc(1_000_000_000))) - .with_inp_acc(( - inp_acc.into(), - mock_token_acc(raw_token_acc(WSOL_MINT.to_bytes(), signer, inp_amt)), - )) - .with_out_acc(( - out_acc.into(), - mock_token_acc(raw_token_acc(ps.lp_token_mint, signer, 0)), - )) - .with_inp_mint((WSOL_MINT, mock_mint(raw_mint(None, None, u64::MAX, 9)))) - .with_inp_pool_reserves(( - lsl.all_pool_reserves[WSOL_MINT.as_array()].into(), - mock_token_acc(raw_token_acc( - WSOL_MINT.to_bytes(), - POOL_STATE_ID, - wsol_sol_val, - )), - )) - .with_inp_token_program(mollusk_svm_programs_token::token::keyed_account()) - .with_out_mint(lp_mint.clone()) - .with_out_pool_reserves(lp_mint) - .with_out_token_program(mollusk_svm_programs_token::token::keyed_account()) - .with_pool_state((POOL_STATE_ID.into(), pool_state_v2_account(ps))) - .with_lst_state_list(( - LST_STATE_LIST_ID.into(), - lst_state_list_account(lsl.lst_state_list), - )) - .build(); - let ix_prefix = IxPreAccs(accounts.0.each_ref().map(|(pk, _)| pk.to_bytes())); - - let accs = Accs { - ix_prefix, - inp_calc_prog: *SvcAgTy::Wsol(()).svc_program_id(), - inp_calc: SvcAg::Wsol(WsolCalcAccs), - out_calc_prog: inf1_ctl_jiminy::ID, - out_calc: SvcCalcAccsAg::Inf(InfDummyCalcAccs), - pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), - pricing: PricingAg::FlatSlab(pp_accs), - }; - let args = Args { - inp_lst_index: idx.try_into().unwrap(), - out_lst_index: u32::MAX, - limit: 0, - amount: inp_amt, - accs, - }; - - let mut bef = accounts.0.into_iter().chain(pp_am).collect(); - fill_swap_prog_accs(&mut bef, &accs); - - (curr_slot, args, bef) - }, - ) -} - proptest! { #[test] fn swap_exact_in_v2_wsol_add_from_zero_lp_supply( - (slot, args, bef) in add_liq_wsol_zero_inf_strat() + (slot, args, bef) in wsol_add_liq_zero_inf_exact_in_strat() ) { silence_mollusk_logs(); diff --git a/controller/program/tests/tests/swap/v2/exact_in/errs.rs b/controller/program/tests/tests/swap/v2/exact_in/errs.rs index 4a7c43b..5a9a608 100644 --- a/controller/program/tests/tests/swap/v2/exact_in/errs.rs +++ b/controller/program/tests/tests/swap/v2/exact_in/errs.rs @@ -19,10 +19,8 @@ use crate::{ common::SVM, tests::swap::{ common::fill_swap_prog_accs, - v2::{ - exact_in::{swap_exact_in_v2_test, Accs, Args}, - jupsol_to_wsol_prefix_fixtures, - }, + v2::{exact_in::swap_exact_in_v2_test, jupsol_to_wsol_prefix_fixtures}, + V2Accs, V2Args, }, }; @@ -43,7 +41,7 @@ fn swap_exact_in_input_disabled_fixture() { .unwrap(); U8BoolMut(&mut unsafe { lst_state.as_lst_state_mut() }.is_input_disabled).set_true(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -52,7 +50,7 @@ fn swap_exact_in_input_disabled_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: 0, @@ -85,7 +83,7 @@ fn swap_exact_in_pool_rebalancing_fixture() { *pool_state.is_rebalancing_mut() = 1; prefix_am.pool_state_mut().1 = pool_state.into_account(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -94,7 +92,7 @@ fn swap_exact_in_pool_rebalancing_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: 0, @@ -127,7 +125,7 @@ fn swap_exact_in_pool_disabled_fixture() { *pool_state.is_disabled_mut() = 1; prefix_am.pool_state_mut().1 = pool_state.into_account(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -136,7 +134,7 @@ fn swap_exact_in_pool_disabled_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: 0, @@ -167,7 +165,7 @@ fn swap_exact_in_slippage_tolerance_exceeded_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (inp_accs, inp_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -176,7 +174,7 @@ fn swap_exact_in_slippage_tolerance_exceeded_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit, @@ -225,7 +223,7 @@ fn swap_exact_in_same_lst_fixture() { let [inp_calc, out_calc] = core::array::from_fn(|_| SvcCalcAccsAg::Wsol(WsolCalcAccs)); let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::Wsol(()).svc_program_id(), inp_calc, @@ -234,7 +232,7 @@ fn swap_exact_in_same_lst_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit, diff --git a/controller/program/tests/tests/swap/v2/exact_in/mod.rs b/controller/program/tests/tests/swap/v2/exact_in/mod.rs index ddea747..11657d1 100644 --- a/controller/program/tests/tests/swap/v2/exact_in/mod.rs +++ b/controller/program/tests/tests/swap/v2/exact_in/mod.rs @@ -1,5 +1,4 @@ use inf1_ctl_jiminy::{instructions::swap::v2::exact_in::SwapExactInIxData, ID}; -use inf1_pp_ag_core::instructions::PriceExactInAccsAg; use inf1_std::{ instructions::swap::v2::exact_in::{ swap_exact_in_v2_ix_is_signer, swap_exact_in_v2_ix_is_writer, @@ -15,17 +14,14 @@ use mollusk_svm::Mollusk; use solana_instruction::Instruction; use solana_pubkey::Pubkey; -use crate::tests::swap::common::assert_correct_swap_exact_in_v2; +use crate::tests::swap::{common::assert_correct_swap_exact_in_v2, V2Args}; mod add_liq; mod errs; mod rem_liq; mod swap; -type Accs = super::super::V2Accs; -type Args = super::super::V2Args; - -fn to_ix(args: &Args) -> Instruction { +fn to_ix(args: &V2Args) -> Instruction { let accounts = keys_signer_writable_to_metas( swap_exact_in_v2_ix_keys_owned(&args.accs).seq(), swap_exact_in_v2_ix_is_signer(&args.accs).seq(), @@ -41,7 +37,7 @@ fn to_ix(args: &Args) -> Instruction { /// Returns `None` if expected_err is `Some` fn swap_exact_in_v2_test( svm: &Mollusk, - args: &Args, + args: &V2Args, bef: &AccountMap, expected_err: Option>, ) -> Option { diff --git a/controller/program/tests/tests/swap/v2/exact_in/rem_liq.rs b/controller/program/tests/tests/swap/v2/exact_in/rem_liq.rs index fb4caab..c1c7f7c 100644 --- a/controller/program/tests/tests/swap/v2/exact_in/rem_liq.rs +++ b/controller/program/tests/tests/swap/v2/exact_in/rem_liq.rs @@ -11,9 +11,12 @@ use inf1_test_utils::{ }; use jiminy_cpi::program_error::ProgramError; -use crate::{common::SVM, tests::swap::common::fill_swap_prog_accs}; +use crate::{ + common::SVM, + tests::swap::{common::fill_swap_prog_accs, V2Accs, V2Args}, +}; -use super::{swap_exact_in_v2_test, Accs, Args}; +use super::swap_exact_in_v2_test; #[test] fn swap_exact_in_v2_jupsol_rem_liq_fixture() { @@ -42,7 +45,7 @@ fn swap_exact_in_v2_jupsol_rem_liq_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (out_accs, out_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: inf1_ctl_jiminy::ID, inp_calc: SvcCalcAccsAg::Inf(InfDummyCalcAccs), @@ -51,7 +54,7 @@ fn swap_exact_in_v2_jupsol_rem_liq_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: u32::MAX, out_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: 0, diff --git a/controller/program/tests/tests/swap/v2/exact_in/swap.rs b/controller/program/tests/tests/swap/v2/exact_in/swap.rs index f9254e2..439e267 100644 --- a/controller/program/tests/tests/swap/v2/exact_in/swap.rs +++ b/controller/program/tests/tests/swap/v2/exact_in/swap.rs @@ -14,10 +14,12 @@ use jiminy_cpi::program_error::ProgramError; use crate::{ common::SVM, - tests::swap::{common::fill_swap_prog_accs, v2::jupsol_to_wsol_prefix_fixtures}, + tests::swap::{ + common::fill_swap_prog_accs, v2::jupsol_to_wsol_prefix_fixtures, V2Accs, V2Args, + }, }; -use super::{swap_exact_in_v2_test, Accs, Args}; +use super::swap_exact_in_v2_test; #[test] fn swap_exact_in_v2_jupsol_to_wsol_fixture() { @@ -28,7 +30,7 @@ fn swap_exact_in_v2_jupsol_to_wsol_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (inp_accs, inp_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -37,7 +39,7 @@ fn swap_exact_in_v2_jupsol_to_wsol_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: 0, diff --git a/controller/program/tests/tests/swap/v2/exact_out/add_liq.rs b/controller/program/tests/tests/swap/v2/exact_out/add_liq.rs index 56283b0..7ac92be 100644 --- a/controller/program/tests/tests/swap/v2/exact_out/add_liq.rs +++ b/controller/program/tests/tests/swap/v2/exact_out/add_liq.rs @@ -7,13 +7,21 @@ use inf1_pp_ag_core::{PricingAg, PricingAgTy}; use inf1_std::quote::Quote; use inf1_svc_ag_core::{instructions::SvcCalcAccsAg, SvcAg, SvcAgTy}; use inf1_test_utils::{ - flatslab_fixture_suf_accs, jupsol_fixture_svc_suf_accs, KeyedUiAccount, JUPSOL_FIXTURE_LST_IDX, + flatslab_fixture_suf_accs, jupsol_fixture_svc_suf_accs, mollusk_with_clock_override, + silence_mollusk_logs, ClockArgs, ClockU64s, KeyedUiAccount, JUPSOL_FIXTURE_LST_IDX, }; use jiminy_cpi::program_error::ProgramError; +use proptest::prelude::*; -use crate::{common::SVM, tests::swap::common::fill_swap_prog_accs}; +use crate::{ + common::{SVM, SVM_MUT}, + tests::swap::{ + common::{fill_swap_prog_accs, wsol_add_liq_zero_inf_exact_out_strat}, + V2Accs, V2Args, + }, +}; -use super::{swap_exact_out_v2_test, Accs, Args}; +use super::swap_exact_out_v2_test; #[test] fn swap_exact_out_v2_jupsol_add_liq_fixture() { @@ -42,7 +50,7 @@ fn swap_exact_out_v2_jupsol_add_liq_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (inp_accs, inp_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -53,7 +61,7 @@ fn swap_exact_out_v2_jupsol_add_liq_fixture() { }; let mut bef = prefix_am.0.into_iter().chain(pp_am).chain(inp_am).collect(); fill_swap_prog_accs(&mut bef, &accs); - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: u32::MAX, limit: u64::MAX, @@ -73,3 +81,26 @@ fn swap_exact_out_v2_jupsol_add_liq_fixture() { "#]] .assert_debug_eq(&(inp, out, fee)); } + +proptest! { + #[test] + fn swap_exact_out_v2_wsol_add_from_zero_lp_supply( + (slot, args, bef) in wsol_add_liq_zero_inf_exact_out_strat() + ) { + silence_mollusk_logs(); + + let quote = SVM_MUT.with_borrow_mut( + |svm| mollusk_with_clock_override( + svm, + &ClockArgs { + u64s: ClockU64s::default().with_slot(Some(slot)), + ..Default::default() + }, + |svm| swap_exact_out_v2_test(svm, &args, &bef, None::).unwrap(), + ) + ); + // since we're adding from 0 and wsol=sol, + // we should be getting 1:1 minting + prop_assert_eq!(quote.inp, quote.out + quote.fee); + } +} diff --git a/controller/program/tests/tests/swap/v2/exact_out/errs.rs b/controller/program/tests/tests/swap/v2/exact_out/errs.rs index 9d218ec..3620166 100644 --- a/controller/program/tests/tests/swap/v2/exact_out/errs.rs +++ b/controller/program/tests/tests/swap/v2/exact_out/errs.rs @@ -19,10 +19,8 @@ use crate::{ common::SVM, tests::swap::{ common::fill_swap_prog_accs, - v2::{ - exact_out::{swap_exact_out_v2_test, Accs, Args}, - jupsol_to_wsol_prefix_fixtures, - }, + v2::{exact_out::swap_exact_out_v2_test, jupsol_to_wsol_prefix_fixtures}, + V2Accs, V2Args, }, }; @@ -43,7 +41,7 @@ fn swap_exact_out_input_disabled_fixture() { .unwrap(); U8BoolMut(&mut unsafe { lst_state.as_lst_state_mut() }.is_input_disabled).set_true(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -52,7 +50,7 @@ fn swap_exact_out_input_disabled_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: u64::MAX, @@ -85,7 +83,7 @@ fn swap_exact_out_pool_rebalancing_fixture() { *pool_state.is_rebalancing_mut() = 1; prefix_am.pool_state_mut().1 = pool_state.into_account(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -94,7 +92,7 @@ fn swap_exact_out_pool_rebalancing_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: u64::MAX, @@ -127,7 +125,7 @@ fn swap_exact_out_pool_disabled_fixture() { *pool_state.is_disabled_mut() = 1; prefix_am.pool_state_mut().1 = pool_state.into_account(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -136,7 +134,7 @@ fn swap_exact_out_pool_disabled_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: u64::MAX, @@ -167,7 +165,7 @@ fn swap_exact_out_slippage_tolerance_exceeded_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (inp_accs, inp_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -176,7 +174,7 @@ fn swap_exact_out_slippage_tolerance_exceeded_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit, @@ -225,7 +223,7 @@ fn swap_exact_out_same_lst_fixture() { let [inp_calc, out_calc] = core::array::from_fn(|_| SvcCalcAccsAg::Wsol(WsolCalcAccs)); let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::Wsol(()).svc_program_id(), inp_calc, @@ -234,7 +232,7 @@ fn swap_exact_out_same_lst_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit, diff --git a/controller/program/tests/tests/swap/v2/exact_out/mod.rs b/controller/program/tests/tests/swap/v2/exact_out/mod.rs index bdd5d22..5d1ea27 100644 --- a/controller/program/tests/tests/swap/v2/exact_out/mod.rs +++ b/controller/program/tests/tests/swap/v2/exact_out/mod.rs @@ -1,5 +1,4 @@ use inf1_ctl_jiminy::{instructions::swap::v2::exact_out::SwapExactOutIxData, ID}; -use inf1_pp_ag_core::instructions::PriceExactOutAccsAg; use inf1_std::{ instructions::swap::v2::exact_out::{ swap_exact_out_v2_ix_is_signer, swap_exact_out_v2_ix_is_writer, @@ -15,17 +14,14 @@ use mollusk_svm::Mollusk; use solana_instruction::Instruction; use solana_pubkey::Pubkey; -use crate::tests::swap::common::assert_correct_swap_exact_out_v2; +use crate::tests::swap::{common::assert_correct_swap_exact_out_v2, V2Args}; mod add_liq; mod errs; mod rem_liq; mod swap; -type Accs = super::super::V2Accs; -type Args = super::super::V2Args; - -fn to_ix(args: &Args) -> Instruction { +fn to_ix(args: &V2Args) -> Instruction { let accounts = keys_signer_writable_to_metas( swap_exact_out_v2_ix_keys_owned(&args.accs).seq(), swap_exact_out_v2_ix_is_signer(&args.accs).seq(), @@ -41,7 +37,7 @@ fn to_ix(args: &Args) -> Instruction { /// Returns `None` if expected_err is `Some` fn swap_exact_out_v2_test( svm: &Mollusk, - args: &Args, + args: &V2Args, bef: &AccountMap, expected_err: Option>, ) -> Option { diff --git a/controller/program/tests/tests/swap/v2/exact_out/rem_liq.rs b/controller/program/tests/tests/swap/v2/exact_out/rem_liq.rs index ce904d4..8b84702 100644 --- a/controller/program/tests/tests/swap/v2/exact_out/rem_liq.rs +++ b/controller/program/tests/tests/swap/v2/exact_out/rem_liq.rs @@ -11,9 +11,12 @@ use inf1_test_utils::{ }; use jiminy_cpi::program_error::ProgramError; -use crate::{common::SVM, tests::swap::common::fill_swap_prog_accs}; +use crate::{ + common::SVM, + tests::swap::{common::fill_swap_prog_accs, V2Accs, V2Args}, +}; -use super::{swap_exact_out_v2_test, Accs, Args}; +use super::swap_exact_out_v2_test; #[test] fn swap_exact_out_v2_jupsol_rem_liq_fixture() { @@ -42,7 +45,7 @@ fn swap_exact_out_v2_jupsol_rem_liq_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (out_accs, out_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: inf1_ctl_jiminy::ID, inp_calc: SvcCalcAccsAg::Inf(InfDummyCalcAccs), @@ -53,7 +56,7 @@ fn swap_exact_out_v2_jupsol_rem_liq_fixture() { }; let mut bef = prefix_am.0.into_iter().chain(pp_am).chain(out_am).collect(); fill_swap_prog_accs(&mut bef, &accs); - let args = Args { + let args = V2Args { inp_lst_index: u32::MAX, out_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: u64::MAX, diff --git a/controller/program/tests/tests/swap/v2/exact_out/swap.rs b/controller/program/tests/tests/swap/v2/exact_out/swap.rs index 4144619..779ec31 100644 --- a/controller/program/tests/tests/swap/v2/exact_out/swap.rs +++ b/controller/program/tests/tests/swap/v2/exact_out/swap.rs @@ -17,11 +17,10 @@ use crate::{ tests::swap::{ common::fill_swap_prog_accs, v2::{exact_out::swap_exact_out_v2_test, jupsol_to_wsol_prefix_fixtures}, + V2Accs, V2Args, }, }; -use super::{Accs, Args}; - #[test] fn swap_exact_out_v2_jupsol_to_wsol_fixture() { let amount = 10_000; @@ -31,7 +30,7 @@ fn swap_exact_out_v2_jupsol_to_wsol_fixture() { let (pp_accs, pp_am) = flatslab_fixture_suf_accs(); let (inp_accs, inp_am) = jupsol_fixture_svc_suf_accs(); - let accs = Accs { + let accs = V2Accs { ix_prefix: prefix_keys, inp_calc_prog: *SvcAgTy::SanctumSplMulti(()).svc_program_id(), inp_calc: SvcAg::SanctumSplMulti(inp_accs), @@ -40,7 +39,7 @@ fn swap_exact_out_v2_jupsol_to_wsol_fixture() { pricing_prog: *PricingAgTy::FlatSlab(()).program_id(), pricing: PricingAg::FlatSlab(pp_accs), }; - let args = Args { + let args = V2Args { inp_lst_index: JUPSOL_FIXTURE_LST_IDX.try_into().unwrap(), out_lst_index: WSOL_FIXTURE_LST_IDX.try_into().unwrap(), limit: u64::MAX, diff --git a/test-utils/src/accounts/token.rs b/test-utils/src/accounts/token.rs index 29a956b..bb62de2 100644 --- a/test-utils/src/accounts/token.rs +++ b/test-utils/src/accounts/token.rs @@ -17,6 +17,9 @@ const TOKEN_ACC_RENT_EXEMPTION: u64 = Rent::DEFAULT.min_balance(RawTokenAccount: const COPTION_NONE: [u8; 4] = [0; 4]; const COPTION_SOME: [u8; 4] = [1, 0, 0, 0]; +/// Max possible wSOL token acc balance (not u64::MAX due to `native_rent_exemption`) +pub const MAX_WSOL_BALANCE: u64 = u64::MAX - TOKEN_ACC_RENT_EXEMPTION; + /// Adapted from /// https://github.com/igneous-labs/sanctum-solana-utils/blob/dc8426210a11e2c74ff21ae272dee953d457d0cd/sanctum-solana-test-utils/src/token/tokenkeg.rs#L44-L84 pub fn raw_token_acc(mint: [u8; 32], auth: [u8; 32], amt: u64) -> RawTokenAccount { diff --git a/test-utils/src/diff/controller/pool_state/migrations.rs b/test-utils/src/diff/controller/pool_state/migrations.rs index c8df48c..d9d20db 100644 --- a/test-utils/src/diff/controller/pool_state/migrations.rs +++ b/test-utils/src/diff/controller/pool_state/migrations.rs @@ -59,6 +59,10 @@ impl VerPoolState { *each_variant_field!(self, total_sol_value) } + pub fn lp_token_mint(&self) -> &[u8; 32] { + each_variant_field!(self, lp_token_mint) + } + pub fn is_rebalancing_mut(&mut self) -> &mut u8 { each_variant_field_mut!(self, is_rebalancing) } diff --git a/test-utils/src/gen/flatslab.rs b/test-utils/src/gen/flatslab.rs index 2410075..a058c5e 100644 --- a/test-utils/src/gen/flatslab.rs +++ b/test-utils/src/gen/flatslab.rs @@ -2,12 +2,36 @@ use std::{array, collections::HashSet, iter::once}; use inf1_pp_flatslab_core::{ instructions::pricing::FlatSlabPpAccs, + pricing::FlatSlabSwapPricing, typedefs::{FeeNanos, SlabEntryPacked}, }; use proptest::{collection::vec, prelude::*}; use crate::{flatslab_acc_data, mock_flatslab_slab, AccountMap}; +macro_rules! const_fee_nanos { + ($x:expr) => { + match FeeNanos::new($x) { + Err(_) => unreachable!(), + Ok(x) => x, + } + }; +} + +pub const MIN_REASONABLE_FEE_NANOS: FeeNanos = const_fee_nanos!(0); + +pub const MIN_REASONABLE_FLATSLAB_PRICING: FlatSlabSwapPricing = FlatSlabSwapPricing { + inp_fee_nanos: MIN_REASONABLE_FEE_NANOS, + out_fee_nanos: MIN_REASONABLE_FEE_NANOS, +}; + +pub const MAX_REASONABLE_FEE_NANOS: FeeNanos = const_fee_nanos!(450_000_000); + +pub const MAX_REASONABLE_FLATSLAB_PRICING: FlatSlabSwapPricing = FlatSlabSwapPricing { + inp_fee_nanos: MAX_REASONABLE_FEE_NANOS, + out_fee_nanos: MAX_REASONABLE_FEE_NANOS, +}; + /// See [`reasonable_flatslab_data_strat`] pub fn reasonable_flatslab_strat_for_mints( mints: HashSet<[u8; 32]>, @@ -47,5 +71,6 @@ pub fn reasonable_flatslab_data_strat(mints: HashSet<[u8; 32]>) -> impl Strategy } fn reasonable_flatslab_fee_nanos_strat() -> impl Strategy { - (0..=450_000_000).prop_map(|n| FeeNanos::new(n).unwrap()) + (MIN_REASONABLE_FEE_NANOS.get()..=MAX_REASONABLE_FEE_NANOS.get()) + .prop_map(|n| FeeNanos::new(n).unwrap()) }