Skip to content

Commit

Permalink
Pool: first iteration over adding commission for referral
Browse files Browse the repository at this point in the history
  • Loading branch information
gangov committed Oct 27, 2023
1 parent 3d80779 commit 38f0f4c
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 15 deletions.
56 changes: 46 additions & 10 deletions contracts/pool/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use soroban_sdk::{contract, contractimpl, contractmeta, log, Address, BytesN, En
use num_integer::Roots;

use crate::storage::utils::{is_initialized, set_initialized};
use crate::storage::LiquidityPoolInfo;
use crate::storage::{LiquidityPoolInfo, Referral};
use crate::{
stake_contract,
storage::{
Expand Down Expand Up @@ -63,6 +63,7 @@ pub trait LiquidityPoolTrait {
fn swap(
env: Env,
sender: Address,
referral: Option<Referral>,
offer_asset: Address,
offer_amount: i128,
belief_price: Option<i64>,
Expand Down Expand Up @@ -265,6 +266,7 @@ impl LiquidityPoolTrait for LiquidityPool {
do_swap(
env.clone(),
sender.clone(),
None,
config.clone().token_a,
a_for_swap,
None,
Expand All @@ -286,6 +288,7 @@ impl LiquidityPoolTrait for LiquidityPool {
do_swap(
env.clone(),
sender.clone(),
None,
config.clone().token_b,
b_for_swap,
None,
Expand Down Expand Up @@ -352,6 +355,7 @@ impl LiquidityPoolTrait for LiquidityPool {
fn swap(
env: Env,
sender: Address,
referral: Option<Referral>,
offer_asset: Address,
offer_amount: i128,
belief_price: Option<i64>,
Expand All @@ -364,6 +368,7 @@ impl LiquidityPoolTrait for LiquidityPool {
do_swap(
env,
sender,
referral,
offer_asset,
offer_amount,
belief_price,
Expand Down Expand Up @@ -555,11 +560,12 @@ impl LiquidityPoolTrait for LiquidityPool {
(pool_balance_b, pool_balance_a)
};

let (ask_amount, spread_amount, commission_amount) = compute_swap(
let (ask_amount, spread_amount, commission_amount, _referral_fee_amount) = compute_swap(
pool_balance_offer,
pool_balance_ask,
offer_amount,
config.protocol_fee_rate(),
0i64,
);

let total_return = ask_amount + commission_amount + spread_amount;
Expand Down Expand Up @@ -605,6 +611,7 @@ impl LiquidityPoolTrait for LiquidityPool {
fn do_swap(
env: Env,
sender: Address,
referral: Option<Referral>,
offer_asset: Address,
offer_amount: i128,
belief_price: Option<i64>,
Expand All @@ -624,11 +631,18 @@ fn do_swap(
(pool_balance_b, pool_balance_a)
};

let (return_amount, spread_amount, commission_amount) = compute_swap(
let referral_fee_bps = match referral {
Some(ref referral) => referral.clone().fee,
None => 0,
};

// 1. We calculate the referral_fee below. If none referral fee will be 0
let (return_amount, spread_amount, commission_amount, referral_fee_amount) = compute_swap(
pool_balance_sell,
pool_balance_buy,
offer_amount,
config.protocol_fee_rate(),
referral_fee_bps,
);

assert_max_spread(
Expand Down Expand Up @@ -668,6 +682,18 @@ fn do_swap(
&commission_amount,
);

// 2. If referral is present and return amount is larger than 0 we send referral fee commision
// to fee recipient
if let Some(Referral { address, fee }) = referral {
if fee > 0 {
token_contract::Client::new(&env, &buy_token).transfer(
&env.current_contract_address(),
&address,
&referral_fee_amount,
);
}
}

// user is offering to sell A, so they will receive B
// A balance is bigger, B balance is smaller
let (balance_a, balance_b) = if offer_asset == config.token_a {
Expand Down Expand Up @@ -875,34 +901,44 @@ pub fn assert_max_spread(
/// - `ask_pool`: Total amount of ask assets in the pool.
/// - `offer_amount`: Amount of offer assets to swap.
/// - `commission_rate`: Total amount of fees charged for the swap.
/// - `referral_fee`: Amount of fee for the referral
///
/// Returns a tuple containing the following values:
/// - The resulting amount of ask assets after the swap.
/// - The spread amount, representing the difference between the expected and actual swap amounts.
/// - The commission amount, representing the fees charged for the swap.
/// - The referral comission fee.
pub fn compute_swap(
offer_pool: i128,
ask_pool: i128,
offer_amount: i128,
commission_rate: Decimal,
) -> (i128, i128, i128) {
referral_fee: i64,
) -> (i128, i128, i128, i128) {
// Calculate the cross product of offer_pool and ask_pool
let cp: i128 = offer_pool * ask_pool;

// Calculate the resulting amount of ask assets after the swap
// Return amount calculation based on the AMM model's invariant,
// which ensures the product of the amounts of the two assets remains constant before and after a trade.
let return_amount: i128 = ask_pool - (cp / (offer_pool + offer_amount));

// Calculate the spread amount, representing the difference between the expected and actual swap amounts
let spread_amount: i128 = (offer_amount * ask_pool / offer_pool) - return_amount;

let commission_amount: i128 = return_amount * commission_rate;

// Deduct the commission (minus the part that goes to the protocol) from the return amount
let return_amount: i128 = return_amount - commission_amount;
let referral_fee_amount: i128 = return_amount * Decimal::bps(referral_fee);

let return_amount: i128 = return_amount - referral_fee_amount;

(return_amount, spread_amount, commission_amount)
(
return_amount,
spread_amount,
commission_amount,
referral_fee_amount,
)
}

/// Returns an amount of offer assets for a specified amount of ask assets.
Expand Down Expand Up @@ -1046,14 +1082,14 @@ mod tests {

#[test]
fn test_compute_swap_pass() {
let result = compute_swap(1000, 2000, 100, Decimal::percent(10)); // 10% commission rate
assert_eq!(result, (164, 18, 18)); // Expected return amount, spread, and commission
let result = compute_swap(1000, 2000, 100, Decimal::percent(10), 500i64); // 10% commission rate
assert_eq!(result, (156, 18, 18, 8)); // Expected return amount, spread, commission and referral fee commission
}

#[test]
fn test_compute_swap_full_commission() {
let result = compute_swap(1000, 2000, 100, Decimal::one()); // 100% commission rate should lead to return_amount being 0
assert_eq!(result, (0, 18, 182));
let result = compute_swap(1000, 2000, 100, Decimal::one(), 0i64); // 100% commission rate should lead to return_amount being 0
assert_eq!(result, (0, 18, 182, 0));
}

#[test]
Expand Down
41 changes: 36 additions & 5 deletions contracts/pool/src/tests/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use soroban_sdk::testutils::{AuthorizedFunction, AuthorizedInvocation};
use soroban_sdk::{symbol_short, testutils::Address as _, Address, Env, IntoVal};

use super::setup::{deploy_liquidity_pool_contract, deploy_token_contract};
use crate::storage::{Asset, PoolResponse, SimulateReverseSwapResponse, SimulateSwapResponse};
use crate::storage::{
Asset, PoolResponse, Referral, SimulateReverseSwapResponse, SimulateSwapResponse,
};
use decimal::Decimal;

#[test]
Expand Down Expand Up @@ -49,7 +51,14 @@ fn simple_swap() {
// true means "selling A token"
// selling just one token with 1% max spread allowed
let spread = 100i64; // 1% maximum spread allowed
pool.swap(&user1, &token1.address, &1, &None, &Some(spread));
pool.swap(
&user1,
&None::<Referral>,
&token1.address,
&1,
&None,
&Some(spread),
);
assert_eq!(
env.auths(),
[(
Expand All @@ -58,7 +67,15 @@ fn simple_swap() {
function: AuthorizedFunction::Contract((
pool.address.clone(),
symbol_short!("swap"),
(&user1, token1.address.clone(), 1_i128, None::<i64>, spread).into_val(&env)
(
&user1,
None::<Referral>,
token1.address.clone(),
1_i128,
None::<i64>,
spread
)
.into_val(&env)
)),
sub_invocations: std::vec![
(AuthorizedInvocation {
Expand Down Expand Up @@ -98,7 +115,14 @@ fn simple_swap() {

// false means selling B token
// this time 100 units
let output_amount = pool.swap(&user1, &token2.address, &1_000, &None, &Some(spread));
let output_amount = pool.swap(
&user1,
&None::<Referral>,
&token2.address,
&1_000,
&None,
&Some(spread),
);
let result = pool.query_pool_info();
assert_eq!(
result,
Expand Down Expand Up @@ -167,7 +191,14 @@ fn swap_with_high_fee() {
let spread = 1_000; // 10% maximum spread allowed

// let's swap 100_000 units of Token 1 in 1:1 pool with 10% protocol fee
pool.swap(&user1, &token1.address, &100_000, &None, &Some(spread));
pool.swap(
&user1,
&None,
&token1.address,
&100_000,
&None,
&Some(spread),
);

// This is XYK LP with constant product formula
// Y_new = (X_in * Y_old) / (X_in + X_old)
Expand Down

0 comments on commit 38f0f4c

Please sign in to comment.