diff --git a/Cargo.lock b/Cargo.lock index 6db19bedb..bb590ba92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1043,7 +1043,6 @@ name = "bifrost-salp" version = "0.8.0" dependencies = [ "bifrost-bancor", - "bifrost-kusama-runtime", "bifrost-runtime-common", "frame-benchmarking", "frame-support", @@ -1054,6 +1053,7 @@ dependencies = [ "orml-tokens 0.4.1-dev (git+https://github.com/open-web3-stack/open-runtime-module-library?rev=17a791edf431d7d7aee1ea3dfaeeb7bc21944301)", "orml-traits 0.4.1-dev (git+https://github.com/open-web3-stack/open-runtime-module-library?rev=17a791edf431d7d7aee1ea3dfaeeb7bc21944301)", "pallet-balances", + "pallet-collective", "pallet-multisig", "pallet-xcm", "parity-scale-codec", diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index 44c815808..071e78870 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -97,9 +97,7 @@ bifrost-liquidity-mining-rpc-runtime-api = { path = "../../pallets/liquidity-min default = [ "std" ] std = [] runtime-benchmarks = [ - "asgard-runtime/runtime-benchmarks", "bifrost-kusama-runtime/runtime-benchmarks", - "bifrost-polkadot-runtime/runtime-benchmarks", ] with-asgard-runtime = [ "asgard-runtime", diff --git a/pallets/salp-lite/src/benchmarking.rs b/pallets/salp-lite/src/benchmarking.rs index 2f2e5bacf..9154d79d9 100644 --- a/pallets/salp-lite/src/benchmarking.rs +++ b/pallets/salp-lite/src/benchmarking.rs @@ -49,11 +49,11 @@ fn create_fund(id: u32) -> ParaId { #[allow(dead_code)] fn contribute_fund(who: &T::AccountId, index: ParaId) { let value = T::MinContribution::get(); - assert_ok!(Salp::::issue(RawOrigin::Root.into(), who.clone(), index, value, [0; 32])); + let confirmer: T::Origin = RawOrigin::Signed(Salp::::multisig_confirm_account()).into(); + assert_ok!(Salp::::issue(confirmer, who.clone(), index, value, [0; 32])); } benchmarks! { - redeem { let fund_index = create_fund::(1); let caller: T::AccountId = whitelisted_caller(); @@ -70,22 +70,19 @@ benchmarks! { assert_last_event::(Event::::Redeemed(caller.clone(), fund_index, (0 as u32).into(),(7 as u32).into(),contribution).into()) } - batch_migrate { - let k in 1 .. T::BatchKeysLimit::get(); + refund { let fund_index = create_fund::(1); + let caller: T::AccountId = whitelisted_caller(); + let caller_origin: T::Origin = RawOrigin::Signed(caller.clone()).into(); let contribution = T::MinContribution::get(); - let mut caller: T::AccountId = whitelisted_caller(); - for i in 0 .. k { - caller = account("contributor", i, 0); - contribute_fund::(&caller,fund_index); - } + contribute_fund::(&caller,fund_index); assert_ok!(Salp::::fund_fail(RawOrigin::Root.into(), fund_index)); - }: _(RawOrigin::Signed(caller.clone()), fund_index) + assert_ok!(Salp::::withdraw(RawOrigin::Root.into(), fund_index)); + assert_eq!(Salp::::redeem_pool(), T::MinContribution::get()); + }: _(RawOrigin::Signed(caller.clone()), fund_index,(0 as u32).into(),(7 as u32).into(),contribution) verify { - let fund = Salp::::funds(fund_index).unwrap(); - let (_, status) = Salp::::contribution(fund.trie_index, &caller); - assert_eq!(status, ContributionStatus::Unlocked); - assert_last_event::(Event::::AllUnlocked(fund_index).into()); + assert_eq!(Salp::::redeem_pool(), 0_u32.saturated_into()); + assert_last_event::(Event::::Refunded(caller.clone(), fund_index, (0 as u32).into(),(7 as u32).into(),contribution).into()) } } diff --git a/pallets/salp/Cargo.toml b/pallets/salp/Cargo.toml index 3369359bf..79337ed82 100644 --- a/pallets/salp/Cargo.toml +++ b/pallets/salp/Cargo.toml @@ -31,9 +31,9 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkad orml-tokens = "0.4.1-dev" orml-currencies = "0.4.1-dev" pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13" } +pallet-collective = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13" } bifrost-bancor = { path = "../../pallets/bancor" } bifrost-runtime-common = { path = "../../runtime/common" } -bifrost-kusama-runtime = { path = "../../runtime/bifrost-kusama" } [features] default = ["std"] @@ -57,4 +57,7 @@ runtime-benchmarks = [ "frame-benchmarking", "sp-runtime/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-collective/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", ] diff --git a/pallets/salp/src/benchmarking.rs b/pallets/salp/src/benchmarking.rs index 53a613b31..dd0fa5bb8 100644 --- a/pallets/salp/src/benchmarking.rs +++ b/pallets/salp/src/benchmarking.rs @@ -72,8 +72,9 @@ benchmarks! { let caller_origin: T::Origin = RawOrigin::Signed(caller.clone()).into(); let contribution = T::MinContribution::get(); contribute_fund::(&caller,fund_index); + let confirmer: T::Origin = RawOrigin::Signed(Salp::::multisig_confirm_account()).into(); assert_ok!(Salp::::confirm_contribute( - RawOrigin::Root.into(), + confirmer, caller.clone(), fund_index, true, @@ -84,11 +85,11 @@ benchmarks! { let fund = Salp::::funds(fund_index).unwrap(); let (_, status) = Salp::::contribution(fund.trie_index, &caller); assert_eq!(status, ContributionStatus::Idle); - }: _(RawOrigin::Signed(caller.clone()), fund_index) + }: _(RawOrigin::Signed(caller.clone()), fund_index,(0 as u32).into(),(7 as u32).into(),contribution) verify { let (_, status) = Salp::::contribution(fund.trie_index, &caller); - assert_eq!(status, ContributionStatus::Refunded); - assert_last_event::(Event::::Refunded(caller.clone(), fund_index, contribution).into()) + assert_eq!(status, ContributionStatus::Idle); + assert_last_event::(Event::::Refunded(caller.clone(), fund_index, (0 as u32).into(),(7 as u32).into(),contribution).into()) } unlock { @@ -97,8 +98,9 @@ benchmarks! { let caller_origin: T::Origin = RawOrigin::Signed(caller.clone()).into(); let contribution = T::MinContribution::get(); contribute_fund::(&caller,fund_index); + let confirmer: T::Origin = RawOrigin::Signed(Salp::::multisig_confirm_account()).into(); assert_ok!(Salp::::confirm_contribute( - RawOrigin::Root.into(), + confirmer, caller.clone(), fund_index, true, @@ -117,11 +119,12 @@ benchmarks! { let fund_index = create_fund::(1); let contribution = T::MinContribution::get(); let mut caller: T::AccountId = whitelisted_caller(); + let confirmer: T::Origin = RawOrigin::Signed(Salp::::multisig_confirm_account()).into(); for i in 0 .. k { caller = account("contributor", i, 0); contribute_fund::(&caller,fund_index); let _ = Salp::::confirm_contribute( - RawOrigin::Root.into(), + confirmer.clone(), caller.clone(), fund_index, true, @@ -143,8 +146,9 @@ benchmarks! { let caller_origin: T::Origin = RawOrigin::Signed(caller.clone()).into(); let contribution = T::MinContribution::get(); contribute_fund::(&caller,fund_index); + let confirmer: T::Origin = RawOrigin::Signed(Salp::::multisig_confirm_account()).into(); assert_ok!(Salp::::confirm_contribute( - RawOrigin::Root.into(), + confirmer, caller.clone(), fund_index, true, diff --git a/pallets/salp/src/lib.rs b/pallets/salp/src/lib.rs index e332d5b2e..75e58dfff 100644 --- a/pallets/salp/src/lib.rs +++ b/pallets/salp/src/lib.rs @@ -71,6 +71,7 @@ pub enum FundStatus { Failed, RefundWithdrew, RedeemWithdrew, + FailedToContinue, End, } @@ -238,7 +239,7 @@ pub mod pallet { /// Withdrew full balance of a contributor. [who, fund_index, amount] Withdrew(ParaId, BalanceOf), /// refund to account. [who, fund_index,value] - Refunded(AccountIdOf, ParaId, BalanceOf), + Refunded(AccountIdOf, ParaId, LeasePeriod, LeasePeriod, BalanceOf), /// all refund AllRefunded(ParaId), /// redeem to account. [who, fund_index, first_slot, last_slot, value] @@ -255,6 +256,8 @@ pub mod pallet { Success(ParaId), Retired(ParaId), End(ParaId), + Continued(ParaId, LeasePeriod, LeasePeriod), + RefundedDissolved(ParaId, LeasePeriod, LeasePeriod), /// Proxy ProxyAdded(AccountIdOf), ProxyRemoved(AccountIdOf), @@ -305,6 +308,11 @@ pub mod pallet { NotEnoughBalanceInRedeemPool, ConvertFailure, XcmExecutionFailed, + NotEnoughBalanceInFund, + InvalidFundSameSlot, + InvalidFundNotExist, + InvalidRefund, + NotEnoughBalanceToContribute, } /// Multisig confirm account @@ -340,6 +348,19 @@ pub mod pallet { #[pallet::getter(fn redeem_pool)] pub(super) type RedeemPool = StorageValue<_, BalanceOf, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn failed_funds_to_refund)] + pub(super) type FailedFundsToRefund = StorageNMap< + _, + ( + NMapKey, + NMapKey, + NMapKey, + ), + Option, LeasePeriod>>, + ValueQuery, + >; + #[pallet::genesis_config] pub struct GenesisConfig { pub initial_multisig_account: Option>, @@ -419,6 +440,44 @@ pub mod pallet { Ok(()) } + #[pallet::weight(( + 0, + DispatchClass::Normal, + Pays::No + ))] + pub fn continue_fund( + origin: OriginFor, + #[pallet::compact] index: ParaId, + #[pallet::compact] first_slot: LeasePeriod, + #[pallet::compact] last_slot: LeasePeriod, + ) -> DispatchResult { + T::EnsureConfirmAsGovernance::ensure_origin(origin)?; + + // crownload is failed, so enable the withdrawal function of vsToken/vsBond + let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; + ensure!(fund.status == FundStatus::RefundWithdrew, Error::::InvalidFundStatus); + ensure!( + fund.first_slot != first_slot || fund.last_slot != last_slot, + Error::::InvalidFundSameSlot + ); + + let fund_old = FundInfo { status: FundStatus::FailedToContinue, ..fund }; + FailedFundsToRefund::::insert( + (index, fund.first_slot, fund.last_slot), + Some(fund_old.clone()), + ); + let fund_new = FundInfo { status: FundStatus::Ongoing, first_slot, last_slot, ..fund }; + Funds::::insert(index, Some(fund_new)); + + Self::deposit_event(Event::::Continued( + index, + fund_old.first_slot, + fund_old.last_slot, + )); + + Ok(()) + } + #[pallet::weight(( 0, DispatchClass::Normal, @@ -462,161 +521,84 @@ pub mod pallet { Ok(()) } - /// Edit the configuration for an in-progress crowdloan. - /// - /// Can only be called by Root origin. + /// Create a new crowdloaning campaign for a parachain slot deposit for the current auction. #[pallet::weight(( 0, DispatchClass::Normal, Pays::No ))] - pub fn edit( + pub fn create( origin: OriginFor, #[pallet::compact] index: ParaId, #[pallet::compact] cap: BalanceOf, - #[pallet::compact] raised: BalanceOf, #[pallet::compact] first_slot: LeasePeriod, #[pallet::compact] last_slot: LeasePeriod, - fund_status: Option, ) -> DispatchResult { T::EnsureConfirmAsGovernance::ensure_origin(origin)?; - let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; + ensure!(!Funds::::contains_key(index), Error::::FundAlreadyCreated); - let status = match fund_status { - None => fund.status, - Some(status) => status, - }; + ensure!(first_slot <= last_slot, Error::::LastSlotBeforeFirstSlot); + + let last_slot_limit = first_slot + .checked_add(((T::SlotLength::get() as u32) - 1).into()) + .ok_or(Error::::FirstSlotTooFarInFuture)?; + ensure!(last_slot <= last_slot_limit, Error::::LastSlotTooFarInFuture); Funds::::insert( index, Some(FundInfo { + raised: Zero::zero(), cap, first_slot, last_slot, - status, - raised, - trie_index: fund.trie_index, + trie_index: Self::next_trie_index()?, + status: FundStatus::Ongoing, }), ); - Self::deposit_event(Event::::Edited(index)); - Ok(()) - } - - /// Unlock the reserved vsToken/vsBond after fund success - #[pallet::weight(T::WeightInfo::unlock())] - #[transactional] - pub fn unlock( - _origin: OriginFor, - who: AccountIdOf, - #[pallet::compact] index: ParaId, - ) -> DispatchResult { - let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; - - let (contributed, _) = Self::contribution(fund.trie_index, &who); - - #[allow(non_snake_case)] - let (vsToken, vsBond) = Self::vsAssets(index, fund.first_slot, fund.last_slot); - - T::MultiCurrency::unreserve(vsToken, &who, contributed); - T::MultiCurrency::unreserve(vsBond, &who, contributed); - - Self::put_contribution( - fund.trie_index, - &who, - contributed, - ContributionStatus::Unlocked, - ); - - Self::deposit_event(Event::::Unlocked(who, index, contributed)); - - Ok(()) - } - - /// Unlock the reserved vsToken/vsBond after fund success - #[pallet::weight(T::WeightInfo::batch_unlock(T::RemoveKeysLimit::get()))] - #[transactional] - pub fn batch_unlock( - origin: OriginFor, - #[pallet::compact] index: ParaId, - ) -> DispatchResult { - ensure_signed(origin)?; - - let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; - - let mut unlock_count = 0u32; - let contributions = Self::contribution_iterator(fund.trie_index); - // Assume everyone will be refunded. - let mut all_unlocked = true; - - for (who, (contributed, status)) in contributions { - if unlock_count >= T::RemoveKeysLimit::get() { - // Not everyone was able to be refunded this time around. - all_unlocked = false; - break; - } - if status != ContributionStatus::Unlocked { - #[allow(non_snake_case)] - let (vsToken, vsBond) = Self::vsAssets(index, fund.first_slot, fund.last_slot); - T::MultiCurrency::unreserve(vsToken, &who, contributed); - T::MultiCurrency::unreserve(vsBond, &who, contributed); - - Self::put_contribution( - fund.trie_index, - &who, - contributed, - ContributionStatus::Unlocked, - ); - unlock_count += 1; - } - } - - if all_unlocked { - Self::deposit_event(Event::::AllUnlocked(index)); - } + Self::deposit_event(Event::::Created(index)); Ok(()) } - /// Create a new crowdloaning campaign for a parachain slot deposit for the current auction. + /// Edit the configuration for an in-progress crowdloan. + /// + /// Can only be called by Root origin. #[pallet::weight(( - 0, - DispatchClass::Normal, - Pays::No - ))] - pub fn create( + 0, + DispatchClass::Normal, + Pays::No + ))] + pub fn edit( origin: OriginFor, #[pallet::compact] index: ParaId, #[pallet::compact] cap: BalanceOf, + #[pallet::compact] raised: BalanceOf, #[pallet::compact] first_slot: LeasePeriod, #[pallet::compact] last_slot: LeasePeriod, + fund_status: Option, ) -> DispatchResult { T::EnsureConfirmAsGovernance::ensure_origin(origin)?; - ensure!(!Funds::::contains_key(index), Error::::FundAlreadyCreated); - - ensure!(first_slot <= last_slot, Error::::LastSlotBeforeFirstSlot); - - let last_slot_limit = first_slot - .checked_add(((T::SlotLength::get() as u32) - 1).into()) - .ok_or(Error::::FirstSlotTooFarInFuture)?; - ensure!(last_slot <= last_slot_limit, Error::::LastSlotTooFarInFuture); + let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; + let status = match fund_status { + None => fund.status, + Some(status) => status, + }; Funds::::insert( index, Some(FundInfo { - raised: Zero::zero(), cap, first_slot, last_slot, - trie_index: Self::next_trie_index()?, - status: FundStatus::Ongoing, + status, + raised, + trie_index: fund.trie_index, }), ); - - Self::deposit_event(Event::::Created(index)); - + Self::deposit_event(Event::::Edited(index)); Ok(()) } @@ -649,9 +631,12 @@ pub mod pallet { Error::::InvalidContributionStatus ); - if T::TransactType::get() == ParachainTransactType::Xcm { - T::MultiCurrency::reserve(T::RelayChainToken::get(), &who, value)?; - } + ensure!( + T::MultiCurrency::can_reserve(T::RelayChainToken::get(), &who, value), + Error::::NotEnoughBalanceToContribute + ); + + T::MultiCurrency::reserve(T::RelayChainToken::get(), &who, value)?; Self::put_contribution( fund.trie_index, @@ -661,17 +646,10 @@ pub mod pallet { ); let nonce = Self::next_nonce_index(index)?; - let message_id: MessageId; - if T::TransactType::get() == ParachainTransactType::Xcm { - message_id = Self::xcm_ump_contribute(origin, index, value, nonce) - .map_err(|_e| Error::::XcmFailed)?; - } else { - message_id = sp_io::hashing::blake2_256(&nonce.encode()); - if T::TransactProxyType::get() == ParachainTransactProxyType::Derived { - Self::xcm_ump_transfer(who.clone(), value)?; - } - } + let message_id = Self::xcm_ump_contribute(origin, index, value, nonce) + .map_err(|_e| Error::::XcmFailed)?; + Self::deposit_event(Event::Contributing(who.clone(), index, value.clone(), message_id)); Ok(()) } @@ -710,24 +688,20 @@ pub mod pallet { if is_success { // Issue reserved vsToken/vsBond to contributor T::MultiCurrency::deposit(vsToken, &who, contributing)?; - T::MultiCurrency::reserve(vsToken, &who, contributing)?; T::MultiCurrency::deposit(vsBond, &who, contributing)?; - T::MultiCurrency::reserve(vsBond, &who, contributing)?; // Update the raised of fund let fund_new = FundInfo { raised: fund.raised.saturating_add(contributing), ..fund }; Funds::::insert(index, Some(fund_new)); - if T::TransactType::get() == ParachainTransactType::Xcm { - T::MultiCurrency::unreserve(T::RelayChainToken::get(), &who, contributing); - T::MultiCurrency::transfer( - T::RelayChainToken::get(), - &who, - &Self::fund_account_id(index), - contributing, - )?; - } + T::MultiCurrency::unreserve(T::RelayChainToken::get(), &who, contributing); + T::MultiCurrency::transfer( + T::RelayChainToken::get(), + &who, + &Self::fund_account_id(index), + contributing, + )?; // Update the contribution of who let contributed_new = contributed.saturating_add(contributing); @@ -746,15 +720,88 @@ pub mod pallet { contributed, ContributionStatus::Idle, ); - if T::TransactType::get() == ParachainTransactType::Xcm { - T::MultiCurrency::unreserve(T::RelayChainToken::get(), &who, contributing); - } + T::MultiCurrency::unreserve(T::RelayChainToken::get(), &who, contributing); Self::deposit_event(Event::ContributeFailed(who, index, contributing, message_id)); } Ok(()) } + /// Unlock the reserved vsToken/vsBond after fund success + #[pallet::weight(T::WeightInfo::unlock())] + #[transactional] + pub fn unlock( + _origin: OriginFor, + who: AccountIdOf, + #[pallet::compact] index: ParaId, + ) -> DispatchResult { + let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; + + let (contributed, _) = Self::contribution(fund.trie_index, &who); + + #[allow(non_snake_case)] + let (vsToken, vsBond) = Self::vsAssets(index, fund.first_slot, fund.last_slot); + + T::MultiCurrency::unreserve(vsToken, &who, contributed); + T::MultiCurrency::unreserve(vsBond, &who, contributed); + + Self::put_contribution( + fund.trie_index, + &who, + contributed, + ContributionStatus::Unlocked, + ); + + Self::deposit_event(Event::::Unlocked(who, index, contributed)); + + Ok(()) + } + + /// Unlock the reserved vsToken/vsBond after fund success + #[pallet::weight(T::WeightInfo::batch_unlock(T::RemoveKeysLimit::get()))] + #[transactional] + pub fn batch_unlock( + origin: OriginFor, + #[pallet::compact] index: ParaId, + ) -> DispatchResult { + ensure_signed(origin)?; + + let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; + + let mut unlock_count = 0u32; + let contributions = Self::contribution_iterator(fund.trie_index); + // Assume everyone will be refunded. + let mut all_unlocked = true; + + for (who, (contributed, status)) in contributions { + if unlock_count >= T::RemoveKeysLimit::get() { + // Not everyone was able to be refunded this time around. + all_unlocked = false; + break; + } + if status != ContributionStatus::Unlocked { + #[allow(non_snake_case)] + let (vsToken, vsBond) = Self::vsAssets(index, fund.first_slot, fund.last_slot); + T::MultiCurrency::unreserve(vsToken, &who, contributed); + T::MultiCurrency::unreserve(vsBond, &who, contributed); + + Self::put_contribution( + fund.trie_index, + &who, + contributed, + ContributionStatus::Unlocked, + ); + unlock_count += 1; + } + } + + if all_unlocked { + Self::deposit_event(Event::::AllUnlocked(index)); + } + + Ok(()) + } + /// Withdraw full balance of the parachain. /// - `index`: The parachain to whose crowdloan the contribution was made. #[pallet::weight(( @@ -775,11 +822,11 @@ pub mod pallet { if fund.status == FundStatus::Retired { let fund_new = FundInfo { status: FundStatus::RedeemWithdrew, ..fund }; Funds::::insert(index, Some(fund_new)); - RedeemPool::::set(Self::redeem_pool().saturating_add(amount_withdrew)); } else if fund.status == FundStatus::Failed { let fund_new = FundInfo { status: FundStatus::RefundWithdrew, ..fund }; Funds::::insert(index, Some(fund_new)); } + RedeemPool::::set(Self::redeem_pool().saturating_add(amount_withdrew)); Self::deposit_event(Event::Withdrew(index, amount_withdrew)); @@ -788,89 +835,65 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::refund())] #[transactional] - pub fn refund(origin: OriginFor, #[pallet::compact] index: ParaId) -> DispatchResult { + pub fn refund( + origin: OriginFor, + #[pallet::compact] index: ParaId, + #[pallet::compact] first_slot: LeasePeriod, + #[pallet::compact] last_slot: LeasePeriod, + #[pallet::compact] value: BalanceOf, + ) -> DispatchResult { let who = ensure_signed(origin.clone())?; - let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; - let (contributed, status) = Self::contribution(fund.trie_index, &who); - ensure!(contributed > Zero::zero(), Error::::ZeroContribution); - ensure!(status == ContributionStatus::Idle, Error::::InvalidContributionStatus); - - ensure!(fund.raised >= contributed, Error::::NotEnoughBalanceInRefundPool); + let mut fund = Self::find_fund(index, first_slot, last_slot) + .map_err(|_| Error::::InvalidFundNotExist)?; + ensure!( + fund.status == FundStatus::FailedToContinue || + fund.status == FundStatus::RefundWithdrew, + Error::::InvalidRefund + ); + ensure!( + fund.first_slot == first_slot && fund.last_slot == last_slot, + Error::::InvalidRefund + ); + ensure!(fund.raised >= value, Error::::NotEnoughBalanceInFund); + ensure!(Self::redeem_pool() >= value, Error::::NotEnoughBalanceInRefundPool); #[allow(non_snake_case)] let (vsToken, vsBond) = Self::vsAssets(index, fund.first_slot, fund.last_slot); + T::MultiCurrency::ensure_can_withdraw(vsToken, &who, value) + .map_err(|_e| Error::::NotEnoughFreeAssetsToRedeem)?; + T::MultiCurrency::ensure_can_withdraw(vsBond, &who, value) + .map_err(|_e| Error::::NotEnoughFreeAssetsToRedeem)?; - fund.raised = fund.raised.saturating_sub(contributed); + T::MultiCurrency::withdraw(vsToken, &who, value)?; + T::MultiCurrency::withdraw(vsBond, &who, value)?; - Funds::::insert(index, Some(fund.clone())); - - T::MultiCurrency::slash_reserved(vsToken, &who, contributed); - T::MultiCurrency::slash_reserved(vsBond, &who, contributed); - - if T::TransactType::get() == ParachainTransactType::Xcm { - T::MultiCurrency::transfer( - T::RelayChainToken::get(), - &Self::fund_account_id(index), - &who, - contributed, - )?; - } - Self::kill_contribution(fund.trie_index, &who); - - Self::deposit_event(Event::Refunded(who, index, contributed)); - - Ok(()) - } - - #[pallet::weight(T::WeightInfo::refund())] - #[transactional] - pub fn batch_refund( - origin: OriginFor, - #[pallet::compact] index: ParaId, - ) -> DispatchResult { - ensure_signed(origin)?; - - let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; - ensure!(fund.status == FundStatus::RefundWithdrew, Error::::InvalidFundStatus); - - let mut refund_count = 0u32; - let contributions = Self::contribution_iterator(fund.trie_index); - // Assume everyone will be refunded. - let mut all_refunded = true; - - for (who, (contributed, status)) in contributions { - if refund_count >= T::RemoveKeysLimit::get() { - // Not everyone was able to be refunded this time around. - all_refunded = false; - break; - } - if status == ContributionStatus::Idle { - #[allow(non_snake_case)] - let (vsToken, vsBond) = Self::vsAssets(index, fund.first_slot, fund.last_slot); - fund.raised = fund.raised.saturating_sub(contributed); - - T::MultiCurrency::slash_reserved(vsToken, &who, contributed); - T::MultiCurrency::slash_reserved(vsBond, &who, contributed); - - if T::TransactType::get() == ParachainTransactType::Xcm { - T::MultiCurrency::transfer( - T::RelayChainToken::get(), - &Self::fund_account_id(index), - &who, - contributed, - )?; - } - Self::kill_contribution(fund.trie_index, &who); - refund_count += 1; - } + RedeemPool::::set(Self::redeem_pool().saturating_sub(value)); + let mut fund_new = Self::funds(index).ok_or(Error::::InvalidParaId)?; + fund_new.raised = fund_new.raised.saturating_sub(value); + Funds::::insert(index, Some(fund_new)); + if fund.status == FundStatus::FailedToContinue { + fund.raised = fund.raised.saturating_sub(value); + FailedFundsToRefund::::insert( + (index, first_slot, last_slot), + Some(fund.clone()), + ); } - Funds::::insert(index, Some(fund)); + T::MultiCurrency::transfer( + T::RelayChainToken::get(), + &Self::fund_account_id(index), + &who, + value, + )?; - if all_refunded { - Self::deposit_event(Event::::AllRefunded(index)); - } + Self::deposit_event(Event::Refunded( + who, + index, + fund.first_slot, + fund.last_slot, + value, + )); Ok(()) } @@ -885,54 +908,33 @@ pub mod pallet { let who = ensure_signed(origin.clone())?; let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?; - ensure!( - fund.status == FundStatus::RefundWithdrew || - fund.status == FundStatus::RedeemWithdrew, - Error::::InvalidFundStatus - ); + ensure!(fund.status == FundStatus::RedeemWithdrew, Error::::InvalidFundStatus); ensure!(fund.raised >= value, Error::::NotEnoughBalanceInRedeemPool); - let (contributed, _) = Self::contribution(fund.trie_index, &who); #[allow(non_snake_case)] let (vsToken, vsBond) = Self::vsAssets(index, fund.first_slot, fund.last_slot); - if fund.status == FundStatus::RedeemWithdrew { - ensure!(Self::redeem_pool() >= value, Error::::NotEnoughBalanceInRedeemPool); - let cur_block = >::block_number(); - ensure!(!Self::is_expired(cur_block, fund.last_slot), Error::::VSBondExpired); - T::MultiCurrency::ensure_can_withdraw(vsToken, &who, value) - .map_err(|_e| Error::::NotEnoughFreeAssetsToRedeem)?; - T::MultiCurrency::ensure_can_withdraw(vsBond, &who, value) - .map_err(|_e| Error::::NotEnoughFreeAssetsToRedeem)?; - } + ensure!(Self::redeem_pool() >= value, Error::::NotEnoughBalanceInRedeemPool); + let cur_block = >::block_number(); + ensure!(!Self::is_expired(cur_block, fund.last_slot), Error::::VSBondExpired); + T::MultiCurrency::ensure_can_withdraw(vsToken, &who, value) + .map_err(|_e| Error::::NotEnoughFreeAssetsToRedeem)?; + T::MultiCurrency::ensure_can_withdraw(vsBond, &who, value) + .map_err(|_e| Error::::NotEnoughFreeAssetsToRedeem)?; - if fund.status == FundStatus::RedeemWithdrew { - T::MultiCurrency::withdraw(vsToken, &who, value)?; - T::MultiCurrency::withdraw(vsBond, &who, value)?; - RedeemPool::::set(Self::redeem_pool().saturating_sub(value)); - } else if fund.status == FundStatus::RefundWithdrew { - T::MultiCurrency::slash_reserved(vsToken, &who, contributed); - T::MultiCurrency::slash_reserved(vsBond, &who, contributed); - } + T::MultiCurrency::withdraw(vsToken, &who, value)?; + T::MultiCurrency::withdraw(vsBond, &who, value)?; + RedeemPool::::set(Self::redeem_pool().saturating_sub(value)); fund.raised = fund.raised.saturating_sub(value); Funds::::insert(index, Some(fund.clone())); - if T::TransactType::get() == ParachainTransactType::Xcm { - T::MultiCurrency::transfer( - T::RelayChainToken::get(), - &Self::fund_account_id(index), - &who, - value, - )?; - } - let contributed_new = contributed.saturating_sub(value); - Self::put_contribution( - fund.trie_index, + T::MultiCurrency::transfer( + T::RelayChainToken::get(), + &Self::fund_account_id(index), &who, - contributed_new, - ContributionStatus::Redeemed, - ); + value, + )?; Self::deposit_event(Event::Redeemed( who, index, @@ -944,6 +946,33 @@ pub mod pallet { Ok(()) } + /// Remove a fund after the retirement period has ended and all funds have been returned. + #[pallet::weight(( + 0, + DispatchClass::Normal, + Pays::No + ))] + #[transactional] + pub fn dissolve_refunded( + origin: OriginFor, + #[pallet::compact] index: ParaId, + #[pallet::compact] first_slot: LeasePeriod, + #[pallet::compact] last_slot: LeasePeriod, + ) -> DispatchResult { + T::EnsureConfirmAsGovernance::ensure_origin(origin)?; + + let fund = Self::failed_funds_to_refund((index, first_slot, last_slot)) + .ok_or(Error::::InvalidRefund)?; + + ensure!(fund.status == FundStatus::FailedToContinue, Error::::InvalidFundStatus); + + FailedFundsToRefund::::remove((index, first_slot, last_slot)); + + Self::deposit_event(Event::::RefundedDissolved(index, first_slot, last_slot)); + + Ok(()) + } + /// Remove a fund after the retirement period has ended and all funds have been returned. #[pallet::weight(( 0, @@ -980,59 +1009,6 @@ pub mod pallet { Ok(()) } - /// Add proxy for parachain account - /// - `delegate`: The delegate proxy account - #[pallet::weight(( - 0, - DispatchClass::Normal, - Pays::No - ))] - #[transactional] - pub fn add_proxy(origin: OriginFor, delegate: AccountIdOf) -> DispatchResult { - T::EnsureConfirmAsGovernance::ensure_origin(origin)?; - - Self::xcm_ump_add_proxy(delegate.clone()).map_err(|_| Error::::XcmFailed)?; - - Self::deposit_event(Event::ProxyAdded(delegate)); - - Ok(()) - } - - /// Add proxy for parachain account - /// - `delegate`: The delegate proxy account - #[pallet::weight(( - 0, - DispatchClass::Normal, - Pays::No - ))] - #[transactional] - pub fn remove_proxy(origin: OriginFor, delegate: AccountIdOf) -> DispatchResult { - T::EnsureConfirmAsGovernance::ensure_origin(origin)?; - - Self::xcm_ump_remove_proxy(delegate.clone()).map_err(|_| Error::::XcmFailed)?; - - Self::deposit_event(Event::ProxyRemoved(delegate)); - - 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(()) - } - /// transfer asset to statemine #[pallet::weight(T::WeightInfo::contribute())] pub fn transfer_statemine_assets( @@ -1149,6 +1125,20 @@ pub mod pallet { (slot + 1).saturating_mul(T::LeasePeriod::get()) } + pub fn find_fund( + index: ParaId, + first_slot: LeasePeriod, + last_slot: LeasePeriod, + ) -> Result, LeasePeriod>, Error> { + return match Self::failed_funds_to_refund((index, first_slot, last_slot)) { + Some(fund) => Ok(fund), + _ => match Self::funds(index) { + Some(fund) => Ok(fund), + _ => Err(Error::::InvalidFundNotExist), + }, + }; + } + pub fn fund_account_id(index: ParaId) -> T::AccountId { T::PalletId::get().into_sub_account(index) } @@ -1264,62 +1254,6 @@ pub mod pallet { ) }) } - - fn xcm_ump_add_proxy(delegate: AccountIdOf) -> Result { - use_relay!({ - let call = - RelaychainCall::Proxy::, AccountIdOf, BlockNumberFor>( - ProxyCall::Add(AddProxy { - delegate, - proxy_type: ProxyType::Any, - delay: T::BlockNumber::zero(), - }), - ) - .encode() - .into(); - - T::BifrostXcmExecutor::ump_transact( - MultiLocation::here(), - call, - T::AddProxyWeight::get(), - false, - 0, - ) - }) - } - - fn xcm_ump_remove_proxy(delegate: AccountIdOf) -> Result { - use_relay!({ - let call = - RelaychainCall::Proxy::, AccountIdOf, BlockNumberFor>( - ProxyCall::Remove(RemoveProxy { - delegate, - proxy_type: ProxyType::Any, - delay: T::BlockNumber::zero(), - }), - ) - .encode() - .into(); - - T::BifrostXcmExecutor::ump_transact( - MultiLocation::here(), - call, - T::AddProxyWeight::get(), - false, - 0, - ) - }) - } - - 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 a3c80897e..f5e580b38 100644 --- a/pallets/salp/src/mock.rs +++ b/pallets/salp/src/mock.rs @@ -17,7 +17,6 @@ // along with this program. If not, see . // Ensure we're `no_std` when compiling for Wasm. -use bifrost_kusama_runtime::create_x2_multilocation; use frame_support::{ construct_runtime, parameter_types, traits::{EnsureOrigin, GenesisBuild, Nothing}, @@ -202,7 +201,7 @@ parameter_types! { BRUCE, CATHI ],2); - pub RelaychainSovereignSubAccount: MultiLocation = create_x2_multilocation(0); + pub RelaychainSovereignSubAccount: MultiLocation = MultiLocation::parent(); pub SalpTransactProxyType: ParachainTransactProxyType = ParachainTransactProxyType::Derived; pub SalpTransactType: ParachainTransactType = ParachainTransactType::Xcm; pub const RelayNetwork: NetworkId = NetworkId::Kusama; diff --git a/pallets/salp/src/tests.rs b/pallets/salp/src/tests.rs index 94e29b3be..c8b18ba2c 100644 --- a/pallets/salp/src/tests.rs +++ b/pallets/salp/src/tests.rs @@ -291,12 +291,8 @@ fn contribute_should_work() { #[allow(non_snake_case)] let (vsToken, vsBond) = Salp::vsAssets(3_000, 1, SlotLength::get()); - assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).reserved, 100); - assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).reserved, 100); + assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 100); + assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 100); }); } @@ -330,12 +326,8 @@ fn double_contribute_should_work() { #[allow(non_snake_case)] let (vsToken, vsBond) = Salp::vsAssets(3_000, 1, SlotLength::get()); - assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).reserved, 200); - assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).reserved, 200); + assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 200); + assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 200); }); } @@ -391,12 +383,8 @@ fn confirm_contribute_later_should_work() { #[allow(non_snake_case)] let (vsToken, vsBond) = Salp::vsAssets(3_000, 1, SlotLength::get()); - assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).reserved, 100); - assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).reserved, 100); + assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 100); + assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 100); }); } @@ -667,7 +655,7 @@ fn refund_should_work() { )); assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); - assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000)); + assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000, 1, SlotLength::get(), 100)); #[allow(non_snake_case)] let (vsToken, vsBond) = Salp::vsAssets(3_000, 1, SlotLength::get()); @@ -697,7 +685,7 @@ fn refund_when_xcm_error_should_work() { )); assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); - assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000)); + assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000, 1, SlotLength::get(), 100)); #[allow(non_snake_case)] let (vsToken, vsBond) = Salp::vsAssets(3_000, 1, SlotLength::get()); @@ -724,19 +712,11 @@ fn double_refund_when_one_of_xcm_error_should_work() { )); assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); - assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000)); - assert_noop!(Salp::refund(Some(BRUCE).into(), 3_000), Error::::ZeroContribution); - }); -} - -#[test] -fn refund_with_zero_contribution_should_fail() { - new_test_ext().execute_with(|| { - assert_ok!(Salp::create(Some(ALICE).into(), 3_000, 1_000, 1, SlotLength::get())); - assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); - assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); - - assert_noop!(Salp::refund(Some(BRUCE).into(), 3_000), Error::::ZeroContribution); + assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000, 1, SlotLength::get(), 100)); + assert_noop!( + Salp::refund(Some(BRUCE).into(), 3_000, 1, SlotLength::get(), 100), + Error::::NotEnoughBalanceInFund + ); }); } @@ -755,10 +735,16 @@ fn refund_with_wrong_origin_should_fail() { assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); - assert_noop!(Salp::refund(Origin::root(), 3_000), DispatchError::BadOrigin); - assert_noop!(Salp::refund(Origin::none(), 3_000), DispatchError::BadOrigin); + assert_noop!( + Salp::refund(Origin::root(), 3_000, 1, SlotLength::get(), 100), + DispatchError::BadOrigin + ); + assert_noop!( + Salp::refund(Origin::none(), 3_000, 1, SlotLength::get(), 100), + DispatchError::BadOrigin + ); - assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000)); + assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000, 1, SlotLength::get(), 100)); }); } @@ -769,15 +755,13 @@ fn refund_with_wrong_para_id_should_fail() { assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); - assert_noop!(Salp::refund(Some(BRUCE).into(), 4_000), Error::::InvalidParaId); + assert_noop!( + Salp::refund(Some(BRUCE).into(), 4_000, 1, SlotLength::get(), 100), + Error::::InvalidFundNotExist + ); }); } -#[test] -fn refund_with_when_ump_wrong_should_fail() { - // TODO: Require an solution to settle with parallel test workflow -} - #[test] fn dissolve_should_work() { new_test_ext().execute_with(|| { @@ -1182,46 +1166,6 @@ fn batch_unlock_should_work() { }) } -#[test] -fn edit_fund_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Salp::create(Some(ALICE).into(), 3_000, 1_000, 1, SlotLength::get())); - assert_ok!(Salp::contribute(Some(BRUCE).into(), 3_000, 100)); - assert_ok!(Salp::confirm_contribute( - Some(ALICE).into(), - BRUCE, - 3_000, - true, - CONTRIBUTON_INDEX - )); - assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); - assert_ok!(Salp::edit( - Some(ALICE).into(), - 3_000, - 1_000, - 200, - 2, - SlotLength::get() + 1, - None - )); - let mut fund = Salp::funds(3_000).unwrap(); - assert_eq!(fund.first_slot, 2); - assert_eq!(fund.status, FundStatus::Failed); - assert_ok!(Salp::edit( - Some(ALICE).into(), - 3_000, - 1_000, - 200, - 2, - SlotLength::get() + 1, - Some(FundStatus::Ongoing), - )); - fund = Salp::funds(3_000).unwrap(); - assert_eq!(fund.status, FundStatus::Ongoing); - assert_eq!(fund.raised, 200); - }) -} - #[test] fn unlock_when_fund_ongoing_should_work() { new_test_ext().execute_with(|| { @@ -1249,35 +1193,27 @@ fn unlock_when_fund_ongoing_should_work() { } #[test] -fn refund_when_fund_ongoing_should_work() { +fn set_confirmor_should_work() { new_test_ext().execute_with(|| { assert_ok!(Salp::create(Some(ALICE).into(), 3_000, 1_000, 1, SlotLength::get())); assert_ok!(Salp::contribute(Some(BRUCE).into(), 3_000, 100)); + assert_noop!( + Salp::confirm_contribute(Some(BRUCE).into(), BRUCE, 3_000, true, CONTRIBUTON_INDEX), + DispatchError::BadOrigin, + ); + assert_ok!(Salp::set_multisig_confirm_account(Some(ALICE).into(), BRUCE)); assert_ok!(Salp::confirm_contribute( - Some(ALICE).into(), + Some(BRUCE).into(), BRUCE, 3_000, true, CONTRIBUTON_INDEX )); - assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000)); - - #[allow(non_snake_case)] - let (vsToken, vsBond) = Salp::vsAssets(3_000, 1, SlotLength::get()); - assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).reserved, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).reserved, 0); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).free, INIT_BALANCE); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).reserved, 0); }); } #[test] -fn redeem_when_fund_failed_should_work() { +fn refund_meanwhile_issue_should_work() { new_test_ext().execute_with(|| { assert_ok!(Salp::create(Some(ALICE).into(), 3_000, 1_000, 1, SlotLength::get())); assert_ok!(Salp::contribute(Some(BRUCE).into(), 3_000, 100)); @@ -1288,56 +1224,88 @@ fn redeem_when_fund_failed_should_work() { true, CONTRIBUTON_INDEX )); - - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).free, INIT_BALANCE - 100); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).reserved, 0); - - assert_eq!( - Tokens::accounts(Salp::fund_account_id(3_000), RelayCurrencyId::get()).free, - 100 + assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); + assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); + let (_, vs_bond_old) = Salp::vsAssets(3_000, 1, SlotLength::get()); + assert_eq!(Tokens::accounts(BRUCE, vs_bond_old).free, 100); + assert_ok!(Salp::continue_fund(Some(ALICE).into(), 3_000, 2, SlotLength::get() + 1)); + let old_fund = Salp::failed_funds_to_refund((3_000, 1, SlotLength::get())).unwrap(); + assert_eq!(old_fund.first_slot, 1); + assert_eq!(old_fund.raised, 100); + let mut new_fund = Salp::funds(3_000).unwrap(); + assert_eq!(new_fund.first_slot, 2); + assert_eq!(new_fund.raised, 100); + let (_, vs_bond_new) = Salp::vsAssets(3_000, 2, SlotLength::get() + 1); + assert_ok!(Salp::contribute(Some(BRUCE).into(), 3_000, 100)); + assert_ok!(Salp::confirm_contribute( + Some(ALICE).into(), + BRUCE, + 3_000, + true, + CONTRIBUTON_INDEX + )); + new_fund = Salp::funds(3_000).unwrap(); + assert_eq!(new_fund.raised, 200); + assert_eq!(Tokens::accounts(BRUCE, vs_bond_new).free, 100); + // refund from old failed fund should success + assert_ok!(Salp::refund(Some(BRUCE).into(), 3_000, 1, SlotLength::get(), 50)); + assert_eq!(Tokens::accounts(BRUCE, vs_bond_old).free, 50); + // refund from new fund should failed + assert_noop!( + Salp::refund(Some(BRUCE).into(), 3_000, 2, SlotLength::get() + 1, 100), + Error::::InvalidRefund, ); - assert_eq!( - Tokens::accounts(Salp::fund_account_id(3_000), RelayCurrencyId::get()).frozen, - 0 + // refund from not exist fund should failed + assert_noop!( + Salp::refund(Some(BRUCE).into(), 4_000, 2, SlotLength::get() + 1, 100), + Error::::InvalidFundNotExist, ); - assert_eq!( - Tokens::accounts(Salp::fund_account_id(3_000), RelayCurrencyId::get()).reserved, - 0 + // after dissolve failed fund refund from old should fail + assert_ok!(Salp::dissolve_refunded(Some(ALICE).into(), 3_000, 1, SlotLength::get())); + assert_noop!( + Salp::refund(Some(BRUCE).into(), 3_000, 1, SlotLength::get(), 50), + Error::::InvalidRefund, ); - - assert_ok!(Salp::unlock(Some(BRUCE).into(), BRUCE, 3_000)); - assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); + // after new fund finally success redeem should success + assert_ok!(Salp::fund_success(Some(ALICE).into(), 3_000)); + assert_ok!(Salp::fund_retire(Some(ALICE).into(), 3_000)); assert_ok!(Salp::withdraw(Some(ALICE).into(), 3_000)); - - #[allow(non_snake_case)] - let (vsToken, vsBond) = Salp::vsAssets(3_000, 1, SlotLength::get()); - - assert_ok!(>::transfer(vsToken, &BRUCE, &CATHI, 50)); - assert_ok!(>::transfer(vsBond, &BRUCE, &CATHI, 50)); - assert_ok!(Salp::redeem(Some(BRUCE).into(), 3_000, 50)); + assert_eq!(Tokens::accounts(BRUCE, vs_bond_new).free, 50); + // after fund dissolved redeem should fail + assert_ok!(Salp::fund_end(Some(ALICE).into(), 3_000)); + assert_ok!(Salp::dissolve(Some(ALICE).into(), 3_000)); + assert_noop!(Salp::redeem(Some(BRUCE).into(), 3_000, 50), Error::::InvalidParaId); + }); +} - assert_eq!(Tokens::accounts(BRUCE, vsToken).free, 50); - assert_eq!(Tokens::accounts(BRUCE, vsToken).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsToken).reserved, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).free, 50); - assert_eq!(Tokens::accounts(BRUCE, vsBond).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, vsBond).reserved, 0); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).free, INIT_BALANCE - 50); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).frozen, 0); - assert_eq!(Tokens::accounts(BRUCE, RelayCurrencyId::get()).reserved, 0); - - assert_ok!(Salp::redeem(Some(CATHI).into(), 3_000, 50)); +#[test] +fn edit_fund_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Salp::create(Some(ALICE).into(), 3_000, 1_000, 1, SlotLength::get())); + assert_ok!(Salp::contribute(Some(BRUCE).into(), 3_000, 100)); + assert_ok!(Salp::confirm_contribute( + Some(ALICE).into(), + BRUCE, + 3_000, + true, + CONTRIBUTON_INDEX + )); + assert_ok!(Salp::fund_fail(Some(ALICE).into(), 3_000)); + let mut fund = Salp::funds(3_000).unwrap(); + assert_eq!(fund.raised, 100); - assert_eq!(Tokens::accounts(CATHI, vsToken).free, 50); - assert_eq!(Tokens::accounts(CATHI, vsToken).frozen, 0); - assert_eq!(Tokens::accounts(CATHI, vsToken).reserved, 0); - assert_eq!(Tokens::accounts(CATHI, vsBond).free, 50); - assert_eq!(Tokens::accounts(CATHI, vsBond).frozen, 0); - assert_eq!(Tokens::accounts(CATHI, vsBond).reserved, 0); - assert_eq!(Tokens::accounts(CATHI, RelayCurrencyId::get()).free, INIT_BALANCE + 50); - assert_eq!(Tokens::accounts(CATHI, RelayCurrencyId::get()).frozen, 0); - assert_eq!(Tokens::accounts(CATHI, RelayCurrencyId::get()).reserved, 0); + assert_ok!(Salp::edit( + Some(ALICE).into(), + 3_000, + 1_000, + 150, + 2, + SlotLength::get() + 1, + Some(FundStatus::Ongoing) + )); + fund = Salp::funds(3_000).unwrap(); + assert_eq!(fund.raised, 150); + assert_eq!(fund.status, FundStatus::Ongoing); }); } diff --git a/runtime/bifrost-kusama/src/lib.rs b/runtime/bifrost-kusama/src/lib.rs index 9ba7f9290..05e02d622 100644 --- a/runtime/bifrost-kusama/src/lib.rs +++ b/runtime/bifrost-kusama/src/lib.rs @@ -73,7 +73,7 @@ use bifrost_flexible_fee::{ use bifrost_runtime_common::{ cent, constants::{parachains, time::*}, - dollar, micro, microcent, milli, millicent, + dollar, micro, milli, millicent, r#impl::{ BifrostAccountIdToMultiLocation, BifrostAssetMatcher, BifrostCurrencyIdConvert, BifrostFilteredAssets, diff --git a/runtime/common/src/impl.rs b/runtime/common/src/impl.rs index afc0ca6e3..cbb02418a 100644 --- a/runtime/common/src/impl.rs +++ b/runtime/common/src/impl.rs @@ -188,13 +188,11 @@ impl> Convert> for BifrostCurre X3(Parachain(id), PalletInstance(index), GeneralIndex(key)) if (id == parachains::Statemine::ID && index == parachains::Statemine::PALLET_ID) => - { if key == parachains::Statemine::RMRK_ID.into() { Some(Token(TokenSymbol::RMRK)) } else { None - } - }, + }, X1(Parachain(id)) if id == parachains::phala::ID => Some(Token(TokenSymbol::PHA)), _ => None, },