diff --git a/state-chain/pallets/cf-ingress-egress/src/lib.rs b/state-chain/pallets/cf-ingress-egress/src/lib.rs index c9627e8290b..029be65ea24 100644 --- a/state-chain/pallets/cf-ingress-egress/src/lib.rs +++ b/state-chain/pallets/cf-ingress-egress/src/lib.rs @@ -144,7 +144,7 @@ impl CrossChainMessage { } } -pub const PALLET_VERSION: StorageVersion = StorageVersion::new(11); +pub const PALLET_VERSION: StorageVersion = StorageVersion::new(12); impl_pallet_safe_mode! { PalletSafeMode; diff --git a/state-chain/pallets/cf-ingress-egress/src/migrations.rs b/state-chain/pallets/cf-ingress-egress/src/migrations.rs index 0d6ccb8f43c..1f6d9829cfc 100644 --- a/state-chain/pallets/cf-ingress-egress/src/migrations.rs +++ b/state-chain/pallets/cf-ingress-egress/src/migrations.rs @@ -4,9 +4,12 @@ mod add_refund_params; pub mod remove_prewitnessed_deposits; pub mod withheld_transaction_fees; +mod add_dca_params; + pub type PalletMigration = ( VersionedMigration, remove_prewitnessed_deposits::Migration, 8, 9>, VersionedMigration, add_refund_params::Migration, 9, 10>, VersionedMigration, withheld_transaction_fees::Migration, 10, 11>, - PlaceholderMigration, 11>, + VersionedMigration, add_dca_params::Migration, 11, 12>, + PlaceholderMigration, 12>, ); diff --git a/state-chain/pallets/cf-ingress-egress/src/migrations/add_dca_params.rs b/state-chain/pallets/cf-ingress-egress/src/migrations/add_dca_params.rs new file mode 100644 index 00000000000..efaa3012900 --- /dev/null +++ b/state-chain/pallets/cf-ingress-egress/src/migrations/add_dca_params.rs @@ -0,0 +1,233 @@ +use crate::*; +use frame_support::traits::OnRuntimeUpgrade; + +pub(super) mod old { + + use super::*; + + #[derive(Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo)] + pub enum ChannelAction { + Swap { + destination_asset: Asset, + destination_address: ForeignChainAddress, + broker_fees: Beneficiaries, + refund_params: Option, + }, + LiquidityProvision { + lp_account: AccountId, + }, + CcmTransfer { + destination_asset: Asset, + destination_address: ForeignChainAddress, + channel_metadata: CcmChannelMetadata, + refund_params: Option, + }, + } + + #[derive(CloneNoBound, RuntimeDebug, PartialEq, Eq, Encode, Decode, TypeInfo)] + #[scale_info(skip_type_params(T, I))] + pub struct DepositChannelDetails, I: 'static> { + pub deposit_channel: DepositChannel, + /// The block number at which the deposit channel was opened, expressed as a block number + /// on the external Chain. + pub opened_at: TargetChainBlockNumber, + /// The last block on the target chain that the witnessing will witness it in. If funds are + /// sent after this block, they will not be witnessed. + pub expires_at: TargetChainBlockNumber, + + /// The action to be taken when the DepositChannel is deposited to. + pub action: ChannelAction, + /// The boost fee + pub boost_fee: BasisPoints, + /// Boost status, indicating whether there is pending boost on the channel + pub boost_status: BoostStatus>, + } + + #[frame_support::storage_alias] + pub type DepositChannelLookup, I: 'static> = StorageMap< + Pallet, + Twox64Concat, + TargetChainAccount, + DepositChannelDetails, + OptionQuery, + >; +} + +pub struct Migration, I: 'static>(PhantomData<(T, I)>); + +impl, I: 'static> OnRuntimeUpgrade for Migration { + fn on_runtime_upgrade() -> Weight { + DepositChannelLookup::::translate(|_, details: old::DepositChannelDetails| { + Some(DepositChannelDetails { + deposit_channel: details.deposit_channel, + opened_at: details.opened_at, + expires_at: details.expires_at, + action: match details.action { + old::ChannelAction::Swap { + destination_asset, + destination_address, + broker_fees, + refund_params, + } => ChannelAction::Swap { + destination_asset, + destination_address, + broker_fees, + refund_params, + dca_params: None, + }, + old::ChannelAction::LiquidityProvision { lp_account } => + ChannelAction::LiquidityProvision { lp_account }, + old::ChannelAction::CcmTransfer { + destination_asset, + destination_address, + channel_metadata, + refund_params, + } => ChannelAction::CcmTransfer { + destination_asset, + destination_address, + channel_metadata, + refund_params, + dca_params: None, + }, + }, + boost_fee: details.boost_fee, + boost_status: details.boost_status, + }) + }); + + Weight::zero() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, DispatchError> { + Ok(vec![]) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), DispatchError> { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + use crate::mock_btc::{new_test_ext, Test}; + use cf_chains::{ + btc::{ + deposit_address::{DepositAddress, TapscriptPath}, + BitcoinScript, ScriptPubkey, + }, + Bitcoin, + }; + + fn mock_deposit_channel() -> DepositChannel { + DepositChannel { + channel_id: 123, + address: ScriptPubkey::Taproot([0u8; 32]).clone(), + asset: ::ChainAsset::Btc, + state: DepositAddress { + pubkey_x: [1u8; 32], + script_path: Some(TapscriptPath { + salt: 123, + tweaked_pubkey_bytes: [2u8; 33], + tapleaf_hash: [3u8; 32], + unlock_script: BitcoinScript::new(Default::default()), + }), + }, + } + } + + #[test] + fn test_migration() { + new_test_ext().execute_with(|| { + let input_address_1 = ScriptPubkey::Taproot([0u8; 32]); + let input_address_2 = ScriptPubkey::Taproot([1u8; 32]); + let output_address = ForeignChainAddress::Eth([0u8; 20].into()); + + let refund_params = ChannelRefundParameters { + retry_duration: 40, + refund_address: ForeignChainAddress::Eth([3u8; 20].into()), + min_price: 2.into(), + }; + + let old_details_swap = old::DepositChannelDetails:: { + deposit_channel: mock_deposit_channel(), + opened_at: Default::default(), + expires_at: Default::default(), + boost_status: BoostStatus::NotBoosted, + action: old::ChannelAction::Swap { + destination_asset: Asset::Flip, + destination_address: output_address.clone(), + broker_fees: Default::default(), + refund_params: Some(refund_params.clone()), + }, + boost_fee: 0, + }; + + let old_details_ccm = old::DepositChannelDetails:: { + action: old::ChannelAction::CcmTransfer { + destination_asset: Asset::Flip, + destination_address: output_address.clone(), + channel_metadata: CcmChannelMetadata { + message: vec![0u8, 1u8, 2u8, 3u8, 4u8].try_into().unwrap(), + gas_budget: 50 * 10u128.pow(18), + cf_parameters: Default::default(), + }, + refund_params: Some(refund_params.clone()), + }, + ..old_details_swap.clone() + }; + + old::DepositChannelLookup::::insert( + input_address_1.clone(), + old_details_swap, + ); + + old::DepositChannelLookup::::insert(input_address_2.clone(), old_details_ccm); + + Migration::::on_runtime_upgrade(); + + assert_eq!( + DepositChannelLookup::::get(input_address_1), + Some(DepositChannelDetails:: { + deposit_channel: mock_deposit_channel(), + opened_at: Default::default(), + expires_at: Default::default(), + boost_status: BoostStatus::NotBoosted, + action: ChannelAction::Swap { + destination_asset: Asset::Flip, + destination_address: output_address.clone(), + broker_fees: Default::default(), + refund_params: Some(refund_params.clone()), + dca_params: None, + }, + boost_fee: 0, + }) + ); + assert_eq!( + DepositChannelLookup::::get(input_address_2), + Some(DepositChannelDetails:: { + deposit_channel: mock_deposit_channel(), + opened_at: Default::default(), + expires_at: Default::default(), + boost_status: BoostStatus::NotBoosted, + action: ChannelAction::CcmTransfer { + destination_asset: Asset::Flip, + destination_address: output_address.clone(), + channel_metadata: CcmChannelMetadata { + message: vec![0u8, 1u8, 2u8, 3u8, 4u8].try_into().unwrap(), + gas_budget: 50 * 10u128.pow(18), + cf_parameters: Default::default(), + }, + refund_params: Some(refund_params.clone()), + dca_params: None, + }, + boost_fee: 0, + }) + ); + }); + } +}