Skip to content

Commit 09ff97e

Browse files
[LST-417] More swap tests add liq from zero, ExactOut (#153)
* refactor * jebaited by my own type aliases 🤦 * more refactor * add exact out add liq from 0 tests * nit naming
1 parent 0e0ab51 commit 09ff97e

File tree

25 files changed

+636
-338
lines changed

25 files changed

+636
-338
lines changed

controller/core/src/instructions/swap/v2/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,68 @@ pub const IX_PRE_IS_WRITER: IxPreAccFlags = IxPreAccFlags::memset(true)
5757

5858
pub const IX_PRE_IS_SIGNER: IxPreAccFlags = IxPreAccFlags::memset(false).const_with_signer(true);
5959

60+
/// Utils
61+
impl<T> IxPreAccs<T> {
62+
/// For easier usage with type-aliases
63+
#[inline]
64+
pub const fn new(a: [T; IX_PRE_ACCS_LEN]) -> Self {
65+
Self(a)
66+
}
67+
68+
#[inline]
69+
pub const fn switch_inp_out(&mut self) -> &mut Self {
70+
let this = self.0.as_mut_slice();
71+
let idxs = [
72+
[IX_PRE_ACCS_IDX_INP_MINT, IX_PRE_ACCS_IDX_OUT_MINT],
73+
[IX_PRE_ACCS_IDX_INP_ACC, IX_PRE_ACCS_IDX_OUT_ACC],
74+
[
75+
IX_PRE_ACCS_IDX_INP_TOKEN_PROGRAM,
76+
IX_PRE_ACCS_IDX_OUT_TOKEN_PROGRAM,
77+
],
78+
[
79+
IX_PRE_ACCS_IDX_INP_POOL_RESERVES,
80+
IX_PRE_ACCS_IDX_OUT_POOL_RESERVES,
81+
],
82+
];
83+
84+
let mut i = 0;
85+
while i < idxs.len() {
86+
let [x, y] = idxs[i];
87+
this.swap(x, y);
88+
89+
i += 1;
90+
}
91+
92+
self
93+
}
94+
95+
#[inline]
96+
pub const fn with_switched_inp_out(mut self) -> Self {
97+
self.switch_inp_out();
98+
self
99+
}
100+
}
101+
102+
/// The accounts for one of the tokens (inp or out) involved in a swap
103+
#[generic_array_struct(builder pub)]
104+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
105+
#[repr(transparent)]
106+
pub struct SwapEntryAccs<T> {
107+
pub mint: T,
108+
pub acc: T,
109+
pub token_program: T,
110+
111+
/// Set to LP mint if `mint = LP mint`
112+
pub pool_reserves: T,
113+
}
114+
115+
impl<T: Copy> SwapEntryAccs<T> {
116+
#[inline]
117+
pub const fn memset(val: T) -> Self {
118+
Self([val; SWAP_ENTRY_ACCS_LEN])
119+
}
120+
}
121+
60122
// v1 conversions
61123

62124
impl<T: Clone> IxPreAccs<T> {

controller/core/src/svc.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,19 @@ impl InfCalc {
107107
/// `None` if pool is insolvent for LPers (lp_due is negative)
108108
#[inline]
109109
pub const fn lp_due_over_supply(&self) -> Option<Floor<Ratio<u64, u64>>> {
110-
let r = match self.pool_lamports.lp_due_checked() {
110+
let lp_due = match self.pool_lamports.lp_due_checked() {
111111
None => return None,
112-
Some(n) => Ratio {
113-
n,
112+
Some(x) => x,
113+
};
114+
let r = if self.mint_supply == 0 || (lp_due == 0 && *self.pool_lamports.withheld() == 0) {
115+
Ratio::<u64, u64>::ONE
116+
} else {
117+
Ratio {
118+
n: lp_due,
114119
d: self.mint_supply,
115-
},
120+
}
116121
};
117-
Some(Floor(
118-
if r.d == 0 || (r.n == 0 && *self.pool_lamports.withheld() == 0) {
119-
Ratio::<u64, u64>::ONE
120-
} else {
121-
r
122-
},
123-
))
122+
Some(Floor(r))
124123
}
125124
}
126125

@@ -258,9 +257,17 @@ mod tests {
258257

259258
proptest! {
260259
#[test]
261-
fn lp_due_over_supply_is_never_zero(calc in any_calc()) {
260+
fn lp_due_over_supply_zero_cases(calc in any_calc()) {
262261
let r = calc.lp_due_over_supply().unwrap();
263-
assert!(!r.0.is_zero());
262+
263+
// this is the only case where r should be 0
264+
if *calc.pool_lamports.withheld() != 0
265+
&& calc.pool_lamports.lp_due_checked().unwrap() == 0
266+
{
267+
assert!(r.0.is_zero());
268+
} else {
269+
assert!(!r.0.is_zero());
270+
}
264271
}
265272
}
266273

controller/program/tests/tests/swap/common/accounts.rs

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1+
use core::mem::take;
2+
3+
use generic_array_struct::generic_array_struct;
14
use inf1_core::instructions::swap::IxAccs;
2-
use inf1_test_utils::{fill_mock_prog_accs, AccountMap};
5+
use inf1_ctl_jiminy::{
6+
instructions::swap::v2::{
7+
exact_in::NewSwapExactInV2IxPreAccsBuilder, IxPreKeysOwned, NewSwapEntryAccsBuilder,
8+
SwapEntryAccs,
9+
},
10+
keys::{LST_STATE_LIST_ID, POOL_STATE_ID},
11+
};
12+
use inf1_pp_core::pair::Pair;
13+
use inf1_test_utils::{
14+
fill_mock_prog_accs, lst_state_list_account, mock_mint, mock_sys_acc, mock_token_acc, raw_mint,
15+
raw_token_acc, AccountMap, LstStateListData, VerPoolState,
16+
};
17+
use solana_account::Account;
18+
use solana_pubkey::Pubkey;
319

420
pub fn fill_swap_prog_accs<I, C, D, P>(
521
am: &mut AccountMap,
@@ -12,3 +28,122 @@ pub fn fill_swap_prog_accs<I, C, D, P>(
1228
) {
1329
fill_mock_prog_accs(am, [*inp_calc_prog, *out_calc_prog, *pricing_prog]);
1430
}
31+
32+
#[generic_array_struct(builder pub)]
33+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34+
pub struct SwapTokenU64s<T> {
35+
pub reserves_bal: T,
36+
pub acc_bal: T,
37+
pub mint_supply: T,
38+
}
39+
40+
#[generic_array_struct(builder pub)]
41+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42+
pub struct SwapTokenAddrs<T> {
43+
pub mint: T,
44+
pub acc: T,
45+
}
46+
47+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48+
pub struct SwapTokenArg<U, A> {
49+
pub u64s: SwapTokenU64s<U>,
50+
pub addrs: SwapTokenAddrs<A>,
51+
}
52+
53+
type SwapTokenArgVals = SwapTokenArg<u64, [u8; 32]>;
54+
55+
/// Assumes
56+
/// - both mints are tokenkeg mints
57+
/// - both tokens are 9 decimals
58+
pub fn swap_pre_accs(
59+
signer: &[u8; 32],
60+
ps: &VerPoolState,
61+
lsl: &LstStateListData,
62+
args: &Pair<SwapTokenArgVals>,
63+
) -> (IxPreKeysOwned, AccountMap) {
64+
let mut pair = args.map(|args| {
65+
if args.addrs.mint() == ps.lp_token_mint() {
66+
lp_accs(ps, signer, &args)
67+
} else {
68+
lst_accs(lsl, signer, &args)
69+
}
70+
});
71+
let accounts = NewSwapExactInV2IxPreAccsBuilder::start()
72+
.with_signer(((*signer).into(), mock_sys_acc(1_000_000_000)))
73+
.with_pool_state((POOL_STATE_ID.into(), ps.into_account()))
74+
.with_lst_state_list((
75+
LST_STATE_LIST_ID.into(),
76+
lst_state_list_account(lsl.lst_state_list.clone()),
77+
))
78+
// move instead of clone
79+
.with_inp_mint(take(pair.inp.mint_mut()))
80+
.with_inp_acc(take(pair.inp.acc_mut()))
81+
.with_inp_token_program(take(pair.inp.token_program_mut()))
82+
.with_inp_pool_reserves(take(pair.inp.pool_reserves_mut()))
83+
.with_out_mint(take(pair.out.mint_mut()))
84+
.with_out_acc(take(pair.out.acc_mut()))
85+
.with_out_token_program(take(pair.out.token_program_mut()))
86+
.with_out_pool_reserves(take(pair.out.pool_reserves_mut()))
87+
.build();
88+
let ix_prefix = IxPreKeysOwned::new(accounts.0.each_ref().map(|(pk, _)| pk.to_bytes()));
89+
90+
(ix_prefix, accounts.0.into_iter().collect())
91+
}
92+
93+
fn lp_accs(
94+
ps: &VerPoolState,
95+
signer: &[u8; 32],
96+
SwapTokenArg { u64s, addrs }: &SwapTokenArgVals,
97+
) -> SwapEntryAccs<(Pubkey, Account)> {
98+
if u64s.reserves_bal() != u64s.mint_supply() {
99+
panic!(
100+
"reserves_bal {} != mint_supply {}. Set both to eq.",
101+
u64s.reserves_bal(),
102+
u64s.mint_supply()
103+
);
104+
}
105+
let lp_mint = (
106+
Pubkey::new_from_array(*ps.lp_token_mint()),
107+
mock_mint(raw_mint(Some(POOL_STATE_ID), None, *u64s.mint_supply(), 9)),
108+
);
109+
let acc = (
110+
Pubkey::new_from_array(*addrs.acc()),
111+
mock_token_acc(raw_token_acc(*ps.lp_token_mint(), *signer, *u64s.acc_bal())),
112+
);
113+
NewSwapEntryAccsBuilder::start()
114+
.with_mint(lp_mint.clone())
115+
.with_acc(acc)
116+
.with_pool_reserves(lp_mint)
117+
.with_token_program(mollusk_svm_programs_token::token::keyed_account())
118+
.build()
119+
}
120+
121+
fn lst_accs(
122+
lsl: &LstStateListData,
123+
signer: &[u8; 32],
124+
SwapTokenArg { u64s, addrs }: &SwapTokenArgVals,
125+
) -> SwapEntryAccs<(Pubkey, Account)> {
126+
let mint = (
127+
Pubkey::new_from_array(*addrs.mint()),
128+
// dont-care abt mint and freeze auths
129+
mock_mint(raw_mint(None, None, *u64s.mint_supply(), 9)),
130+
);
131+
let acc = (
132+
Pubkey::new_from_array(*addrs.acc()),
133+
mock_token_acc(raw_token_acc(*addrs.mint(), *signer, *u64s.acc_bal())),
134+
);
135+
let reserves = (
136+
lsl.all_pool_reserves[addrs.mint()].into(),
137+
mock_token_acc(raw_token_acc(
138+
*addrs.mint(),
139+
POOL_STATE_ID,
140+
*u64s.reserves_bal(),
141+
)),
142+
);
143+
NewSwapEntryAccsBuilder::start()
144+
.with_mint(mint)
145+
.with_acc(acc)
146+
.with_pool_reserves(reserves)
147+
.with_token_program(mollusk_svm_programs_token::token::keyed_account())
148+
.build()
149+
}

0 commit comments

Comments
 (0)