Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions controller/core/src/instructions/swap/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> IxPreAccs<T> {
/// 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<T> {
pub mint: T,
pub acc: T,
pub token_program: T,

/// Set to LP mint if `mint = LP mint`
pub pool_reserves: T,
}

impl<T: Copy> SwapEntryAccs<T> {
#[inline]
pub const fn memset(val: T) -> Self {
Self([val; SWAP_ENTRY_ACCS_LEN])
}
}

// v1 conversions

impl<T: Clone> IxPreAccs<T> {
Expand Down
33 changes: 20 additions & 13 deletions controller/core/src/svc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Floor<Ratio<u64, u64>>> {
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::<u64, u64>::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::<u64, u64>::ONE
} else {
r
},
))
Some(Floor(r))
}
}

Expand Down Expand Up @@ -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());
}
}
}

Expand Down
137 changes: 136 additions & 1 deletion controller/program/tests/tests/swap/common/accounts.rs
Original file line number Diff line number Diff line change
@@ -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<I, C, D, P>(
am: &mut AccountMap,
Expand All @@ -12,3 +28,122 @@ pub fn fill_swap_prog_accs<I, C, D, P>(
) {
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<T> {
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<T> {
pub mint: T,
pub acc: T,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SwapTokenArg<U, A> {
pub u64s: SwapTokenU64s<U>,
pub addrs: SwapTokenAddrs<A>,
}

type SwapTokenArgVals = SwapTokenArg<u64, [u8; 32]>;

/// 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<SwapTokenArgVals>,
) -> (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()
}
Loading