diff --git a/zenlink-protocol/src/fee/mock.rs b/zenlink-protocol/src/fee/mock.rs index 1ecd55a..b42d8b2 100644 --- a/zenlink-protocol/src/fee/mock.rs +++ b/zenlink-protocol/src/fee/mock.rs @@ -71,6 +71,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub const ZenlinkPalletId: PalletId = PalletId(*b"/zenlink"); + pub const NativeSwapFeesPotId: PalletId = PalletId(*b"/swpfees"); pub const MaxReserves: u32 = 50; pub const MaxLocks:u32 = 50; } @@ -145,6 +146,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type MultiAssetsHandler = ZenlinkMultiAssets>; type PalletId = ZenlinkPalletId; + type NativeSwapFeesPotId = NativeSwapFeesPotId; type AssetId = AssetId; type LpGenerate = PairLpGenerate; type SelfParaId = (); diff --git a/zenlink-protocol/src/foreign/mock.rs b/zenlink-protocol/src/foreign/mock.rs index 3dc608b..87ba814 100644 --- a/zenlink-protocol/src/foreign/mock.rs +++ b/zenlink-protocol/src/foreign/mock.rs @@ -35,6 +35,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub const ZenlinkPalletId: PalletId = PalletId(*b"/zenlink"); + pub const NativeSwapFeesPotId: PalletId = PalletId(*b"/swpfees"); pub const MaxReserves: u32 = 50; } @@ -81,6 +82,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type MultiAssetsHandler = ZenlinkMultiAssets; type PalletId = ZenlinkPalletId; + type NativeSwapFeesPotId = NativeSwapFeesPotId; type AssetId = AssetId; type LpGenerate = PairLpGenerate; type SelfParaId = (); diff --git a/zenlink-protocol/src/lib.rs b/zenlink-protocol/src/lib.rs index e9e15b8..124d122 100644 --- a/zenlink-protocol/src/lib.rs +++ b/zenlink-protocol/src/lib.rs @@ -96,6 +96,9 @@ pub mod pallet { /// This parachain id. type SelfParaId: Get; + /// Account Identifier from which the internal Pot is generated. + type NativeSwapFeesPotId: Get; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -193,6 +196,10 @@ pub mod pallet { ValueQuery, >; + #[pallet::storage] + #[pallet::getter(fn get_native_swap_fee_factor)] + pub type NativeSwapFeeFactor = StorageValue<_, u128, ValueQuery>; + #[pallet::genesis_config] /// Refer: https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Pair.sol#L88 pub struct GenesisConfig { @@ -984,5 +991,19 @@ pub mod pallet { Ok(()) } + + #[pallet::call_index(18)] + #[pallet::weight(100_000_000)] + #[frame_support::transactional] + pub fn set_native_swap_fee_factor( + origin: OriginFor, + native_swap_fees_factor: u128, + ) -> DispatchResult { + ensure_root(origin)?; + + NativeSwapFeeFactor::::put(native_swap_fees_factor); + + Ok(()) + } } } diff --git a/zenlink-protocol/src/primitives.rs b/zenlink-protocol/src/primitives.rs index 4afc9b9..7bd79dc 100644 --- a/zenlink-protocol/src/primitives.rs +++ b/zenlink-protocol/src/primitives.rs @@ -42,12 +42,17 @@ pub struct AssetId { pub trait AssetInfo { fn is_support(&self) -> bool; + fn is_native(&self, self_chain_id: u32) -> bool; } impl AssetInfo for AssetId { fn is_support(&self) -> bool { matches!(self.asset_type, NATIVE | LIQUIDITY | LOCAL | RESERVED) } + + fn is_native(&self, self_chain_id: u32) -> bool { + self.chain_id == self_chain_id && self.asset_type == NATIVE && self.asset_index == 0 + } } impl AssetId { diff --git a/zenlink-protocol/src/swap/mock.rs b/zenlink-protocol/src/swap/mock.rs index a7c5794..29949bc 100644 --- a/zenlink-protocol/src/swap/mock.rs +++ b/zenlink-protocol/src/swap/mock.rs @@ -71,6 +71,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub const ZenlinkPalletId: PalletId = PalletId(*b"/zenlink"); + pub const NativeSwapFeesPotId: PalletId = PalletId(*b"/swpfees"); pub const MaxReserves: u32 = 50; pub const MaxLocks:u32 = 50; } @@ -145,6 +146,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type MultiAssetsHandler = ZenlinkMultiAssets>; type PalletId = ZenlinkPalletId; + type NativeSwapFeesPotId = NativeSwapFeesPotId; type AssetId = AssetId; type LpGenerate = PairLpGenerate; type SelfParaId = (); diff --git a/zenlink-protocol/src/swap/mod.rs b/zenlink-protocol/src/swap/mod.rs index d35945b..5bc73e5 100644 --- a/zenlink-protocol/src/swap/mod.rs +++ b/zenlink-protocol/src/swap/mod.rs @@ -214,12 +214,36 @@ impl Pallet { path: &[T::AssetId], recipient: &T::AccountId, ) -> DispatchResult { - let amounts = Self::get_amount_out_by_path(amount_in, path)?; + let mut amount_in_less_native_swap_fee = amount_in; + if path[0].is_native(T::SelfParaId::get()) { + let native_swap_fee_factor = Self::get_native_swap_fee_factor(); + if native_swap_fee_factor != 0u128 { + // charge a configurable % going to a pallet account for later distribution + let native_swap_fee = + amount_in.checked_div(native_swap_fee_factor).unwrap_or_default(); + amount_in_less_native_swap_fee = amount_in.saturating_sub(native_swap_fee); + let native_swap_fees_account = + T::NativeSwapFeesPotId::get().into_account_truncating(); + T::MultiAssetsHandler::transfer( + path[0], + who, + &native_swap_fees_account, + native_swap_fee, + )?; + } + } + + let amounts = Self::get_amount_out_by_path(amount_in_less_native_swap_fee, path)?; ensure!(amounts[amounts.len() - 1] >= amount_out_min, Error::::InsufficientTargetAmount); let pair_account = Self::pair_account_id(path[0], path[1]); - T::MultiAssetsHandler::transfer(path[0], who, &pair_account, amount_in)?; + T::MultiAssetsHandler::transfer( + path[0], + who, + &pair_account, + amount_in_less_native_swap_fee, + )?; Self::swap(&amounts, path, recipient)?; Self::deposit_event(Event::AssetSwap( diff --git a/zenlink-swap-router/src/mock.rs b/zenlink-swap-router/src/mock.rs index 56d9d52..87b0129 100644 --- a/zenlink-swap-router/src/mock.rs +++ b/zenlink-swap-router/src/mock.rs @@ -26,8 +26,7 @@ use crate as router; use crate::{Config, Pallet}; use orml_traits::{parameter_type_with_key, MultiCurrency}; use zenlink_protocol::{ - AssetBalance, AssetId, AssetIdConverter, LocalAssetHandler, PairLpGenerate, ZenlinkMultiAssets, - LOCAL, + AssetBalance, AssetId, LocalAssetHandler, PairLpGenerate, ZenlinkMultiAssets, LOCAL, }; use zenlink_stable_amm::traits::{StablePoolLpCurrencyIdGenerate, ValidateCurrency}; @@ -40,6 +39,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; pub const StableAmmPalletId: PalletId = PalletId(*b"/zlkSAmm"); pub const ZenlinkPalletId: PalletId = PalletId(*b"/zenlink"); + pub const NativeSwapFeesPotId: PalletId = PalletId(*b"/swpfees"); pub const MaxReserves: u32 = 50; pub const MaxLocks:u32 = 50; pub const MinimumPeriod: Moment = SLOT_DURATION / 2; @@ -210,13 +210,10 @@ impl zenlink_protocol::Config for Test { type RuntimeEvent = RuntimeEvent; type MultiAssetsHandler = ZenlinkMultiAssets>; type PalletId = ZenlinkPalletId; + type NativeSwapFeesPotId = NativeSwapFeesPotId; type AssetId = AssetId; - type LpGenerate = PairLpGenerate; - type TargetChains = (); type SelfParaId = SelfParaId; - type XcmExecutor = (); - type AccountIdConverter = (); - type AssetIdConverter = AssetIdConverter; + type LpGenerate = PairLpGenerate; type WeightInfo = (); }