Skip to content

Commit

Permalink
feat: Maximum Swap amount (#4238)
Browse files Browse the repository at this point in the history
Co-authored-by: dandanlen <3168260+dandanlen@users.noreply.github.com>
  • Loading branch information
syan095 and dandanlen authored Nov 15, 2023
1 parent 70f1db1 commit 9e20bc3
Show file tree
Hide file tree
Showing 6 changed files with 558 additions and 32 deletions.
7 changes: 7 additions & 0 deletions state-chain/pallets/cf-swapping/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,10 @@ The Gas budgets are exempt from this threshold (as gas budgets are expected to b
### Minimum Ccm Gas Budget

Ccm messages' Gas budget must be higher than this threshold, or the message will be rejected. This check is done regardless if the Gas needs to be swapped.

### Maximum Swap Threshold
At the onset of the chain, there's a upper limit to a single swap. This is configured by governance via `set_maximum_swap_amount`. Once the Swapping feature is stabilized, this threshold may be increased or removed in the future.

This threshold applies to all swaps, including both normal swaps and CCM gas and principal amount - though realistically this threshold should be set high enough that it does not impact most users.

If the swap amount is higher than the maximum swap threshold, the excess is confiscated by the chain into `CollectedRejectedFunds`, and the `SwapAmountConfiscated` event is emitted. This can be used to trace the confiscation and we may refund the user accordingly.
13 changes: 13 additions & 0 deletions state-chain/pallets/cf-swapping/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,19 @@ benchmarks! {
assert_eq!(crate::MinimumSwapAmount::<T>::get(asset), amount);
}

set_maximum_swap_amount {
let asset = Asset::Eth;
let amount = 1_000;
let call = Call::<T>::set_maximum_swap_amount {
asset,
amount: Some(amount),
};
}: {
let _ = call.dispatch_bypass_filter(<T as Chainflip>::EnsureGovernance::try_successful_origin().unwrap());
} verify {
assert_eq!(crate::MaximumSwapAmount::<T>::get(asset), Some(amount));
}

impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(),
Expand Down
92 changes: 75 additions & 17 deletions state-chain/pallets/cf-swapping/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ pub mod pallet {
pub type CollectedRejectedFunds<T: Config> =
StorageMap<_, Twox64Concat, Asset, AssetAmount, ValueQuery>;

/// Maximum amount allowed to be put into a swap. Excess amounts are confiscated.
#[pallet::storage]
#[pallet::getter(fn maximum_swap_amount)]
pub type MaximumSwapAmount<T: Config> = StorageMap<_, Twox64Concat, Asset, AssetAmount>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Expand Down Expand Up @@ -336,6 +341,17 @@ pub mod pallet {
destination_address: EncodedAddress,
deposit_metadata: CcmDepositMetadata,
},
MaximumSwapAmountSet {
asset: Asset,
amount: Option<AssetAmount>,
},
SwapAmountConfiscated {
swap_id: u64,
source_asset: Asset,
destination_asset: Asset,
total_amount: AssetAmount,
confiscated_amount: AssetAmount,
},
}
#[pallet::error]
pub enum Error<T> {
Expand Down Expand Up @@ -690,6 +706,31 @@ pub mod pallet {
Self::deposit_event(Event::<T>::MinimumSwapAmountSet { asset, amount });
Ok(())
}

/// Sets the Maximum amount allowed in a single swap for an asset.
///
/// Requires Governance.
///
/// ## Events
///
/// - [On update](Event::MaximumSwapAmountSet)
#[pallet::call_index(7)]
#[pallet::weight(T::WeightInfo::set_maximum_swap_amount())]
pub fn set_maximum_swap_amount(
origin: OriginFor<T>,
asset: Asset,
amount: Option<AssetAmount>,
) -> DispatchResult {
T::EnsureGovernance::ensure_origin(origin)?;

match amount {
Some(max) => MaximumSwapAmount::<T>::insert(asset, max),
None => MaximumSwapAmount::<T>::remove(asset),
};

Self::deposit_event(Event::<T>::MaximumSwapAmountSet { asset, amount });
Ok(())
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -859,22 +900,6 @@ pub mod pallet {
grouped_swaps
}

fn schedule_swap_internal(
from: Asset,
to: Asset,
amount: AssetAmount,
swap_type: SwapType,
) -> u64 {
let swap_id = SwapIdCounter::<T>::mutate(|id| {
id.saturating_accrue(1);
*id
});

SwapQueue::<T>::append(Swap::new(swap_id, from, to, amount, swap_type));

swap_id
}

/// Schedule the egress of a completed Cross chain message.
fn schedule_ccm_egress(
ccm_id: u64,
Expand Down Expand Up @@ -913,7 +938,40 @@ pub mod pallet {
Self::deposit_event(Event::<T>::CcmEgressScheduled { ccm_id, egress_id });
}

// Schedule and returns the swap id if the swap is valid.
/// Schedule the swap, assuming all checks already passed.
fn schedule_swap_internal(
from: Asset,
to: Asset,
amount: AssetAmount,
swap_type: SwapType,
) -> u64 {
let swap_id = SwapIdCounter::<T>::mutate(|id| {
id.saturating_accrue(1);
*id
});
let (swap_amount, confiscated_amount) = match MaximumSwapAmount::<T>::get(from) {
Some(max) => (sp_std::cmp::min(amount, max), amount.saturating_sub(max)),
None => (amount, Zero::zero()),
};
if !confiscated_amount.is_zero() {
CollectedRejectedFunds::<T>::mutate(from, |fund| {
*fund = fund.saturating_add(confiscated_amount)
});
Self::deposit_event(Event::<T>::SwapAmountConfiscated {
swap_id,
source_asset: from,
destination_asset: to,
total_amount: amount,
confiscated_amount,
});
}

SwapQueue::<T>::append(Swap::new(swap_id, from, to, swap_amount, swap_type));

swap_id
}

/// Schedule and returns the swap id if the swap is valid.
fn schedule_swap_with_check(
from: Asset,
to: Asset,
Expand Down
4 changes: 4 additions & 0 deletions state-chain/pallets/cf-swapping/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ impl WeightInfo for MockWeightInfo {
fn set_minimum_swap_amount() -> Weight {
Weight::from_parts(100, 0)
}

fn set_maximum_swap_amount() -> Weight {
Weight::from_parts(100, 0)
}
}

impl pallet_cf_swapping::Config for Test {
Expand Down
Loading

0 comments on commit 9e20bc3

Please sign in to comment.