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: on chain swaps #5595

Merged
merged 9 commits into from
Feb 17, 2025
Merged
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
15 changes: 1 addition & 14 deletions state-chain/amm-math/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,9 @@

pub mod test_utilities;

pub use cf_primitives::Price;
use sp_core::{U256, U512};

// TODO: Consider alternative representation for Price:
//
// increasing Price to U512 or switch to a f64 (f64 would only be for the external
// price representation), as at low ticks the precision in the price is VERY LOW, but this does not
// cause any problems for the AMM code in terms of correctness

/// This is the ratio of equivalently valued amounts of asset One and asset Zero.
///
/// The price is always measured in amount of asset One per unit of asset Zero. Therefore as asset
/// zero becomes more valuable relative to asset one the price's literal value goes up, and vice
/// versa. This ratio is represented as a fixed point number with `PRICE_FRACTIONAL_BITS` fractional
/// bits.
pub type Price = U256;

/// Represents an amount of an asset, in its smallest unit i.e. Ethereum has 10^-18 precision, and
/// therefore an `Amount` with the literal value of `1` would represent 10^-18 Ethereum.
pub type Amount = U256;
Expand Down
50 changes: 46 additions & 4 deletions state-chain/chains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@ pub enum SwapOrigin<AccountId> {
broker_id: Option<AccountId>,
},
Internal,
OnChainAccount(AccountId),
}

impl<AccountId> SwapOrigin<AccountId> {
Expand All @@ -694,6 +695,7 @@ impl<AccountId> SwapOrigin<AccountId> {
Self::DepositChannel { ref broker_id, .. } => Some(broker_id),
Self::Vault { ref broker_id, .. } => broker_id.as_ref(),
Self::Internal => None,
Self::OnChainAccount(_) => None,
}
}
}
Expand Down Expand Up @@ -921,6 +923,49 @@ pub struct ChannelRefundParameters<A> {
pub min_price: Price,
}

#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, MaxEncodedLen, PartialOrd, Ord)]
pub struct RefundParametersExtendedGeneric<Address, AccountId> {
pub retry_duration: cf_primitives::BlockNumber,
pub refund_destination: AccountOrAddress<Address, AccountId>,
pub min_price: Price,
}

pub type RefundParametersExtended<AccountId> =
RefundParametersExtendedGeneric<ForeignChainAddress, AccountId>;
msgmaxim marked this conversation as resolved.
Show resolved Hide resolved
pub type RefundParametersExtendedEncoded<AccountId> =
RefundParametersExtendedGeneric<EncodedAddress, AccountId>;

impl<AccountId> RefundParametersExtended<AccountId> {
pub fn to_encoded<Converter: AddressConverter>(
self,
) -> RefundParametersExtendedEncoded<AccountId> {
RefundParametersExtendedEncoded {
retry_duration: self.retry_duration,
refund_destination: match self.refund_destination {
AccountOrAddress::ExternalAddress(address) =>
AccountOrAddress::ExternalAddress(Converter::to_encoded_address(address)),
AccountOrAddress::InternalAccount(account_id) =>
AccountOrAddress::InternalAccount(account_id),
},
min_price: self.min_price,
}
}

pub fn min_output_amount(&self, input_amount: AssetAmount) -> AssetAmount {
use sp_runtime::traits::UniqueSaturatedInto;
cf_amm_math::output_amount_ceil(input_amount.into(), self.min_price).unique_saturated_into()
}
}

/// AccountOrAddress is a enum that can represent an internal account or an external address.
/// This is used to represent the destination address for an egress or an internal account
/// to move funds internally.
#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo, PartialOrd, Ord)]
pub enum AccountOrAddress<Address, AccountId> {
InternalAccount(AccountId),
ExternalAddress(Address),
}

#[cfg(feature = "runtime-benchmarks")]
impl<A: BenchmarkValue> BenchmarkValue for ChannelRefundParameters<A> {
fn benchmark_value() -> Self {
Expand All @@ -931,6 +976,7 @@ impl<A: BenchmarkValue> BenchmarkValue for ChannelRefundParameters<A> {
}
}
}

#[cfg(feature = "std")]
pub type RefundParametersRpc = ChannelRefundParameters<crate::address::AddressString>;
pub type ChannelRefundParametersDecoded = ChannelRefundParameters<ForeignChainAddress>;
Expand All @@ -954,10 +1000,6 @@ impl<A: Clone> ChannelRefundParameters<A> {
min_price: self.min_price,
})
}
pub fn min_output_amount(&self, input_amount: AssetAmount) -> AssetAmount {
use sp_runtime::traits::UniqueSaturatedInto;
cf_amm_math::output_amount_ceil(input_amount.into(), self.min_price).unique_saturated_into()
}
}

pub enum RequiresSignatureRefresh<C: ChainCrypto, Api: ApiCall<C>> {
Expand Down
20 changes: 14 additions & 6 deletions state-chain/pallets/cf-ingress-egress/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ use cf_chains::{
Chain, ChainCrypto, ChannelLifecycleHooks, ChannelRefundParameters,
ChannelRefundParametersDecoded, ConsolidateCall, DepositChannel,
DepositDetailsToTransactionInId, DepositOriginType, ExecutexSwapAndCall, FetchAssetParams,
ForeignChainAddress, IntoTransactionInIdForAnyChain, RejectCall, SwapOrigin,
TransferAssetParams,
ForeignChainAddress, IntoTransactionInIdForAnyChain, RefundParametersExtended, RejectCall,
SwapOrigin, TransferAssetParams,
};
use cf_primitives::{
AccountRole, AffiliateShortId, Affiliates, Asset, AssetAmount, BasisPoints, Beneficiaries,
Expand All @@ -47,7 +47,7 @@ use cf_traits::{
ChannelIdAllocator, DepositApi, EgressApi, EpochInfo, FeePayment,
FetchesTransfersLimitProvider, GetBlockHeight, IngressEgressFeeApi, IngressSink, IngressSource,
NetworkEnvironmentProvider, OnDeposit, PoolApi, ScheduledEgressDetails, SwapLimitsProvider,
SwapRequestHandler, SwapRequestType,
SwapOutputAction, SwapRequestHandler, SwapRequestType,
};
use frame_support::{
pallet_prelude::{OptionQuery, *},
Expand Down Expand Up @@ -1883,11 +1883,19 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
amount_after_fees.into(),
destination_asset,
SwapRequestType::Regular {
ccm_deposit_metadata: deposit_metadata,
output_address: destination_address,
output_action: SwapOutputAction::Egress {
ccm_deposit_metadata: deposit_metadata,
output_address: destination_address,
},
},
broker_fees,
refund_params,
refund_params.map(|params| RefundParametersExtended {
retry_duration: params.retry_duration,
refund_destination: cf_chains::AccountOrAddress::ExternalAddress(
params.refund_address,
),
min_price: params.min_price,
}),
dca_params,
origin.into(),
);
Expand Down
29 changes: 23 additions & 6 deletions state-chain/pallets/cf-ingress-egress/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use cf_traits::{
swap_request_api::{MockSwapRequest, MockSwapRequestHandler},
},
BalanceApi, DepositApi, EgressApi, EpochInfo, FetchesTransfersLimitProvider, FundingInfo,
GetBlockHeight, SafeMode, ScheduledEgressDetails, SwapRequestType,
GetBlockHeight, SafeMode, ScheduledEgressDetails, SwapOutputAction, SwapRequestType,
};
use frame_support::{
assert_err, assert_noop, assert_ok,
Expand Down Expand Up @@ -1865,7 +1865,12 @@ fn can_request_swap_via_extrinsic() {
input_asset: INPUT_ASSET,
output_asset: OUTPUT_ASSET,
input_amount: INPUT_AMOUNT,
swap_type: SwapRequestType::Regular { output_address, ccm_deposit_metadata: None },
swap_type: SwapRequestType::Regular {
output_action: SwapOutputAction::Egress {
output_address,
ccm_deposit_metadata: None
}
},
broker_fees: bounded_vec![Beneficiary { account: BROKER, bps: 0 }],
origin: SwapOrigin::Vault {
tx_id: TransactionInIdForAnyChain::Evm(H256::default()),
Expand Down Expand Up @@ -1925,7 +1930,12 @@ fn vault_swaps_support_affiliate_fees() {
input_asset: INPUT_ASSET,
output_asset: OUTPUT_ASSET,
input_amount: INPUT_AMOUNT,
swap_type: SwapRequestType::Regular { output_address, ccm_deposit_metadata: None },
swap_type: SwapRequestType::Regular {
output_action: SwapOutputAction::Egress {
output_address,
ccm_deposit_metadata: None
}
},
broker_fees: bounded_vec![
Beneficiary { account: BROKER, bps: BROKER_FEE },
// Only one affiliate is used (short id for affiliate 2 has not been
Expand Down Expand Up @@ -1982,7 +1992,12 @@ fn charge_no_broker_fees_on_unknown_primary_broker() {
input_asset: INPUT_ASSET,
output_asset: OUTPUT_ASSET,
input_amount: INPUT_AMOUNT,
swap_type: SwapRequestType::Regular { output_address, ccm_deposit_metadata: None },
swap_type: SwapRequestType::Regular {
output_action: SwapOutputAction::Egress {
output_address,
ccm_deposit_metadata: None
}
},
broker_fees: Default::default(),
origin: SwapOrigin::Vault {
tx_id: cf_chains::TransactionInIdForAnyChain::Evm(H256::default()),
Expand Down Expand Up @@ -2040,8 +2055,10 @@ fn can_request_ccm_swap_via_extrinsic() {
output_asset: OUTPUT_ASSET,
input_amount: INPUT_AMOUNT,
swap_type: SwapRequestType::Regular {
output_address,
ccm_deposit_metadata: Some(ccm_deposit_metadata)
output_action: SwapOutputAction::Egress {
output_address,
ccm_deposit_metadata: Some(ccm_deposit_metadata)
}
},
broker_fees: bounded_vec![Beneficiary { account: BROKER, bps: 0 }],
origin: SwapOrigin::Vault {
Expand Down
7 changes: 5 additions & 2 deletions state-chain/pallets/cf-ingress-egress/src/tests/boost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,7 @@ fn taking_network_fee_from_boost_fee() {
mod vault_swaps {

use cf_chains::ChannelRefundParameters;
use cf_traits::SwapOutputAction;

use crate::BoostedVaultTransactions;

Expand Down Expand Up @@ -1231,8 +1232,10 @@ mod vault_swaps {
output_asset: OUTPUT_ASSET,
input_amount: DEPOSIT_AMOUNT - BOOST_FEE - INGRESS_FEE,
swap_type: SwapRequestType::Regular {
output_address,
ccm_deposit_metadata: None
output_action: SwapOutputAction::Egress {
output_address,
ccm_deposit_metadata: None
}
},
broker_fees: bounded_vec![Beneficiary { account: BROKER, bps: 5 }],
origin: SwapOrigin::Vault {
Expand Down
27 changes: 27 additions & 0 deletions state-chain/pallets/cf-lp/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,32 @@ mod benchmarks {
);
}

#[benchmark]
fn internal_swap() {
let lp_id =
T::AccountRoleRegistry::whitelisted_caller_with_role(AccountRole::LiquidityProvider)
.unwrap();

let caller = RawOrigin::Signed(lp_id.clone());

assert_ok!(Pallet::<T>::register_liquidity_refund_address(
caller.clone().into(),
EncodedAddress::Eth(Default::default()),
));

T::BalanceApi::credit_account(&lp_id, Asset::Eth, 1000);

#[extrinsic_call]
Pallet::<T>::internal_swap(
caller,
1000,
Asset::Eth,
Asset::Flip,
0,
Default::default(),
None,
);
}

impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test,);
}
Loading
Loading