Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Divide swap into individual sell_base, sell_quote, and sell_base_to_base #33

Merged
merged 7 commits into from
Oct 9, 2024
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
2 changes: 2 additions & 0 deletions programs/woofi/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ pub enum ErrorCode {
AmountOutBelowMinimum, //0x178f
#[msg("Amount exceeds max balance cap")]
BalanceCapExceeds, //0x1790
#[msg("Swap pool invalid")]
SwapPoolInvalid, //0x1791
}

impl From<TryFromIntError> for ErrorCode {
Expand Down
270 changes: 230 additions & 40 deletions programs/woofi/src/instructions/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,214 @@ pub struct Swap<'info> {
}

pub fn handler(ctx: Context<Swap>, from_amount: u128, min_to_amount: u128) -> Result<()> {
if ctx.accounts.woopool_from.key() == ctx.accounts.woopool_quote.key() {
sell_quote(ctx, from_amount, min_to_amount)
} else if ctx.accounts.woopool_to.key() == ctx.accounts.woopool_quote.key() {
sell_base(ctx, from_amount, min_to_amount)
} else {
swap_base_to_base(ctx, from_amount, min_to_amount)
}
}

fn sell_quote(ctx: Context<Swap>, from_amount: u128, min_to_amount: u128) -> Result<()> {
require!(
ctx.accounts.woopool_from.token_mint == ctx.accounts.woopool_quote.token_mint
&& ctx.accounts.woopool_from.token_mint != ctx.accounts.woopool_to.token_mint,
ErrorCode::SwapPoolInvalid
);

let token_owner_account_from = &ctx.accounts.token_owner_account_from;
require!(
token_owner_account_from.amount as u128 >= from_amount,
ErrorCode::NotEnoughBalance
);

let woopool_quote = &mut ctx.accounts.woopool_quote;
let quote_price_update = &mut ctx.accounts.quote_price_update;
let quote_token_vault = &ctx.accounts.quote_token_vault;
require!(
(quote_token_vault.amount as u128) + from_amount <= woopool_quote.cap_bal,
ErrorCode::BalanceCapExceeds
);

let woopool_to = &mut ctx.accounts.woopool_to;
let wooracle_to = &mut ctx.accounts.wooracle_to;
let price_update_to = &mut ctx.accounts.price_update_to;
let token_owner_account_to = &ctx.accounts.token_owner_account_to;
let token_vault_to = &ctx.accounts.token_vault_to;
let rebate_to = &ctx.accounts.rebate_to;

let fee_rate: u16 = woopool_to.fee_rate;
let swap_fee = checked_mul_div_round_up(from_amount, fee_rate as u128, ONE_E5_U128)?;
let quote_amount: u128 = from_amount.checked_sub(swap_fee).unwrap();

let decimals_to = Decimals::new(
wooracle_to.price_decimals as u32,
wooracle_to.quote_decimals as u32,
woopool_to.base_decimals as u32,
);

let state_to = get_price::get_state_impl(wooracle_to, price_update_to, quote_price_update)?;

let (to_amount, to_new_price) = swap_math::calc_base_amount_sell_quote(
quote_amount,
woopool_to,
&decimals_to,
&state_to,
)?;
wooracle_to.post_price(to_new_price)?;

require!(
woopool_to.reserve >= to_amount && token_vault_to.amount as u128 >= to_amount,
ErrorCode::NotEnoughOut
);

require!(to_amount >= min_to_amount, ErrorCode::AmountOutBelowMinimum);

woopool_quote.add_reserve(from_amount).unwrap();
woopool_to.sub_reserve(to_amount).unwrap();

// record fee into account
woopool_quote.sub_reserve(swap_fee).unwrap();
woopool_quote.add_unclaimed_fee(swap_fee).unwrap();

transfer_from_owner_to_vault(
&ctx.accounts.payer,
token_owner_account_from,
quote_token_vault,
&ctx.accounts.token_program,
from_amount as u64,
)?;

transfer_from_vault_to_owner(
woopool_to,
token_vault_to,
token_owner_account_to,
&ctx.accounts.token_program,
to_amount as u64,
)?;

emit!(SwapEvent {
sender: ctx.accounts.payer.key(),
from_token_mint: woopool_quote.token_mint,
to_token_mint: woopool_to.token_mint,
from_amount,
to_amount,
from_account: token_owner_account_from.key(),
to_account: token_owner_account_to.key(),
rebate_to: rebate_to.key(),
swap_vol: quote_amount + swap_fee,
swap_fee,
});

Ok(())
}

fn sell_base(ctx: Context<Swap>, from_amount: u128, min_to_amount: u128) -> Result<()> {
require!(
ctx.accounts.woopool_to.token_mint == ctx.accounts.woopool_quote.token_mint
&& ctx.accounts.woopool_from.token_mint != ctx.accounts.woopool_to.token_mint,
ErrorCode::SwapPoolInvalid
);

let token_owner_account_from = &ctx.accounts.token_owner_account_from;
require!(
token_owner_account_from.amount as u128 >= from_amount,
ErrorCode::NotEnoughBalance
);

let wooracle_from = &mut ctx.accounts.wooracle_from;
let price_update_from = &mut ctx.accounts.price_update_from;
let woopool_from = &mut ctx.accounts.woopool_from;
let token_vault_from = &ctx.accounts.token_vault_from;
require!(
(token_vault_from.amount as u128) + from_amount <= woopool_from.cap_bal,
ErrorCode::BalanceCapExceeds
);

let woopool_quote = &mut ctx.accounts.woopool_quote;
let quote_price_update = &mut ctx.accounts.quote_price_update;
let quote_token_vault = &ctx.accounts.quote_token_vault;
let token_owner_account_to = &ctx.accounts.token_owner_account_to;
let rebate_to = &ctx.accounts.rebate_to;

let fee_rate: u16 = woopool_from.fee_rate;

let decimals_from = Decimals::new(
wooracle_from.price_decimals as u32,
wooracle_from.quote_decimals as u32,
woopool_from.base_decimals as u32,
);

let state_from =
get_price::get_state_impl(wooracle_from, price_update_from, quote_price_update)?;

let (mut quote_amount, new_base_price) = swap_math::calc_quote_amount_sell_base(
from_amount,
woopool_from,
&decimals_from,
&state_from,
)?;

wooracle_from.post_price(new_base_price)?;

let swap_fee = checked_mul_div_round_up(quote_amount, fee_rate as u128, ONE_E5_U128)?;
quote_amount = quote_amount.checked_sub(swap_fee).unwrap();

require!(
woopool_quote.reserve >= swap_fee + quote_amount && quote_token_vault.amount as u128 >= swap_fee + quote_amount,
ErrorCode::NotEnoughOut
);

require!(quote_amount >= min_to_amount, ErrorCode::AmountOutBelowMinimum);

woopool_from.add_reserve(from_amount).unwrap();
woopool_quote.sub_reserve(quote_amount).unwrap();

// record fee into account
woopool_quote.sub_reserve(swap_fee).unwrap();
woopool_quote.add_unclaimed_fee(swap_fee).unwrap();

transfer_from_owner_to_vault(
&ctx.accounts.payer,
token_owner_account_from,
token_vault_from,
&ctx.accounts.token_program,
from_amount as u64,
)?;

transfer_from_vault_to_owner(
woopool_quote,
quote_token_vault,
token_owner_account_to,
&ctx.accounts.token_program,
quote_amount as u64,
)?;

emit!(SwapEvent {
sender: ctx.accounts.payer.key(),
from_token_mint: woopool_from.token_mint,
to_token_mint: woopool_quote.token_mint,
from_amount,
to_amount: quote_amount,
from_account: token_owner_account_from.key(),
to_account: token_owner_account_to.key(),
rebate_to: rebate_to.key(),
swap_vol: quote_amount + swap_fee,
swap_fee,
});

Ok(())
}

fn swap_base_to_base(ctx: Context<Swap>, from_amount: u128, min_to_amount: u128) -> Result<()> {
require!(
ctx.accounts.woopool_from.token_mint != ctx.accounts.woopool_quote.token_mint
&& ctx.accounts.woopool_to.token_mint != ctx.accounts.woopool_quote.token_mint
&& ctx.accounts.woopool_from.token_mint != ctx.accounts.woopool_to.token_mint,
ErrorCode::SwapPoolInvalid
);

Copy link
Contributor

@fb-alexcq fb-alexcq Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better also add another require:

   woopool_from.token_mint != woopool_to.token_mint

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will add

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, double checked the code in Solidity, this check is required. from_token != to_token

let price_update_from = &mut ctx.accounts.price_update_from;
let price_update_to = &mut ctx.accounts.price_update_to;
let quote_price_update = &mut ctx.accounts.quote_price_update;
Expand Down Expand Up @@ -124,67 +332,49 @@ pub fn handler(ctx: Context<Swap>, from_amount: u128, min_to_amount: u128) -> Re
ErrorCode::BalanceCapExceeds
);

let fee_rate: u16 = if woopool_from.token_mint == woopool_from.quote_token_mint {
woopool_to.fee_rate
} else if woopool_to.token_mint == woopool_to.quote_token_mint {
woopool_from.fee_rate
} else {
max(woopool_from.fee_rate, woopool_to.fee_rate)
};
let fee_rate: u16 = max(woopool_from.fee_rate, woopool_to.fee_rate);

let decimals_from = Decimals::new(
wooracle_from.price_decimals as u32,
wooracle_from.quote_decimals as u32,
woopool_from.base_decimals as u32,
);

let mut quote_amount = from_amount;
if woopool_from.token_mint != woopool_from.quote_token_mint {
let state_from =
get_price::get_state_impl(wooracle_from, price_update_from, quote_price_update)?;
let state_from =
get_price::get_state_impl(wooracle_from, price_update_from, quote_price_update)?;

let (_quote_amount, new_base_price) = swap_math::calc_quote_amount_sell_base(
from_amount,
woopool_from,
&decimals_from,
&state_from,
)?;

wooracle_from.post_price(new_base_price)?;
let (mut quote_amount, new_base_price) = swap_math::calc_quote_amount_sell_base(
from_amount,
woopool_from,
&decimals_from,
&state_from,
)?;

quote_amount = _quote_amount;
}
wooracle_from.post_price(new_base_price)?;

let swap_fee = checked_mul_div_round_up(quote_amount, fee_rate as u128, ONE_E5_U128)?;
quote_amount = quote_amount.checked_sub(swap_fee).unwrap();

if woopool_from.token_mint != woopool_from.quote_token_mint {
require!(
woopool_quote.reserve >= swap_fee && quote_token_vault.amount as u128 >= swap_fee,
ErrorCode::NotEnoughOut
);
}
require!(
woopool_quote.reserve >= swap_fee && quote_token_vault.amount as u128 >= swap_fee,
ErrorCode::NotEnoughOut
);

let decimals_to = Decimals::new(
wooracle_to.price_decimals as u32,
wooracle_to.quote_decimals as u32,
woopool_to.base_decimals as u32,
);

let mut to_amount = quote_amount;
if woopool_to.token_mint != woopool_to.quote_token_mint {
let state_to = get_price::get_state_impl(wooracle_to, price_update_to, quote_price_update)?;

let (_to_amount, to_new_price) = swap_math::calc_base_amount_sell_quote(
quote_amount,
woopool_to,
&decimals_to,
&state_to,
)?;
wooracle_to.post_price(to_new_price)?;
let state_to = get_price::get_state_impl(wooracle_to, price_update_to, quote_price_update)?;

to_amount = _to_amount;
}
let (to_amount, to_new_price) = swap_math::calc_base_amount_sell_quote(
quote_amount,
woopool_to,
&decimals_to,
&state_to,
)?;
wooracle_to.post_price(to_new_price)?;

require!(
woopool_to.reserve >= to_amount && token_vault_to.amount as u128 >= to_amount,
Expand Down
5 changes: 5 additions & 0 deletions sdk/src/artifacts/woofi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,11 @@
"code": 6032,
"name": "BalanceCapExceeds",
"msg": "Amount exceeds max balance cap"
},
{
"code": 6033,
"name": "SwapPoolInvalid",
"msg": "Swap pool invalid"
}
],
"metadata": {
Expand Down
10 changes: 10 additions & 0 deletions sdk/src/artifacts/woofi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,11 @@ export type Woofi = {
"code": 6032,
"name": "BalanceCapExceeds",
"msg": "Amount exceeds max balance cap"
},
{
"code": 6033,
"name": "SwapPoolInvalid",
"msg": "Swap pool invalid"
}
]
};
Expand Down Expand Up @@ -3414,6 +3419,11 @@ export const IDL: Woofi = {
"code": 6032,
"name": "BalanceCapExceeds",
"msg": "Amount exceeds max balance cap"
},
{
"code": 6033,
"name": "SwapPoolInvalid",
"msg": "Swap pool invalid"
}
]
};
Loading