Skip to content

Commit

Permalink
add test
Browse files Browse the repository at this point in the history
  • Loading branch information
0x777A committed Feb 26, 2024
1 parent 5f585cf commit 707cd6e
Show file tree
Hide file tree
Showing 28 changed files with 4,684 additions and 116 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ target
node_modules
test-ledger
.vscode
.yarn


1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
16 changes: 11 additions & 5 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ members = ["programs/cp-swap"]
seeds = false
skip-lint = false

[programs.mainnet]
token_swap = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"

[programs.devnet]
token_swap = "CPMDWBwJDtYax9qW7AyRuVC19Cc4L4Vcy4n2BHAbHkCW"
[programs.Localnet]
cp_swap = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"


[registry]
Expand All @@ -26,3 +23,12 @@ wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

[test]
startup_wait = 10000

[test.validator]
url = "https://api.mainnet-beta.solana.com"

[[test.validator.clone]]
address = "DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@coral-xyz/anchor": "^0.29.0",
"@solana/spl-token": "^0.4.0",
"@solana/web3.js": "^1.90.0"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
}
}
25 changes: 11 additions & 14 deletions programs/cp-swap/src/curve/calculator.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Swap calculations
use crate::curve::{constant_product::ConstantProductCurve, fees::Fees};
use anchor_lang::prelude::*;
use {crate::error::ErrorCode, std::fmt::Debug};
use crate::curve::{constant_product::ConstantProductCurve, fees::Fees};

/// Helper function for mapping to ErrorCode::CalculationFailure
pub fn map_zero_to_none(x: u128) -> Option<u128> {
Expand Down Expand Up @@ -153,16 +153,17 @@ impl CurveCalculator {
swap_destination_amount,
)?;

let source_amount = Fees::calculate_pre_fee_amount( source_amount_swapped, trade_fee_rate).unwrap();
let source_amount =
Fees::calculate_pre_fee_amount(source_amount_swapped, trade_fee_rate).unwrap();
let trade_fee = Fees::trading_fee(source_amount, trade_fee_rate)?;
let protocol_fee = Fees::protocol_fee(trade_fee, protocol_fee_rate)?;
let fund_fee = Fees::fund_fee(trade_fee, fund_fee_rate)?;

Some(SwapResult {
new_swap_source_amount: swap_source_amount.checked_add(source_amount)?,
new_swap_destination_amount: swap_destination_amount
.checked_sub(destination_amount_swapped)?,
source_amount_swapped:source_amount,
source_amount_swapped: source_amount,
destination_amount_swapped,
trade_fee,
protocol_fee,
Expand Down Expand Up @@ -243,7 +244,9 @@ pub mod test {
TradeDirection::ZeroForOne => (swap_source_amount, swap_destination_amount),
TradeDirection::OneForZero => (swap_destination_amount, swap_source_amount),
};
let previous_value = normalized_value(swap_token_0_amount, swap_token_1_amount).unwrap();
let previous_value = swap_token_0_amount
.checked_mul(swap_token_1_amount)
.unwrap();

let new_swap_source_amount = swap_source_amount
.checked_add(results.source_amount_swapped)
Expand All @@ -256,16 +259,10 @@ pub mod test {
TradeDirection::OneForZero => (new_swap_destination_amount, new_swap_source_amount),
};

let new_value = normalized_value(swap_token_0_amount, swap_token_1_amount).unwrap();
assert!(new_value.greater_than_or_equal(&previous_value));

let epsilon = 1; // Extremely close!
let difference = new_value
.checked_sub(&previous_value)
.unwrap()
.to_imprecise()
let new_value = swap_token_0_amount
.checked_mul(swap_token_1_amount)
.unwrap();
assert!(difference <= epsilon);
assert!(new_value >= previous_value);
}

/// Test function checking that a deposit never reduces the value of pool
Expand Down
63 changes: 23 additions & 40 deletions programs/cp-swap/src/curve/constant_product.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! The Uniswap invariantConstantProductCurve::
use {
crate::curve::calculator::{
map_zero_to_none, RoundDirection, SwapWithoutFeesResult, TradingTokenResult,
},
spl_math::checked_ceil_div::CheckedCeilDiv,
use crate::{
curve::calculator::{RoundDirection, SwapWithoutFeesResult, TradingTokenResult},
utils::CheckedCeilDiv,
};

/// ConstantProductCurve struct implementing CurveCalculator
Expand All @@ -23,18 +21,14 @@ impl ConstantProductCurve {
swap_source_amount: u128,
swap_destination_amount: u128,
) -> Option<SwapWithoutFeesResult> {
let invariant = swap_source_amount.checked_mul(swap_destination_amount)?;

let new_swap_source_amount = swap_source_amount.checked_add(source_amount)?;
let (new_swap_destination_amount, new_swap_source_amount) =
invariant.checked_ceil_div(new_swap_source_amount)?;

let source_amount_swapped = new_swap_source_amount.checked_sub(swap_source_amount)?;
let destination_amount_swapped =
map_zero_to_none(swap_destination_amount.checked_sub(new_swap_destination_amount)?)?;
// (x + delta_x) * (y - delta_y) = x * y
// delta_y = (delta_x * y) / (x + delta_x)
let numerator = source_amount.checked_mul(swap_destination_amount).unwrap();
let denominator = swap_source_amount.checked_add(source_amount).unwrap();
let destination_amount_swapped = numerator.checked_div(denominator).unwrap();

Some(SwapWithoutFeesResult {
source_amount_swapped,
source_amount_swapped: source_amount,
destination_amount_swapped,
})
}
Expand All @@ -44,19 +38,17 @@ impl ConstantProductCurve {
swap_source_amount: u128,
swap_destination_amount: u128,
) -> Option<SwapWithoutFeesResult> {
let invariant = swap_source_amount.checked_mul(swap_destination_amount)?;

let new_swap_destination_amount = swap_destination_amount.checked_sub(destinsation_amount)?;
let (new_swap_source_amount,new_swap_destination_amount) =
invariant.checked_ceil_div(new_swap_destination_amount)?;

let source_amount_swapped = new_swap_source_amount.checked_sub(swap_source_amount)?;
let destination_amount_swapped =
map_zero_to_none(swap_destination_amount.checked_sub(new_swap_destination_amount)?)?;
// (x + delta_x) * (y - delta_y) = x * y
// delta_x = (x * delta_y) / (y - delta_y)
let numerator = swap_source_amount.checked_mul(destinsation_amount).unwrap();
let denominator = swap_destination_amount
.checked_sub(destinsation_amount)
.unwrap();
let (source_amount_swapped, _) = numerator.checked_ceil_div(denominator).unwrap();

Some(SwapWithoutFeesResult {
source_amount_swapped,
destination_amount_swapped,
destination_amount_swapped: destinsation_amount,
})
}

Expand Down Expand Up @@ -115,10 +107,9 @@ mod tests {
crate::curve::calculator::{
test::{
check_curve_value_from_swap, check_pool_value_from_deposit,
check_pool_value_from_withdraw, total_and_intermediate
check_pool_value_from_withdraw, total_and_intermediate,
},
RoundDirection,
TradeDirection
RoundDirection, TradeDirection,
},
proptest::prelude::*,
};
Expand Down Expand Up @@ -196,21 +187,13 @@ mod tests {

#[test]
fn constant_product_swap_rounding() {
// much too small
assert!(ConstantProductCurve::swap_base_input_without_fees(
10,
70_000_000_000,
4_000_000,
)
.is_none()); // spot: 10 * 4m / 70b = 0

let tests: &[(u128, u128, u128, u128, u128)] = &[
// spot: 10 * 70b / ~4m = 174,999.99
(10, 4_000_000, 70_000_000_000, 10, 174_999),
// spot: 20 * 1 / 3.000 = 6.6667 (source can be 18 to get 6 dest.)
(20, 30_000 - 20, 10_000, 18, 6),
(20, 30_000 - 20, 10_000, 20, 6),
// spot: 19 * 1 / 2.999 = 6.3334 (source can be 18 to get 6 dest.)
(19, 30_000 - 20, 10_000, 18, 6),
(19, 30_000 - 20, 10_000, 19, 6),
// spot: 18 * 1 / 2.999 = 6.0001
(18, 30_000 - 20, 10_000, 18, 6),
// spot: 10 * 3 / 2.0010 = 14.99
Expand All @@ -220,11 +203,11 @@ mod tests {
// spot: 10 * 3 / 2.0000 = 15
(10, 20_000 - 10, 30_000, 10, 15),
// spot: 100 * 3 / 6.001 = 49.99 (source can be 99 to get 49 dest.)
(100, 60_000, 30_000, 99, 49),
(100, 60_000, 30_000, 100, 49),
// spot: 99 * 3 / 6.001 = 49.49
(99, 60_000, 30_000, 99, 49),
// spot: 98 * 3 / 6.001 = 48.99 (source can be 97 to get 48 dest.)
(98, 60_000, 30_000, 97, 48),
(98, 60_000, 30_000, 98, 48),
];
for (
source_amount,
Expand Down
9 changes: 4 additions & 5 deletions programs/cp-swap/src/curve/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,14 @@ impl Fees {
if trade_fee_rate == 0 {
Some(post_fee_amount)
} else {
let numerator =
post_fee_amount.checked_mul(u128::from(FEE_RATE_DENOMINATOR_VALUE))?;
let numerator = post_fee_amount.checked_mul(u128::from(FEE_RATE_DENOMINATOR_VALUE))?;
let denominator =
u128::from(FEE_RATE_DENOMINATOR_VALUE).checked_sub(u128::from(trade_fee_rate))?;

numerator
.checked_add(denominator)?
.checked_sub(1)?
.checked_div(denominator)
.checked_add(denominator)?
.checked_sub(1)?
.checked_div(denominator)
}
}
}
4 changes: 2 additions & 2 deletions programs/cp-swap/src/instructions/admin/update_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ fn set_new_protocol_owner(amm_config: &mut Account<AmmConfig>, new_owner: Pubkey
require_keys_neq!(new_owner, Pubkey::default());
#[cfg(feature = "enable-log")]
msg!(
"amm_config, old_owner:{}, new_owner:{}",
amm_config.owner.to_string(),
"amm_config, old_protocol_owner:{}, new_owner:{}",
amm_config.protocol_owner.to_string(),
new_owner.key().to_string()
);
amm_config.protocol_owner = new_owner;
Expand Down
26 changes: 20 additions & 6 deletions programs/cp-swap/src/instructions/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ pub struct Deposit<'info> {
pub vault_1_mint: Box<InterfaceAccount<'info, Mint>>,

/// Lp token mint
#[account(address = pool_state.load()?.lp_mint @ ErrorCode::IncorrectLpMint)]
#[account(
mut,
address = pool_state.load()?.lp_mint @ ErrorCode::IncorrectLpMint)
]
pub lp_mint: Box<InterfaceAccount<'info, Mint>>,
}

Expand Down Expand Up @@ -115,10 +118,6 @@ pub fn deposit(
)
};

if transfer_token_0_amount > maximum_token_0_amount {
return Err(ErrorCode::ExceededSlippage.into());
}

let token_1_amount = u64::try_from(results.token_1_amount).unwrap();
let (transfer_token_1_amount, transfer_token_1_fee) = {
let transfer_fee = get_transfer_inverse_fee(&ctx.accounts.vault_1_mint, token_1_amount)?;
Expand All @@ -127,7 +126,22 @@ pub fn deposit(
transfer_fee,
)
};
if transfer_token_1_amount > maximum_token_1_amount {

#[cfg(feature = "enable-log")]
msg!(
"results.token_0_amount;{}, results.token_1_amount:{},transfer_token_0_amount:{},transfer_token_0_fee:{},
transfer_token_1_amount:{},transfer_token_1_fee:{}",
results.token_0_amount,
results.token_1_amount,
transfer_token_0_amount,
transfer_token_0_fee,
transfer_token_1_amount,
transfer_token_1_fee
);

if transfer_token_0_amount > maximum_token_0_amount
|| transfer_token_1_amount > maximum_token_1_amount
{
return Err(ErrorCode::ExceededSlippage.into());
}

Expand Down
2 changes: 1 addition & 1 deletion programs/cp-swap/src/instructions/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub struct Initialize<'info> {
)]
pub pool_state: AccountLoader<'info, PoolState>,

/// Token_0 mint, the key must grater then token_1 mint.
/// Token_0 mint, the key must smaller then token_1 mint.
#[account(
constraint = token_0_mint.key() < token_1_mint.key(),
mint::token_program = token_0_program,
Expand Down
2 changes: 1 addition & 1 deletion programs/cp-swap/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ pub mod admin;
pub use admin::*;

pub mod swap_base_output;
pub use swap_base_output::*;
pub use swap_base_output::*;
10 changes: 10 additions & 0 deletions programs/cp-swap/src/instructions/swap_base_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn swap_base_input(ctx: Context<Swap>, amount_in: u64, minimum_amount_out: u
let transfer_fee = get_transfer_fee(&ctx.accounts.input_token_mint, amount_in)?;
// Take transfer fees into account for actual amount transferred in
let actual_amount_in = amount_in.saturating_sub(transfer_fee);
require_gt!(actual_amount_in, 0);

// Calculate the trade amounts
let (trade_direction, total_input_token_amount, total_output_token_amount) =
Expand Down Expand Up @@ -130,6 +131,14 @@ pub fn swap_base_input(ctx: Context<Swap>, amount_in: u64, minimum_amount_out: u
let constant_after = u128::from(result.new_swap_source_amount)
.checked_mul(u128::from(result.new_swap_destination_amount))
.unwrap();
#[cfg(feature = "enable-log")]
msg!(
"source_amount_swapped:{}, destination_amount_swapped:{},constant_before:{},constant_after:{}",
result.source_amount_swapped,
result.destination_amount_swapped,
constant_before,
constant_after
);
require_gte!(constant_after, constant_before);

// Re-calculate the source amount swapped based on what the curve says
Expand All @@ -147,6 +156,7 @@ pub fn swap_base_input(ctx: Context<Swap>, amount_in: u64, minimum_amount_out: u
let amount_out = u64::try_from(result.destination_amount_swapped).unwrap();
let transfer_fee = get_transfer_fee(&ctx.accounts.output_token_mint, amount_out)?;
let amount_received = amount_out.checked_sub(transfer_fee).unwrap();
require_gt!(amount_received, 0);
if amount_received < minimum_amount_out {
return Err(ErrorCode::ExceededSlippage.into());
}
Expand Down
13 changes: 11 additions & 2 deletions programs/cp-swap/src/instructions/swap_base_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,32 @@ pub fn swap_base_output(
let constant_after = u128::from(result.new_swap_source_amount)
.checked_mul(u128::from(result.new_swap_destination_amount))
.unwrap();

#[cfg(feature = "enable-log")]
msg!(
"source_amount_swapped:{}, destination_amount_swapped:{},constant_before:{},constant_after:{}",
result.source_amount_swapped,
result.destination_amount_swapped,
constant_before,
constant_after
);
require_gte!(constant_after, constant_before);

// Re-calculate the source amount swapped based on what the curve says
let (input_transfer_amount, input_transfer_fee) = {
let source_amount_swapped = u64::try_from(result.source_amount_swapped).unwrap();
require_gt!(source_amount_swapped, 0);
let transfer_fee =
get_transfer_inverse_fee(&ctx.accounts.input_token_mint, source_amount_swapped)?;
let input_transfer_amount = source_amount_swapped.checked_add(transfer_fee).unwrap();
if input_transfer_amount < max_amount_in {
if input_transfer_amount > max_amount_in {
return Err(ErrorCode::ExceededSlippage.into());
}
(input_transfer_amount, transfer_fee)
};

let (output_transfer_amount, output_transfer_fee) = {
let destination_amount_swapped = u64::try_from(result.destination_amount_swapped).unwrap();
require_gte!(actual_amount_out, destination_amount_swapped);
let output_transfer_fee =
get_transfer_inverse_fee(&ctx.accounts.output_token_mint, destination_amount_swapped)?;
(destination_amount_swapped, output_transfer_fee)
Expand Down
Loading

0 comments on commit 707cd6e

Please sign in to comment.