Skip to content
Draft
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
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions dex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,8 @@ path = "governance"
[dev-dependencies.fees-collector]
path = "../energy-integration/fees-collector"

[dev-dependencies.energy-factory]
path = "../locked-asset/energy-factory"

[dev-dependencies.farm-boosted-yields]
path = "../energy-integration/farm-boosted-yields"
3 changes: 2 additions & 1 deletion dex/fuzz/src/fuzz_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,8 @@ pub mod fuzz_data_tests {
.execute_tx(owner_addr, &pair_wrapper, &rust_zero, |sc| {
let first_token_id = managed_token_id!(first_token);
let second_token_id = managed_token_id!(second_token);
let router_address = managed_address!(owner_addr);
// Set router address to pair's own address so fees_collector_address lookup returns empty
let router_address = sc.blockchain().get_sc_address();
let router_owner_address = managed_address!(owner_addr);
let total_fee_percent = TOTAL_FEE_PERCENT;
let special_fee_percent = SPECIAL_FEE_PERCENT;
Expand Down
3 changes: 3 additions & 0 deletions dex/pair/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ version = "=0.53.2"

[dev-dependencies.energy-factory-mock]
path = "../../energy-integration/energy-factory-mock"

[dev-dependencies.router]
path = "../../dex/router"
137 changes: 20 additions & 117 deletions dex/pair/src/fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ use super::amm;
use super::config;
use super::errors::*;
use super::liquidity_pool;
use crate::config::MAX_PERCENTAGE;
use crate::contexts::base::StorageCache;
use crate::contexts::base::SwapTokensOrder;

use common_structs::TokenPair;
use fees_collector::fees_accumulation::ProxyTrait as _;
use multiversx_sc::storage::StorageKey;

pub static FEES_COLLECTOR_ADDRESS_STORAGE_KEY: &[u8] = b"fees_collector_address";

mod self_proxy {
multiversx_sc::imports!();
Expand All @@ -32,11 +34,6 @@ pub trait FeeModule:
+ permissions_module::PermissionsModule
+ pausable::PausableModule
{
#[view(getFeeState)]
fn is_fee_enabled(&self) -> bool {
!self.destination_map().is_empty() || !self.fees_collector_address().is_empty()
}

#[endpoint(whitelist)]
fn whitelist_endpoint(&self, address: ManagedAddress) {
self.require_caller_has_owner_permissions();
Expand Down Expand Up @@ -97,81 +94,21 @@ pub trait FeeModule:
}
}

/// `fees_collector_cut_percentage` of the special fees are sent to the fees_collector_address SC
///
/// For example, if special fees is 5%, and fees_collector_cut_percentage is 10%,
/// then of the 5%, 10% are reserved, and only the rest are split between other pair contracts.
#[endpoint(setupFeesCollector)]
fn setup_fees_collector(
&self,
fees_collector_address: ManagedAddress,
fees_collector_cut_percentage: u64,
) {
self.require_caller_has_owner_permissions();
require!(
self.blockchain().is_smart_contract(&fees_collector_address),
"Invalid fees collector address"
);
require!(
fees_collector_cut_percentage > 0 && fees_collector_cut_percentage <= MAX_PERCENTAGE,
"Invalid fees percentage"
);

self.fees_collector_address().set(&fees_collector_address);
self.fees_collector_cut_percentage()
.set(fees_collector_cut_percentage);
}

fn send_fee(
&self,
storage_cache: &mut StorageCache<Self>,
swap_tokens_order: SwapTokensOrder,
fee_token: &TokenIdentifier,
fee_amount: &BigUint,
) {
fn send_fee(&self, fee_token: &TokenIdentifier, fee_amount: &BigUint) {
if fee_amount == &0u64 {
return;
}

let fees_collector_configured = !self.fees_collector_address().is_empty();
let remaining_fee = if fees_collector_configured {
let fees_collector_cut_percentage = self.fees_collector_cut_percentage().get();
let cut_amount = fee_amount * fees_collector_cut_percentage / MAX_PERCENTAGE;
let reminder = fee_amount - &cut_amount;

if cut_amount > 0 {
self.send_fees_collector_cut(fee_token.clone(), cut_amount);
}

reminder
} else {
fee_amount.clone()
};

let slices = self.destination_map().len() as u64;
if slices == 0 {
return;
}
self.send_fees_collector_cut(fee_token.clone(), fee_amount.clone());
}

let fee_slice = remaining_fee / slices;
if fee_slice == 0 {
fn send_fees_collector_cut(&self, token: TokenIdentifier, cut_amount: BigUint) {
let fees_collector_mapper = self.get_fees_collector_address_mapper();
if fees_collector_mapper.is_empty() {
return;
}

for (fee_address, fee_token_requested) in self.destination_map().iter() {
self.send_fee_slice(
storage_cache,
swap_tokens_order,
fee_token,
&fee_slice,
&fee_address,
&fee_token_requested,
);
}
}

fn send_fees_collector_cut(&self, token: TokenIdentifier, cut_amount: BigUint) {
let fees_collector_address = self.fees_collector_address().get();
let fees_collector_address = fees_collector_mapper.get();
let _: IgnoreValue = self
.fees_collector_proxy(fees_collector_address)
.deposit_swap_fees()
Expand Down Expand Up @@ -346,39 +283,6 @@ pub trait FeeModule:
}
}

#[endpoint(setFeeOn)]
fn set_fee_on(
&self,
enabled: bool,
fee_to_address: ManagedAddress,
fee_token: TokenIdentifier,
) {
self.require_caller_has_owner_permissions();
let is_dest = self
.destination_map()
.keys()
.any(|dest_address| dest_address == fee_to_address);

if enabled {
require!(!is_dest, ERROR_ALREADY_FEE_DEST);
self.destination_map().insert(fee_to_address, fee_token);
} else {
require!(is_dest, ERROR_NOT_FEE_DEST);
let dest_fee_token = self.destination_map().get(&fee_to_address).unwrap();
require!(fee_token == dest_fee_token, ERROR_BAD_TOKEN_FEE_DEST);
self.destination_map().remove(&fee_to_address);
}
}

#[view(getFeeDestinations)]
fn get_fee_destinations(&self) -> MultiValueEncoded<(ManagedAddress, TokenIdentifier)> {
let mut result = MultiValueEncoded::new();
for pair in self.destination_map().iter() {
result.push((pair.0, pair.1))
}
result
}

#[view(getTrustedSwapPairs)]
fn get_trusted_swap_pairs(&self) -> MultiValueEncoded<(TokenPair<Self::Api>, ManagedAddress)> {
let mut result = MultiValueEncoded::new();
Expand All @@ -397,23 +301,22 @@ pub trait FeeModule:
result
}

fn get_fees_collector_address_mapper(
&self,
) -> SingleValueMapper<ManagedAddress, ManagedAddress> {
let router_address = self.router_address().get();
SingleValueMapper::<_, _, ManagedAddress>::new_from_address(
router_address,
StorageKey::new(FEES_COLLECTOR_ADDRESS_STORAGE_KEY),
)
}

#[proxy]
fn pair_proxy(&self) -> self_proxy::Proxy<Self::Api>;

#[proxy]
fn fees_collector_proxy(&self, sc_address: ManagedAddress) -> fees_collector::Proxy<Self::Api>;

#[view(getFeesCollectorAddress)]
#[storage_mapper("feesCollectorAddress")]
fn fees_collector_address(&self) -> SingleValueMapper<ManagedAddress>;

#[view(getFeesCollectorCutPercentage)]
#[storage_mapper("feesCollectorCutPercentage")]
fn fees_collector_cut_percentage(&self) -> SingleValueMapper<u64>;

#[storage_mapper("fee_destination")]
fn destination_map(&self) -> MapMapper<ManagedAddress, TokenIdentifier>;

#[storage_mapper("trusted_swap_pair")]
fn trusted_swap_pair(&self) -> MapMapper<TokenPair<Self::Api>, ManagedAddress>;

Expand Down
32 changes: 8 additions & 24 deletions dex/pair/src/pair_actions/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,7 @@ pub trait SwapModule:
);
require!(initial_k <= new_k, ERROR_K_INVARIANT_FAILED);

if swap_context.fee_amount > 0 {
self.send_fee(
&mut storage_cache,
swap_context.swap_tokens_order,
&swap_context.input_token_id,
&swap_context.fee_amount,
);
}
self.send_fee(&swap_context.input_token_id, &swap_context.fee_amount);

let caller = self.blockchain().get_caller();
let output_payments = self.build_swap_output_payments(&swap_context);
Expand Down Expand Up @@ -211,12 +204,7 @@ pub trait SwapModule:
require!(initial_k <= new_k, ERROR_K_INVARIANT_FAILED);

if swap_context.fee_amount > 0 {
self.send_fee(
&mut storage_cache,
swap_context.swap_tokens_order,
&swap_context.input_token_id,
&swap_context.fee_amount,
);
self.send_fee(&swap_context.input_token_id, &swap_context.fee_amount);
}

let caller = self.blockchain().get_caller();
Expand Down Expand Up @@ -251,12 +239,10 @@ pub trait SwapModule:
context.final_output_amount = amount_out_optimal;

let mut amount_in_after_fee = context.input_token_amount.clone();
if self.is_fee_enabled() {
let fee_amount = self.get_special_fee_from_input(&amount_in_after_fee);
amount_in_after_fee -= &fee_amount;
let fee_amount = self.get_special_fee_from_input(&amount_in_after_fee);
amount_in_after_fee -= &fee_amount;

context.fee_amount = fee_amount;
}
context.fee_amount = fee_amount;

*storage_cache.get_mut_reserve_in(context.swap_tokens_order) += amount_in_after_fee;
*storage_cache.get_mut_reserve_out(context.swap_tokens_order) -=
Expand Down Expand Up @@ -284,12 +270,10 @@ pub trait SwapModule:
context.final_input_amount = amount_in_optimal.clone();

let mut amount_in_optimal_after_fee = amount_in_optimal;
if self.is_fee_enabled() {
let fee_amount = self.get_special_fee_from_input(&amount_in_optimal_after_fee);
amount_in_optimal_after_fee -= &fee_amount;
let fee_amount = self.get_special_fee_from_input(&amount_in_optimal_after_fee);
amount_in_optimal_after_fee -= &fee_amount;

context.fee_amount = fee_amount;
}
context.fee_amount = fee_amount;

*storage_cache.get_mut_reserve_in(context.swap_tokens_order) += amount_in_optimal_after_fee;
*storage_cache.get_mut_reserve_out(context.swap_tokens_order) -=
Expand Down
Loading