Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Core DCA #5106

Merged
merged 31 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0b80187
fix: read and remove request state on refunding
msgmaxim Jul 30, 2024
cac0630
feat: DCA works for non-CCM swaps
msgmaxim Jul 30, 2024
73593e7
feat: DCA full refund
msgmaxim Jul 31, 2024
c031439
feat: DCA partial refund
msgmaxim Jul 31, 2024
d7f2c3f
refactor: use egress_for_swap for CCM
msgmaxim Jul 31, 2024
899cccf
test: dca_with_fok_fully_executed
msgmaxim Aug 1, 2024
e0dcf0b
feat: rename fee->egress_fee in SwapEgressScheduled
msgmaxim Aug 1, 2024
7a66f1f
feat: add dca_parameters to the extrinsic
msgmaxim Aug 1, 2024
30e1915
feat: dca parameters in channel actions + migration
msgmaxim Aug 2, 2024
e9dc8a8
chore: clippy fix
msgmaxim Aug 2, 2024
a132930
feat: DCA support for CCM swaps
msgmaxim Aug 6, 2024
cfb2c01
chore: address minor review comments
msgmaxim Aug 7, 2024
f450573
refactor: deduplicate SwapRequestComplited
msgmaxim Aug 7, 2024
bc02334
Merge branch 'main' into feat/dca
msgmaxim Aug 7, 2024
a74e647
fix: use chunk interval for DCA
msgmaxim Aug 8, 2024
c71f870
refactor: prepare_next_chunk returns Option
msgmaxim Aug 8, 2024
e3b7b88
refactor: prepare_next_chunks records prev chunk output
msgmaxim Aug 9, 2024
221f053
chore: refund_params getter
msgmaxim Aug 9, 2024
7d5a996
fix: ccm transfers should charge broker fee
dandanlen Aug 7, 2024
3edafdb
fix: migration for ccm broker fee
msgmaxim Aug 9, 2024
7af1e38
chore: address minor review comments
msgmaxim Aug 12, 2024
7589dac
feat: try-runtime migration test for DCA
msgmaxim Aug 12, 2024
f15f4fa
Merge branch 'main' into feat/dca
msgmaxim Aug 13, 2024
2719d61
test: check dca state in tests
msgmaxim Aug 13, 2024
5ebc809
test: use swap rate other than 1 in swapping tests
msgmaxim Aug 13, 2024
5ed5175
feat: FoK lifetime per chunk
msgmaxim Aug 14, 2024
4e844a2
Merge branch 'main' into feat/dca
msgmaxim Aug 15, 2024
5efcce6
refactor: aliases for ChannelRefundParameters
msgmaxim Aug 15, 2024
9064003
revert: refund_block
msgmaxim Aug 16, 2024
100467b
Merge branch 'main' into feat/dca
msgmaxim Aug 16, 2024
5e4bd9c
chore: fix migration
dandanlen Aug 16, 2024
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
204 changes: 125 additions & 79 deletions state-chain/pallets/cf-swapping/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1234,37 +1234,41 @@ pub mod pallet {
};

// In case of DCA, there may be extra input that we need to refund
let remaining_input_amount = match request.state {
SwapRequestState::Regular { dca_state, .. } => dca_state.remaining_input_amount,
_ => 0,
};

let refund_amount = swap.input_amount + remaining_input_amount;
match request.state {
SwapRequestState::Regular {
dca_state: DCAState { remaining_input_amount, accumulated_output_amount, .. },
output_address,
} => {
Self::egress_for_swap(
request.id,
swap.input_amount + remaining_input_amount,
dandanlen marked this conversation as resolved.
Show resolved Hide resolved
request.input_asset,
refund_params.refund_address,
true, /* refund */
);

match T::EgressHandler::schedule_egress(
swap.from,
refund_amount,
refund_params.refund_address,
None,
) {
Ok(ScheduledEgressDetails { egress_id, egress_amount, fee_withheld }) => {
Self::deposit_event(Event::<T>::RefundEgressScheduled {
swap_request_id: swap.swap_request_id,
egress_id,
asset: swap.from,
amount: egress_amount,
egress_fee: fee_withheld,
});
if accumulated_output_amount > 0 {
Self::egress_for_swap(
swap.swap_request_id,
accumulated_output_amount,
request.output_asset,
output_address,
false, /* refund */
);
}
},
Err(err) => {
Self::deposit_event(Event::<T>::RefundEgressIgnored {
swap_request_id: swap.swap_request_id,
asset: swap.from,
amount: swap.input_amount,
reason: err.into(),
});
SwapRequestState::Ccm { output_address: _, .. } => {
// TODO: add remaining DCA input to the refunded amount:
dandanlen marked this conversation as resolved.
Show resolved Hide resolved
Self::egress_for_swap(
request.id,
swap.input_amount,
request.input_asset,
refund_params.refund_address,
true, /* refund */
);
},
}
_ => {},
};
}

fn process_swap_outcome(swap: SwapState) {
Expand Down Expand Up @@ -1366,38 +1370,15 @@ pub mod pallet {
} else {
debug_assert!(dca_state.remaining_input_amount == 0);

let total_output_amount =
output_amount + dca_state.accumulated_output_amount;

Self::deposit_event(Event::<T>::SwapRequestCompleted { swap_request_id });
match T::EgressHandler::schedule_egress(

Self::egress_for_swap(
swap_request_id,
output_amount + dca_state.accumulated_output_amount,
dandanlen marked this conversation as resolved.
Show resolved Hide resolved
swap.output_asset(),
total_output_amount,
output_address.clone(),
None, /* ccm metadata */
) {
Ok(ScheduledEgressDetails {
egress_id,
egress_amount,
fee_withheld,
}) => {
Self::deposit_event(Event::<T>::SwapEgressScheduled {
swap_request_id: swap.swap.swap_request_id,
egress_id,
asset: swap.output_asset(),
amount: egress_amount,
fee: fee_withheld,
});
},
Err(err) => {
Self::deposit_event(Event::<T>::SwapEgressIgnored {
swap_request_id,
asset: swap.output_asset(),
amount: output_amount,
reason: err.into(),
});
},
};
false, /* refund */
);
}
},
SwapRequestState::NetworkFee => {
Expand Down Expand Up @@ -1637,6 +1618,53 @@ pub mod pallet {
});
NetworkFeeTaken { remaining_amount: remaining, network_fee: fee }
}

fn egress_for_swap(
swap_request_id: SwapRequestId,
amount: AssetAmount,
asset: Asset,
address: ForeignChainAddress,
refund: bool,
j4m1ef0rd marked this conversation as resolved.
Show resolved Hide resolved
) {
match T::EgressHandler::schedule_egress(
asset, amount, address, None, /* ccm metadata */
) {
Ok(ScheduledEgressDetails { egress_id, egress_amount, fee_withheld }) =>
if refund {
Self::deposit_event(Event::<T>::RefundEgressScheduled {
swap_request_id,
egress_id,
asset,
amount: egress_amount,
egress_fee: fee_withheld,
});
} else {
Self::deposit_event(Event::<T>::SwapEgressScheduled {
dandanlen marked this conversation as resolved.
Show resolved Hide resolved
swap_request_id,
egress_id,
asset,
amount: egress_amount,
fee: fee_withheld,
});
},
Err(err) =>
if refund {
Self::deposit_event(Event::<T>::RefundEgressIgnored {
swap_request_id,
asset,
amount,
reason: err.into(),
});
} else {
Self::deposit_event(Event::<T>::SwapEgressIgnored {
swap_request_id,
asset,
amount,
reason: err.into(),
});
},
};
}
}

impl<T: Config> SwapRequestHandler for Pallet<T> {
Expand Down Expand Up @@ -1733,22 +1761,6 @@ pub mod pallet {
origin: origin.clone(),
});

// Now that we know input amount, we can calculate the minimum output amount:
let swap_refund_params = refund_params.clone().map(|params| SwapRefundParameters {
refund_block: {
use sp_arithmetic::traits::UniqueSaturatedInto;
// In practice block number always fits in u32:
let current_block: u32 =
frame_system::Pallet::<T>::block_number().unique_saturated_into();
current_block.saturating_add(params.retry_duration)
},
min_output: u128::try_from(cf_amm::common::output_amount_ceil(
input_amount.into(),
params.min_price,
))
.unwrap_or(u128::MAX),
});

match request_type {
SwapRequestType::NetworkFee => {
Self::schedule_swap(
Expand Down Expand Up @@ -1802,11 +1814,20 @@ pub mod pallet {

let chunk_input_amount = dca_state.prepare_next_chunk();

let swap_refund_params = refund_params.as_ref().map(|params| {
utilities::calculate_swap_refund_parameters(
params,
// In practice block number always fits in u32:
frame_system::Pallet::<T>::block_number().unique_saturated_into(),
chunk_input_amount.into(),
)
});

Self::schedule_swap(
input_asset,
output_asset,
chunk_input_amount,
swap_refund_params.clone(),
swap_refund_params,
SwapType::Swap,
request_id,
);
Expand All @@ -1817,7 +1838,7 @@ pub mod pallet {
id: request_id,
input_asset,
output_asset,
refund_params: refund_params.clone(),
refund_params,
state: SwapRequestState::Regular {
output_address: output_address.clone(),
dca_state,
Expand Down Expand Up @@ -1862,11 +1883,20 @@ pub mod pallet {

// See if princial swap is needed, schedule it first if so:
if input_asset != output_asset && !principal_swap_amount.is_zero() {
let swap_refund_params = refund_params.as_ref().map(|params| {
utilities::calculate_swap_refund_parameters(
dandanlen marked this conversation as resolved.
Show resolved Hide resolved
params,
// In practice block number always fits in u32:
frame_system::Pallet::<T>::block_number().unique_saturated_into(),
principal_swap_amount.into(),
)
});

let swap_id = Self::schedule_swap(
input_asset,
output_asset,
principal_swap_amount,
swap_refund_params.clone(),
swap_refund_params,
SwapType::CcmPrincipal,
request_id,
);
Expand Down Expand Up @@ -1992,10 +2022,11 @@ impl<T: Config> ExecutionCondition for NoPendingSwaps<T> {
SwapQueue::<T>::iter().all(|(_, swaps)| swaps.is_empty())
}
}
pub mod utilities {

pub(crate) mod utilities {
use super::*;

pub fn calculate_network_fee(
pub(crate) fn calculate_network_fee(
fee_percentage: Permill,
input: AssetAmount,
) -> (AssetAmount, AssetAmount) {
Expand All @@ -2009,7 +2040,7 @@ pub mod utilities {
///
/// The value should be large enough to allow a good estimation of the fee, but small enough
/// to not exhaust the pool liquidity.
pub fn fee_estimation_basis(asset: Asset) -> Option<u128> {
pub(crate) fn fee_estimation_basis(asset: Asset) -> Option<u128> {
use cf_primitives::FLIPPERINOS_PER_FLIP;
/// 20 Dollars.
const USD_ESTIMATION_CAP: u128 = 20_000_000;
Expand All @@ -2022,4 +2053,19 @@ pub mod utilities {
_ => None,
}
}

pub(super) fn calculate_swap_refund_parameters(
params: &ChannelRefundParameters,
deposit_block: u32,
input_amount: AssetAmount,
) -> SwapRefundParameters {
SwapRefundParameters {
refund_block: deposit_block.saturating_add(params.retry_duration),
min_output: u128::try_from(cf_amm::common::output_amount_ceil(
input_amount.into(),
params.min_price,
))
.unwrap_or(u128::MAX),
}
}
}
Loading