diff --git a/node/primitives/src/xcm.rs b/node/primitives/src/xcm.rs index ff0a0dba0f..9dfd8e8b2e 100644 --- a/node/primitives/src/xcm.rs +++ b/node/primitives/src/xcm.rs @@ -40,3 +40,16 @@ impl From for u64 { x.0.into() } } + +/// represent the xcmp transact type +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode)] +pub enum ParachainTransactProxyType { + Primary = 0, + Derived = 1, +} + +#[repr(u16)] +pub enum ParachainDerivedProxyAccountType { + Salp = 0, + Staking = 1, +} diff --git a/pallets/salp/src/lib.rs b/pallets/salp/src/lib.rs index afb2c143a0..ed63bdc88c 100644 --- a/pallets/salp/src/lib.rs +++ b/pallets/salp/src/lib.rs @@ -176,8 +176,11 @@ pub mod pallet { PalletId, }; use frame_system::pallet_prelude::*; - use node_primitives::{BancorHandler, CurrencyId, LeasePeriod, ParaId, TransferOriginType}; - use orml_traits::{currency::TransferAll, MultiCurrency, MultiReservableCurrency}; + use node_primitives::{ + BancorHandler, CurrencyId, LeasePeriod, ParaId, ParachainTransactProxyType, + TransferOriginType, + }; + use orml_traits::{currency::TransferAll, MultiCurrency, MultiReservableCurrency, XcmTransfer}; use polkadot_parachain::primitives::Id as PolkadotParaId; use sp_arithmetic::Percent; use sp_std::{convert::TryInto, prelude::*}; @@ -256,8 +259,13 @@ pub mod pallet { /// Weight information for the extrinsics in this module. type WeightInfo: WeightInfo; + /// Parachain Id type SelfParaId: Get; + /// Weight to Fee calculator + type WeightToFee: WeightToFeePolynomial>; + + /// Xcm weight #[pallet::constant] type BaseXcmWeight: Get; @@ -267,13 +275,21 @@ pub mod pallet { #[pallet::constant] type WithdrawWeight: Get; - type WeightToFee: WeightToFeePolynomial>; - #[pallet::constant] type AddProxyWeight: Get; #[pallet::constant] type RemoveProxyWeight: Get; + + /// The interface to Cross-chain transfer. + type XcmTransfer: XcmTransfer, BalanceOf, CurrencyId>; + + /// The sovereign sub-account for where the staking currencies are sent to. + #[pallet::constant] + type SovereignSubAccountLocation: Get; + + #[pallet::constant] + type TransactType: Get; } #[pallet::pallet] @@ -316,6 +332,8 @@ pub mod pallet { /// Proxy ProxyAdded(AccountIdOf), ProxyRemoved(AccountIdOf), + /// Mint + Minted(AccountIdOf, BalanceOf), } #[pallet::error] @@ -568,7 +586,6 @@ pub mod pallet { Ok(()) } - /// TODO: Refactor the docs. /// Contribute to a crowd sale. This will transfer some balance over to fund a parachain /// slot. It will be withdrawable in two instances: the parachain becomes retired; or the /// slot is unable to be purchased and the timeout expires. @@ -593,9 +610,10 @@ pub mod pallet { let (contributed, status) = Self::contribution(fund.trie_index, &who); ensure!(status == ContributionStatus::Idle, Error::::InvalidContributionStatus); - if is_proxy { - Self::xcm_ump_contribute(origin, index, value) - .map_err(|_e| Error::::XcmFailed)?; + if (!is_proxy && T::XcmTransferOrigin::get() == TransferOriginType::FromRelayChain) || + T::TransactType::get() == ParachainTransactProxyType::Primary + { + T::MultiCurrency::reserve(T::RelayChainToken::get(), &who, value)?; } Self::put_contribution( @@ -605,8 +623,16 @@ pub mod pallet { ContributionStatus::Contributing(value), ); - Self::deposit_event(Event::Contributing(who, index, value)); + Self::deposit_event(Event::Contributing(who.clone(), index, value.clone())); + if !is_proxy { + Self::xcm_ump_contribute(origin, index, value) + .map_err(|_e| Error::::XcmFailed)?; + } else { + if T::TransactType::get() == ParachainTransactProxyType::Derived { + Self::xcm_ump_transfer(who.clone(), value)?; + } + } Ok(()) } @@ -623,7 +649,6 @@ pub mod pallet { is_success: bool, ) -> DispatchResult { T::EnsureConfirmAsMultiSig::ensure_origin(origin)?; - let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; let can_confirm = fund.status == FundStatus::Ongoing || fund.status == FundStatus::Failed || @@ -649,7 +674,9 @@ pub mod pallet { FundInfo { raised: fund.raised.saturating_add(contributing), ..fund }; Funds::::insert(index, Some(fund_new)); - if T::XcmTransferOrigin::get() == TransferOriginType::FromRelayChain { + if T::TransactType::get() == ParachainTransactProxyType::Primary && + T::XcmTransferOrigin::get() == TransferOriginType::FromRelayChain + { T::MultiCurrency::withdraw(T::RelayChainToken::get(), &who, contributing)?; } @@ -697,7 +724,7 @@ pub mod pallet { let can = fund.status == FundStatus::Failed || fund.status == FundStatus::Retired; ensure!(can, Error::::InvalidFundStatus); - if is_proxy { + if !is_proxy { Self::xcm_ump_withdraw(origin, index).map_err(|_| Error::::XcmFailed)?; } @@ -775,7 +802,7 @@ pub mod pallet { Error::::NotEnoughReservedAssetsToRefund ); - if is_proxy { + if !is_proxy { Self::xcm_ump_redeem(origin, index, contributed) .map_err(|_| Error::::XcmFailed)?; } @@ -874,7 +901,7 @@ pub mod pallet { let status = Self::redeem_status(who.clone(), (index, fund.first_slot, fund.last_slot)); ensure!(status == RedeemStatus::Idle, Error::::InvalidRedeemStatus); - if is_proxy { + if !is_proxy { Self::xcm_ump_redeem(origin.clone(), index, value) .map_err(|_| Error::::XcmFailed)?; } @@ -985,7 +1012,6 @@ pub mod pallet { let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; ensure!(fund.status == FundStatus::End, Error::::InvalidFundStatus); - // TODO: Delete element when iter? Fix it? let mut refund_count = 0u32; // Try killing the crowdloan child trie and Assume everyone will be refunded. let contributions = Self::contribution_iterator(fund.trie_index); @@ -1044,6 +1070,23 @@ pub mod pallet { Ok(()) } + + /// transfer to parachain salp account + #[pallet::weight(( + 0, + DispatchClass::Normal, + Pays::No + ))] + #[transactional] + pub fn mint(origin: OriginFor, amount: BalanceOf) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::xcm_ump_transfer(who.clone(), amount.clone())?; + + Self::deposit_event(Event::::Minted(who, amount)); + + Ok(()) + } } #[pallet::hooks] @@ -1260,5 +1303,15 @@ pub mod pallet { false, ) } + + fn xcm_ump_transfer(who: AccountIdOf, amount: BalanceOf) -> DispatchResult { + T::XcmTransfer::transfer( + who.clone(), + T::RelayChainToken::get(), + amount, + T::SovereignSubAccountLocation::get(), + 3 * T::BaseXcmWeight::get(), + ) + } } } diff --git a/pallets/salp/src/mock.rs b/pallets/salp/src/mock.rs index cdeef10fb4..285482a69f 100644 --- a/pallets/salp/src/mock.rs +++ b/pallets/salp/src/mock.rs @@ -25,7 +25,9 @@ use frame_support::{ PalletId, }; use frame_system::RawOrigin; -use node_primitives::{Amount, Balance, CurrencyId, TokenSymbol, TransferOriginType}; +use node_primitives::{ + Amount, Balance, CurrencyId, ParachainTransactProxyType, TokenSymbol, TransferOriginType, +}; use sp_arithmetic::Percent; use sp_core::H256; use sp_runtime::{ @@ -183,6 +185,13 @@ impl bifrost_bancor::Config for Test { type WeightInfo = (); } +pub fn create_x2_parachain_multilocation(_index: u16) -> MultiLocation { + MultiLocation::X2( + Junction::Parent, + Junction::AccountId32 { network: NetworkId::Any, id: ALICE.into() }, + ) +} + parameter_types! { pub const SubmissionDeposit: u32 = 1; pub const MinContribution: Balance = 10; @@ -207,6 +216,8 @@ parameter_types! { BRUCE, CATHI ],2); + pub RelaychainSovereignSubAccount: MultiLocation = create_x2_parachain_multilocation(0 as u16); + pub SalpTransactType: ParachainTransactProxyType = ParachainTransactProxyType::Derived; } parameter_types! { @@ -237,8 +248,12 @@ impl EnsureOrigin for EnsureConfirmAsMultiSig { } } +use frame_support::dispatch::DispatchResult; +use orml_traits::XcmTransfer; use smallvec::smallvec; pub use sp_runtime::Perbill; +use xcm::{opaque::v0::MultiAsset, v0::Junction}; + pub struct WeightToFee; impl WeightToFeePolynomial for WeightToFee { type Balance = Balance; @@ -252,6 +267,29 @@ impl WeightToFeePolynomial for WeightToFee { } } +pub struct MockXTokens; + +impl XcmTransfer for MockXTokens { + fn transfer( + _who: AccountId, + _currency_id: CurrencyId, + _amount: Balance, + _dest: MultiLocation, + _dest_weight: Weight, + ) -> DispatchResult { + Ok(()) + } + + fn transfer_multi_asset( + _who: AccountId, + _asset: MultiAsset, + _dest: MultiLocation, + _dest_weight: Weight, + ) -> DispatchResult { + Ok(()) + } +} + impl salp::Config for Test { type BancorPool = Bancor; type BifrostXcmExecutor = MockXcmExecutor; @@ -279,6 +317,9 @@ impl salp::Config for Test { type WeightToFee = WeightToFee; type AddProxyWeight = AddProxyWeight; type RemoveProxyWeight = RemoveProxyWeight; + type XcmTransfer = MockXTokens; + type SovereignSubAccountLocation = RelaychainSovereignSubAccount; + type TransactType = SalpTransactType; } pub struct SalpWeightInfo; diff --git a/runtime/asgard/src/lib.rs b/runtime/asgard/src/lib.rs index 213845e4ff..74d833b636 100644 --- a/runtime/asgard/src/lib.rs +++ b/runtime/asgard/src/lib.rs @@ -84,7 +84,8 @@ use constants::{currency::*, time::*}; use cumulus_primitives_core::ParaId as CumulusParaId; use frame_support::traits::{EnsureOrigin, OnRuntimeUpgrade}; use node_primitives::{ - Amount, CurrencyId, Moment, Nonce, TokenSymbol, TransferOriginType, XcmBaseWeight, + Amount, CurrencyId, Moment, Nonce, ParachainDerivedProxyAccountType, + ParachainTransactProxyType, TokenSymbol, TransferOriginType, XcmBaseWeight, }; // orml imports use orml_currencies::BasicCurrencyAdapter; @@ -93,7 +94,9 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use sp_runtime::traits::ConvertInto; use static_assertions::const_assert; -use xcm::v0::{BodyId, Junction::*, MultiAsset, MultiLocation, MultiLocation::*, NetworkId, Xcm}; +use xcm::v0::{ + BodyId, Junction, Junction::*, MultiAsset, MultiLocation, MultiLocation::*, NetworkId, Xcm, +}; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, ParentAsSuperuser, @@ -958,6 +961,38 @@ impl bifrost_minter_reward::Config for Runtime { type WeightInfo = weights::bifrost_minter_reward::WeightInfo; } +pub fn create_x2_parachain_multilocation(index: u16) -> MultiLocation { + MultiLocation::X2( + Junction::Parent, + Junction::AccountId32 { + network: NetworkId::Any, + id: Utility::derivative_account_id(ParachainInfo::get().into_account(), index).into(), + }, + ) +} + +pub struct EnsureConfirmAsMultiSig; +impl EnsureOrigin for EnsureConfirmAsMultiSig { + type Success = AccountId; + + fn try_origin(o: Origin) -> Result { + Into::, Origin>>::into(o).and_then(|o| match o { + RawOrigin::Signed(who) => + if who == ConfirmMuitiSigAccount::get() { + Ok(who) + } else { + Err(Origin::from(Some(who))) + }, + r => Err(Origin::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> Origin { + Origin::from(RawOrigin::Signed(Default::default())) + } +} + parameter_types! { pub const SubmissionDeposit: Balance = 100 * DOLLARS; pub const MinContribution: Balance = 1 * DOLLARS; @@ -981,28 +1016,8 @@ parameter_types! { hex!["eee4ed9bb0a1a72aa966a1a21c403835b5edac59de296be19bd8b2ad31d03f3b"].into(), hex!["ce6072037670ca8e974fd571eae4f215a58d0bf823b998f619c3f87a911c3541"].into(),//5GjJNWYS6f2UQ9aiLexuB8qgjG8fRs2Ax4nHin1z1engpnNt ],3); -} - -pub struct EnsureConfirmAsMultiSig; -impl EnsureOrigin for EnsureConfirmAsMultiSig { - type Success = AccountId; - - fn try_origin(o: Origin) -> Result { - Into::, Origin>>::into(o).and_then(|o| match o { - RawOrigin::Signed(who) => - if who == ConfirmMuitiSigAccount::get() { - Ok(who) - } else { - Err(Origin::from(Some(who))) - }, - r => Err(Origin::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> Origin { - Origin::from(RawOrigin::Signed(Default::default())) - } + pub RelaychainSovereignSubAccount: MultiLocation = create_x2_parachain_multilocation(ParachainDerivedProxyAccountType::Salp as u16); + pub SalpTransactType: ParachainTransactProxyType = ParachainTransactProxyType::Derived; } impl bifrost_salp::Config for Runtime { @@ -1033,6 +1048,9 @@ impl bifrost_salp::Config for Runtime { type WeightToFee = WeightToFee; type AddProxyWeight = AddProxyWeight; type RemoveProxyWeight = RemoveProxyWeight; + type XcmTransfer = XTokens; + type SovereignSubAccountLocation = RelaychainSovereignSubAccount; + type TransactType = SalpTransactType; } parameter_types! { diff --git a/runtime/dev/src/lib.rs b/runtime/dev/src/lib.rs index df066117d5..969f582ed9 100644 --- a/runtime/dev/src/lib.rs +++ b/runtime/dev/src/lib.rs @@ -92,7 +92,8 @@ use frame_support::{ traits::{EnsureOrigin, KeyOwnerProofSystem}, }; use node_primitives::{ - Amount, CurrencyId, Moment, Nonce, TokenSymbol, TransferOriginType, XcmBaseWeight, + Amount, CurrencyId, Moment, Nonce, ParachainDerivedProxyAccountType, + ParachainTransactProxyType, TokenSymbol, TransferOriginType, XcmBaseWeight, }; // orml imports use orml_currencies::BasicCurrencyAdapter; @@ -101,7 +102,9 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use sp_runtime::traits::ConvertInto; use static_assertions::const_assert; -use xcm::v0::{BodyId, Junction::*, MultiAsset, MultiLocation, MultiLocation::*, NetworkId, Xcm}; +use xcm::v0::{ + BodyId, Junction, Junction::*, MultiAsset, MultiLocation, MultiLocation::*, NetworkId, Xcm, +}; use xcm_builder::{ AccountId32Aliases, AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, IsConcrete, LocationInverter, ParentAsSuperuser, @@ -953,6 +956,18 @@ parameter_types! { "ce6072037670ca8e974fd571eae4f215a58d0bf823b998f619c3f87a911c3541" ] .into(); + pub RelaychainSovereignSubAccount: MultiLocation = create_x2_parachain_multilocation(ParachainDerivedProxyAccountType::Salp as u16); + pub SalpTransactType: ParachainTransactProxyType = ParachainTransactProxyType::Derived; +} + +pub fn create_x2_parachain_multilocation(index: u16) -> MultiLocation { + MultiLocation::X2( + Junction::Parent, + Junction::AccountId32 { + network: NetworkId::Any, + id: Utility::derivative_account_id(ParachainInfo::get().into_account(), index).into(), + }, + ) } pub struct EnsureConfirmAsMultiSig; @@ -1004,6 +1019,9 @@ impl bifrost_salp::Config for Runtime { type WeightToFee = WeightToFee; type AddProxyWeight = AddProxyWeight; type RemoveProxyWeight = RemoveProxyWeight; + type XcmTransfer = XTokens; + type SovereignSubAccountLocation = RelaychainSovereignSubAccount; + type TransactType = SalpTransactType; } parameter_types! {