From a0f5015b70e6ceb78ab52c16979327b511426d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Tue, 21 May 2024 00:42:22 -0500 Subject: [PATCH 01/47] change(pallet-assets): define HeldBalance --- substrate/frame/assets/src/lib.rs | 5 +++++ substrate/frame/assets/src/mock.rs | 1 + substrate/frame/assets/src/types.rs | 31 +++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index 6891f04dfb51..923bd9e63078 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -357,6 +357,11 @@ pub mod pallet { #[pallet::no_default] type Freezer: FrozenBalance; + /// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be + /// respected in all permissionless operations. + #[pallet::no_default] + type Holder: HeldBalance; + /// Additional data to be stored with an account's asset balance. type Extra: Member + Parameter + Default + MaxEncodedLen; diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index f6173a451fff..a2ebdeba9ef3 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -114,6 +114,7 @@ impl Config for Test { type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; type Freezer = TestFreezer; + type Holder = (); type CallbackHandle = AssetsCallbackHandle; } diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index 11edc7d3fcb5..e994385b400a 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -232,6 +232,37 @@ impl FrozenBalance for fn died(_: AssetId, _: &AccountId) {} } +/// Trait for specifying a balance that cannot be reduced or forcely reduced. +/// This balance is then added to the minimum balance that must be met in conjunction +/// with the frozen balance (if any), and the `minimum_balance` of the asset. +pub trait HeldBalance { + /// Return the held balance. + /// + /// Generally, the balance of every account must be at least the sum of this (if `Some`) and + /// the asset's `minimum_balance` (the latter since there may be complications to destroying an + /// asset's account completely). + /// + /// Under normal circumstances, the account balance should not go below the sum of this (if + /// `Some`) and the asset's minimum balance. + /// + /// In special cases (privileged intervention) the account balance may also go below the sum. + /// + /// If `None` is returned, then nothing special is enforced. + fn held_balance(asset: AssetId, who: &AccountId) -> Option; + + /// Called after an account has been removed. + /// + /// NOTE: It is possible that the asset does no longer exist when this hook is called. + fn died(asset: AssetId, who: &AccountId); +} + +impl HeldBalance for () { + fn held_balance(_: AssetId, _: &AccountId) -> Option { + None + } + fn died(_: AssetId, _: &AccountId) {} +} + #[derive(Copy, Clone, PartialEq, Eq)] pub(super) struct TransferFlags { /// The debited account must stay alive at the end of the operation; an error is returned if From 6d2aa57bd9746a5ab3dc3319238241e8c3774664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 10 Jun 2024 11:29:16 -0500 Subject: [PATCH 02/47] change(pallet-assets): implement `Holder` to calculate reducible balance based on whether there's held assets or not --- Cargo.lock | 4 ++++ substrate/frame/assets/Cargo.toml | 1 + substrate/frame/assets/src/functions.rs | 28 +++++++++++++++---------- substrate/frame/assets/src/tests.rs | 1 + substrate/frame/assets/src/types.rs | 17 ++++++--------- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27cb7af04d63..888f39c3175e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5212,8 +5212,11 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ + "atty", + "humantime", "log", "regex", + "termcolor", ] [[package]] @@ -9573,6 +9576,7 @@ dependencies = [ name = "pallet-assets" version = "29.0.0" dependencies = [ + "env_logger 0.8.4", "frame-benchmarking", "frame-support", "frame-system", diff --git a/substrate/frame/assets/Cargo.toml b/substrate/frame/assets/Cargo.toml index 3b95750c14c8..1a8246f72d4f 100644 --- a/substrate/frame/assets/Cargo.toml +++ b/substrate/frame/assets/Cargo.toml @@ -30,6 +30,7 @@ frame-benchmarking = { path = "../benchmarking", default-features = false, optio sp-core = { path = "../../primitives/core", default-features = false } [dev-dependencies] +env_logger = "*" sp-std = { path = "../../primitives/std" } sp-io = { path = "../../primitives/io" } pallet-balances = { path = "../balances" } diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 8791aaa736b3..5ec85de3b960 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -221,20 +221,26 @@ impl, I: 'static> Pallet { let account = Account::::get(&id, who).ok_or(Error::::NoAccount)?; ensure!(!account.status.is_frozen(), Error::::Frozen); - let amount = if let Some(frozen) = T::Freezer::frozen_balance(id, who) { - // Frozen balance: account CANNOT be deleted - let required = - frozen.checked_add(&details.min_balance).ok_or(ArithmeticError::Overflow)?; - account.balance.saturating_sub(required) + let frozen = T::Freezer::frozen_balance(id.clone(), who).unwrap_or_default(); + let amount = if let Some(held) = T::Holder::held_balance(id, who) { + let untouchable = frozen + .saturating_sub(held) + .checked_add(&details.min_balance) + .ok_or(ArithmeticError::Overflow)?; + account.balance.saturating_sub(untouchable) } else { - if keep_alive { - // We want to keep the account around. - account.balance.saturating_sub(details.min_balance) + let required = if frozen > Zero::zero() { + frozen.checked_add(&details.min_balance).ok_or(ArithmeticError::Overflow)? } else { - // Don't care if the account dies - account.balance - } + if keep_alive { + details.min_balance + } else { + Zero::zero() + } + }; + account.balance.saturating_sub(required) }; + Ok(amount.min(details.supply)) } diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index c7021bcad531..3b5b56aa3fc2 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1290,6 +1290,7 @@ fn finish_destroy_asset_destroys_asset() { #[test] fn freezer_should_work() { + let _ = env_logger::try_init(); new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index e994385b400a..d1f8718c0b5f 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -232,20 +232,15 @@ impl FrozenBalance for fn died(_: AssetId, _: &AccountId) {} } -/// Trait for specifying a balance that cannot be reduced or forcely reduced. -/// This balance is then added to the minimum balance that must be met in conjunction -/// with the frozen balance (if any), and the `minimum_balance` of the asset. +/// Trait for specifying a balance that is distinct from the free balance. +/// This balance is then summed up with the balance of the account, and the +/// `minimum_balance` (and frozen balance, if any) of the asset to calculate +/// the reducible balance. pub trait HeldBalance { /// Return the held balance. /// - /// Generally, the balance of every account must be at least the sum of this (if `Some`) and - /// the asset's `minimum_balance` (the latter since there may be complications to destroying an - /// asset's account completely). - /// - /// Under normal circumstances, the account balance should not go below the sum of this (if - /// `Some`) and the asset's minimum balance. - /// - /// In special cases (privileged intervention) the account balance may also go below the sum. + /// If `Some`, it means some balance is suspended, and it can be infallibly + /// slashed. /// /// If `None` is returned, then nothing special is enforced. fn held_balance(asset: AssetId, who: &AccountId) -> Option; From 029e887382193818eca705b7895ab8a5f0fcecf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 17 Jun 2024 10:24:29 -0500 Subject: [PATCH 03/47] change: add Holder type to missing pallet_assets Config implementations --- cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs | 3 +++ .../parachains/runtimes/assets/asset-hub-westend/src/lib.rs | 3 +++ cumulus/parachains/runtimes/testing/penpal/src/lib.rs | 2 ++ .../parachains/runtimes/testing/rococo-parachain/src/lib.rs | 1 + polkadot/xcm/pallet-xcm/src/mock.rs | 1 + polkadot/xcm/xcm-builder/src/tests/pay/mock.rs | 1 + substrate/bin/node/runtime/src/lib.rs | 2 ++ substrate/frame/asset-conversion/src/mock.rs | 2 ++ substrate/frame/contracts/mock-network/src/parachain.rs | 1 + substrate/frame/nft-fractionalization/src/mock.rs | 1 + .../asset-conversion-tx-payment/src/mock.rs | 2 ++ .../frame/transaction-payment/asset-tx-payment/src/mock.rs | 1 + 12 files changed, 20 insertions(+) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 23d8f9b667dd..ed4e8b521f87 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -265,6 +265,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; @@ -303,6 +304,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU128<0>; type ApprovalDeposit = ApprovalDeposit; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets_pool::WeightInfo; @@ -398,6 +400,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; type ApprovalDeposit = ForeignAssetsApprovalDeposit; type StringLimit = ForeignAssetsAssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets_foreign::WeightInfo; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index cb2f11637187..5475c0c23d76 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -248,6 +248,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; @@ -285,6 +286,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU128<0>; type ApprovalDeposit = ConstU128<0>; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets_pool::WeightInfo; @@ -377,6 +379,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; type ApprovalDeposit = ForeignAssetsApprovalDeposit; type StringLimit = ForeignAssetsAssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = weights::pallet_assets_foreign::WeightInfo; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 89885d77378b..6c4d72cb6b56 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -445,6 +445,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = pallet_assets::weights::SubstrateWeight; @@ -480,6 +481,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; type ApprovalDeposit = ForeignAssetsApprovalDeposit; type StringLimit = ForeignAssetsAssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = pallet_assets::weights::SubstrateWeight; diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index df335368be1c..3d3468f53d91 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -580,6 +580,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = pallet_assets::weights::SubstrateWeight; diff --git a/polkadot/xcm/pallet-xcm/src/mock.rs b/polkadot/xcm/pallet-xcm/src/mock.rs index 2cc228476ba8..7948d2b346c9 100644 --- a/polkadot/xcm/pallet-xcm/src/mock.rs +++ b/polkadot/xcm/pallet-xcm/src/mock.rs @@ -314,6 +314,7 @@ impl pallet_assets::Config for Test { type MetadataDepositPerByte = ConstU128<1>; type ApprovalDeposit = ConstU128<1>; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type WeightInfo = (); type CallbackHandle = (); diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 019113a12b2f..8ac3d8288920 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -103,6 +103,7 @@ impl pallet_assets::Config for Test { type AssetAccountDeposit = AssetAccountDeposit; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = (); diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index faf7cf7967b7..09d5bc8bb6d5 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1658,6 +1658,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = StringLimit; + type Holder = (); type Freezer = (); type Extra = (); type CallbackHandle = (); @@ -1685,6 +1686,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = StringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = pallet_assets::weights::SubstrateWeight; diff --git a/substrate/frame/asset-conversion/src/mock.rs b/substrate/frame/asset-conversion/src/mock.rs index 4591b87c1867..2a50d17f286a 100644 --- a/substrate/frame/asset-conversion/src/mock.rs +++ b/substrate/frame/asset-conversion/src/mock.rs @@ -92,6 +92,7 @@ impl pallet_assets::Config for Test { type MetadataDepositPerByte = ConstU128<1>; type ApprovalDeposit = ConstU128<1>; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = (); @@ -117,6 +118,7 @@ impl pallet_assets::Config for Test { type MetadataDepositPerByte = ConstU128<0>; type ApprovalDeposit = ConstU128<0>; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = (); diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index d4ad47581d16..b680ba1a9f0f 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -125,6 +125,7 @@ impl pallet_assets::Config for Runtime { type AssetAccountDeposit = AssetAccountDeposit; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = (); diff --git a/substrate/frame/nft-fractionalization/src/mock.rs b/substrate/frame/nft-fractionalization/src/mock.rs index 82a608816260..14ac58122459 100644 --- a/substrate/frame/nft-fractionalization/src/mock.rs +++ b/substrate/frame/nft-fractionalization/src/mock.rs @@ -88,6 +88,7 @@ impl pallet_assets::Config for Test { type MetadataDepositPerByte = ConstU64<1>; type ApprovalDeposit = ConstU64<1>; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type Extra = (); type CallbackHandle = (); diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index d61558cf5363..7ee4600f0ea6 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -199,6 +199,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU64<0>; type ApprovalDeposit = ConstU64<0>; type StringLimit = ConstU32<20>; + type Holder = (); type Freezer = (); type Extra = (); type CallbackHandle = (); @@ -224,6 +225,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU64<0>; type ApprovalDeposit = ConstU64<0>; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = (); diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs index b04d4ffd9e0b..c925eaa9d4ee 100644 --- a/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -162,6 +162,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU64<0>; type ApprovalDeposit = ConstU64<0>; type StringLimit = ConstU32<20>; + type Holder = (); type Freezer = (); type Extra = (); type CallbackHandle = (); From bf376a5931f0c22a6ca4352a50aad8df369e8c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 17 Jun 2024 10:25:07 -0500 Subject: [PATCH 04/47] change(pallet-assets): call died hook for Holder whenever necessary --- substrate/frame/assets/src/functions.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 5ec85de3b960..4c780f737371 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -370,7 +370,8 @@ impl, I: 'static> Pallet { } Asset::::insert(&id, details); // Executing a hook here is safe, since it is not in a `mutate`. - T::Freezer::died(id, &who); + T::Freezer::died(id.clone(), &who); + T::Holder::died(id, &who); Ok(()) } @@ -401,7 +402,8 @@ impl, I: 'static> Pallet { } Asset::::insert(&id, details); // Executing a hook here is safe, since it is not in a `mutate`. - T::Freezer::died(id, &who); + T::Freezer::died(id.clone(), &who); + T::Holder::died(id, &who); return Ok(()) } @@ -569,7 +571,8 @@ impl, I: 'static> Pallet { // Execute hook outside of `mutate`. if let Some(Remove) = target_died { - T::Freezer::died(id, target); + T::Freezer::died(id.clone(), target); + T::Holder::died(id, target); } Ok(actual) } @@ -593,7 +596,8 @@ impl, I: 'static> Pallet { let (balance, died) = Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?; if let Some(Remove) = died { - T::Freezer::died(id, source); + T::Freezer::died(id.clone(), source); + T::Holder::died(id, source); } Ok(balance) } @@ -791,6 +795,7 @@ impl, I: 'static> Pallet { for who in &dead_accounts { T::Freezer::died(id.clone(), &who); + T::Holder::died(id.clone(), &who); } Self::deposit_event(Event::AccountsDestroyed { @@ -951,7 +956,8 @@ impl, I: 'static> Pallet { // Execute hook outside of `mutate`. if let Some(Remove) = owner_died { - T::Freezer::died(id, owner); + T::Freezer::died(id.clone(), owner); + T::Holder::died(id, owner); } Ok(()) } From 89ea43a436c15f463c130573294f2bb4707c7029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 17 Jun 2024 11:10:52 -0500 Subject: [PATCH 05/47] fix(pallet-asset-conversion-ops): missing Holder in mock config --- polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs | 1 + substrate/frame/asset-conversion/ops/src/mock.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs index aa6c1422b608..912d2c1fea4b 100644 --- a/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs +++ b/polkadot/xcm/xcm-fee-payment-runtime-api/tests/mock.rs @@ -87,6 +87,7 @@ impl pallet_assets::Config for TestRuntime { type Currency = Balances; type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; + type Holder = (); type Freezer = (); type AssetDeposit = ConstU128<1>; type AssetAccountDeposit = ConstU128<10>; diff --git a/substrate/frame/asset-conversion/ops/src/mock.rs b/substrate/frame/asset-conversion/ops/src/mock.rs index 91c18b2e7949..31c3bb41fd12 100644 --- a/substrate/frame/asset-conversion/ops/src/mock.rs +++ b/substrate/frame/asset-conversion/ops/src/mock.rs @@ -68,6 +68,7 @@ impl pallet_assets::Config for Test { type Currency = Balances; type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; + type Holder = (); type Freezer = (); } @@ -77,6 +78,7 @@ impl pallet_assets::Config for Test { type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; + type Holder = (); type Freezer = (); } From 2ab35354d86904c035b21a2229452841b79b0457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 10 Jul 2024 14:34:15 +0200 Subject: [PATCH 06/47] change(pallet-assets): adjust reducible/can_decrease to be in line with the tokens' _balance components_ model. On the most recent documentation about tokens (both _fungible_ and _fungibles_ (sets)), the model for calculating the different balance components is explained. The prior implementation of `pallet-assets` featured a weird definition of how to handle them that was not in line with the expected (see ) definition of tokens. This commit changes this implementation for methods `reducible_balance` and `can_decrease` to introduce the calculation of `spendable` balance, and the consequences derived of decreasing a balance that may include both `frozen` and `held` balances. --- substrate/frame/assets/src/functions.rs | 70 +++++++++++++------------ substrate/frame/assets/src/tests.rs | 8 +-- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 075af2712622..4e8131de62f5 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -186,22 +186,31 @@ impl, I: 'static> Pallet { return Frozen } if let Some(rest) = account.balance.checked_sub(&amount) { - if let Some(frozen) = T::Freezer::frozen_balance(id.clone(), who) { - match frozen.checked_add(&details.min_balance) { - Some(required) if rest < required => return Frozen, - None => return Overflow, - _ => {}, - } - } + match ( + T::Holder::held_balance(id.clone(), who), + T::Freezer::frozen_balance(id.clone(), who), + ) { + (None, None) => + if rest < details.min_balance { + if keep_alive { + WouldDie + } else { + ReducedToZero(rest) + } + } else { + Success + }, + (maybe_held, maybe_frozen) => { + let frozen = maybe_frozen.unwrap_or_default(); + let held = maybe_held.unwrap_or_default(); - if rest < details.min_balance { - if keep_alive { - WouldDie - } else { - ReducedToZero(rest) - } - } else { - Success + let required = frozen.saturating_sub(held).max(details.min_balance); + if rest < required { + return Frozen + } + + Success + }, } } else { BalanceLow @@ -221,25 +230,20 @@ impl, I: 'static> Pallet { let account = Account::::get(&id, who).ok_or(Error::::NoAccount)?; ensure!(!account.status.is_frozen(), Error::::Frozen); - let frozen = T::Freezer::frozen_balance(id.clone(), who).unwrap_or_default(); - let amount = if let Some(held) = T::Holder::held_balance(id, who) { - let untouchable = frozen - .saturating_sub(held) - .checked_add(&details.min_balance) - .ok_or(ArithmeticError::Overflow)?; - account.balance.saturating_sub(untouchable) - } else { - let required = if frozen > Zero::zero() { - frozen.checked_add(&details.min_balance).ok_or(ArithmeticError::Overflow)? - } else { - if keep_alive { - details.min_balance - } else { - Zero::zero() - } - }; - account.balance.saturating_sub(required) + let untouchable = match ( + T::Holder::held_balance(id.clone(), who), + T::Freezer::frozen_balance(id.clone(), who), + keep_alive, + ) { + (None, None, true) => details.min_balance, + (None, None, false) => Zero::zero(), + (maybe_held, maybe_frozen, _) => { + let held = maybe_held.unwrap_or_default(); + let frozen = maybe_frozen.unwrap_or_default(); + frozen.saturating_sub(held).max(details.min_balance) + }, }; + let amount = account.balance.saturating_sub(untouchable); Ok(amount.min(details.supply)) } diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index 3b5b56aa3fc2..00283dd90286 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1299,9 +1299,9 @@ fn freezer_should_work() { // freeze 50 of it. set_frozen_balance(0, 1, 50); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 20)); - // cannot transfer another 21 away as this would take the non-frozen balance (30) to below - // the minimum balance (10). + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 30)); + // cannot transfer another 21 away as this would take the spendable balance (30) to below + // zero. assert_noop!( Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 21), Error::::BalanceLow @@ -1324,7 +1324,7 @@ fn freezer_should_work() { // and if we clear it, we can remove the account completely. clear_frozen_balance(0, 1); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 49)); assert_eq!(hooks(), vec![Hook::Died(0, 1)]); }); } From c76b654c5243e1ea8233f2dcb26c84f5980cc0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 11 Jul 2024 10:56:38 +0200 Subject: [PATCH 07/47] change(pallet-assets): introduce tests to check for holds and freezes working together --- substrate/frame/assets/src/mock.rs | 35 ++++++++++++++++++++-- substrate/frame/assets/src/tests.rs | 45 ++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index 9fe7bb99c1f6..e9a0306726d6 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -22,7 +22,7 @@ use crate as pallet_assets; use codec::Encode; use frame_support::{ - construct_runtime, derive_impl, parameter_types, + assert_ok, construct_runtime, derive_impl, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32}, }; use sp_io::storage; @@ -103,7 +103,7 @@ impl Config for Test { type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; type Freezer = TestFreezer; - type Holder = (); + type Holder = TestHolder; type CallbackHandle = (AssetsCallbackHandle, AutoIncAssetId); } @@ -115,9 +115,40 @@ pub enum Hook { } parameter_types! { static Frozen: HashMap<(u32, u64), u64> = Default::default(); + static Held: HashMap<(u32, u64), u64> = Default::default(); static Hooks: Vec = Default::default(); } +pub struct TestHolder; +impl HeldBalance for TestHolder { + fn held_balance(asset: u32, who: &u64) -> Option { + Held::get().get(&(asset, *who)).cloned() + } + + fn died(_asset: u32, _who: &u64) { + // TODO: Connect with existing hooks list + } +} + +pub(crate) fn set_held_balance(asset: u32, who: u64, amount: u64) { + Held::mutate(|v| { + let held_amount = v.get(&(asset, who)).unwrap_or(&0); + + if &amount > held_amount { + // Hold more funds + let amount = amount - held_amount; + let f = DebitFlags { keep_alive: true, best_effort: false }; + assert_ok!(Assets::decrease_balance(asset, &who, amount, f, |_, _| Ok(()))); + } else { + // Release held funds + let amount = held_amount - amount; + assert_ok!(Assets::increase_balance(asset, &who, amount, |_| Ok(()))); + } + + // Asset amount still "exists", we just store it here + v.insert((asset, who), amount); + }); +} pub struct TestFreezer; impl FrozenBalance for TestFreezer { fn frozen_balance(asset: u32, who: &u64) -> Option { diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index 70dd619ac8bf..e54ce926f86d 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1294,7 +1294,6 @@ fn finish_destroy_asset_destroys_asset() { #[test] fn freezer_should_work() { - let _ = env_logger::try_init(); new_test_ext().execute_with(|| { assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); @@ -1333,6 +1332,50 @@ fn freezer_should_work() { }); } +#[test] +fn freezing_and_holds_work() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 10)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + assert_eq!(Assets::balance(0, 1), 100); + + // Hold 50 of it + set_held_balance(0, 1, 50); + assert_eq!(Assets::balance(0, 1), 50); + assert_eq!(TestHolder::held_balance(0, &1), Some(50)); + + // Can freeze up to held + min_balance without affecting reducible + set_frozen_balance(0, 1, 59); + assert_eq!(Assets::reducible_balance(0, &1, true), Ok(40)); + set_frozen_balance(0, 1, 61); + assert_eq!(Assets::reducible_balance(0, &1, true), Ok(39)); + + // Increasing hold is not necessarily restricted by the frozen balance + set_held_balance(0, 1, 62); + assert_eq!(Assets::reducible_balance(0, &1, true), Ok(28)); + + // Transfers are bound to the spendable amount + assert_noop!( + Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 29), + Error::::BalanceLow + ); + // Approved transfers fail as well + Balances::make_free_balance_be(&1, 2); + assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 29)); + assert_noop!( + Assets::transfer_approved(RuntimeOrigin::signed(2), 0, 1, 2, 29), + Error::::BalanceLow + ); + // Also forced transfers fail + assert_noop!( + Assets::force_transfer(RuntimeOrigin::signed(1), 0, 1, 2, 29), + Error::::BalanceLow + ); + // ...but transferring up to spendable works + assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 28)); + }); +} + #[test] fn imbalances_should_work() { use frame_support::traits::fungibles::Balanced; From a4bb10cfe8f79cced347deededf27cf08e883e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Sun, 28 Jul 2024 23:12:27 -0400 Subject: [PATCH 08/47] change(pallet-assets-freezer): adjust mock and tests to changes in `pallet-assets` --- substrate/frame/assets-freezer/src/mock.rs | 1 + substrate/frame/assets-freezer/src/tests.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/substrate/frame/assets-freezer/src/mock.rs b/substrate/frame/assets-freezer/src/mock.rs index 5e04dfe8e2b9..ec1ba9964167 100644 --- a/substrate/frame/assets-freezer/src/mock.rs +++ b/substrate/frame/assets-freezer/src/mock.rs @@ -105,6 +105,7 @@ impl pallet_assets::Config for Test { type RemoveItemsLimit = ConstU32<10>; type CallbackHandle = (); type Currency = Balances; + type Holder = (); type Freezer = AssetsFreezer; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/substrate/frame/assets-freezer/src/tests.rs b/substrate/frame/assets-freezer/src/tests.rs index 4f2dea79c705..cee14a90d16a 100644 --- a/substrate/frame/assets-freezer/src/tests.rs +++ b/substrate/frame/assets-freezer/src/tests.rs @@ -168,7 +168,7 @@ mod impl_mutate_freeze { Preservation::Preserve, Fortitude::Polite, ), - 89 + 90 ); System::assert_last_event( Event::::Frozen { asset_id: ASSET_ID, who: WHO, amount: 10 }.into(), @@ -186,7 +186,7 @@ mod impl_mutate_freeze { Preservation::Preserve, Fortitude::Polite, ), - 91 + 92 ); System::assert_last_event( Event::::Thawed { asset_id: ASSET_ID, who: WHO, amount: 2 }.into(), @@ -219,7 +219,7 @@ mod impl_mutate_freeze { Preservation::Preserve, Fortitude::Polite, ), - 89 + 90 ); assert_ok!(AssetsFreezer::extend_freeze( ASSET_ID, @@ -237,7 +237,7 @@ mod impl_mutate_freeze { Preservation::Preserve, Fortitude::Polite, ), - 88 + 89 ); }); } @@ -261,7 +261,7 @@ mod impl_mutate_freeze { Preservation::Preserve, Fortitude::Polite, ), - 89 + 90 ); assert_ok!(AssetsFreezer::thaw(ASSET_ID, &DummyFreezeReason::Governance, &WHO)); System::assert_has_event( @@ -295,10 +295,10 @@ mod with_pallet_assets { 20 )); assert_noop!( - Assets::transfer(RuntimeOrigin::signed(WHO), Compact(ASSET_ID), 2, 80), + Assets::transfer(RuntimeOrigin::signed(WHO), Compact(ASSET_ID), 2, 81), pallet_assets::Error::::BalanceLow, ); - assert_ok!(Assets::transfer(RuntimeOrigin::signed(WHO), Compact(ASSET_ID), 2, 79)); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(WHO), Compact(ASSET_ID), 2, 80)); }); } } From 32fa5c1c2d40453a16824f41a413a55300cbd221 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 31 Jul 2024 22:27:16 -0400 Subject: [PATCH 09/47] feat(pallet-assets-holder): pallet implementation --- Cargo.lock | 23 +- Cargo.toml | 1 + substrate/frame/assets-holder/Cargo.toml | 63 +++ substrate/frame/assets-holder/src/impls.rs | 253 +++++++++++ substrate/frame/assets-holder/src/lib.rs | 168 ++++++++ substrate/frame/assets-holder/src/mock.rs | 156 +++++++ substrate/frame/assets-holder/src/tests.rs | 480 +++++++++++++++++++++ 7 files changed, 1140 insertions(+), 4 deletions(-) create mode 100644 substrate/frame/assets-holder/Cargo.toml create mode 100644 substrate/frame/assets-holder/src/impls.rs create mode 100644 substrate/frame/assets-holder/src/lib.rs create mode 100644 substrate/frame/assets-holder/src/mock.rs create mode 100644 substrate/frame/assets-holder/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index f38149b1806e..0512f96be22b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5230,11 +5230,8 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ - "atty", - "humantime", "log", "regex", - "termcolor", ] [[package]] @@ -9778,7 +9775,7 @@ dependencies = [ name = "pallet-assets" version = "29.1.0" dependencies = [ - "env_logger 0.8.4", + "env_logger 0.11.3", "frame-benchmarking", "frame-support", "frame-system", @@ -9810,6 +9807,24 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-assets-holder" +version = "0.1.0" +dependencies = [ + "env_logger 0.11.3", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-assets", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-atomic-swap" version = "28.0.0" diff --git a/Cargo.toml b/Cargo.toml index 5c2677fffeb2..a1caf537deb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -306,6 +306,7 @@ members = [ "substrate/frame/asset-conversion/ops", "substrate/frame/asset-rate", "substrate/frame/assets", + "substrate/frame/assets-holder", "substrate/frame/assets-freezer", "substrate/frame/atomic-swap", "substrate/frame/aura", diff --git a/substrate/frame/assets-holder/Cargo.toml b/substrate/frame/assets-holder/Cargo.toml new file mode 100644 index 000000000000..a2501725d3c9 --- /dev/null +++ b/substrate/frame/assets-holder/Cargo.toml @@ -0,0 +1,63 @@ +[package] +authors.workspace = true +description = "Provides holding features to `pallet-assets`" +edition.workspace = true +homepage = "https://substrate.io" +license = "MIT-0" +name = "pallet-assets-holder" +repository.workspace = true +version = "0.1.0" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +log = {workspace = true} + +codec = {workspace = true} +frame-benchmarking = {optional = true, workspace = true} +frame-support = {workspace = true} +frame-system = {workspace = true} +pallet-assets = {workspace = true} +scale-info = {features = ["derive"], workspace = true} +sp-runtime = {workspace = true} + +[dev-dependencies] +env_logger = {workspace = true} +pallet-balances = {workspace = true} +sp-core = {workspace = true} +sp-io = {workspace = true} + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-assets/std", + "pallet-balances/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/assets-holder/src/impls.rs b/substrate/frame/assets-holder/src/impls.rs new file mode 100644 index 000000000000..6266b41c22aa --- /dev/null +++ b/substrate/frame/assets-holder/src/impls.rs @@ -0,0 +1,253 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; + +use frame_support::traits::{ + fungibles::{Dust, Inspect, InspectHold, MutateHold, Unbalanced, UnbalancedHold}, + tokens::{ + DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence, + }, +}; +use pallet_assets::HeldBalance; +use sp_runtime::{ + traits::{CheckedAdd, CheckedSub, Zero}, + ArithmeticError, +}; + +// Implements [`HeldBalance`] from [`pallet-assets`], so it can understand whether there's a held +// amount for an asset account, and is able to signal to this pallet when to clear the state of an +// account. +impl, I: 'static> HeldBalance for Pallet { + fn held_balance(asset: T::AssetId, who: &T::AccountId) -> Option { + HeldBalances::::get(asset, who) + } + + fn died(asset: T::AssetId, who: &T::AccountId) { + HeldBalances::::remove(asset.clone(), who); + Holds::::remove(asset, who); + } +} + +// Implement [`fungibles::Inspect`](frame_support::traits::fungibles::Inspect) as it is bound by +// [`fungibles::InspectHold`](frame_support::traits::fungibles::InspectHold) and +// [`fungibles::MutateHold`](frame_support::traits::fungibles::MutateHold). To do so, we'll +// re-export all of `pallet-assets` implementation of the same trait. +impl, I: 'static> Inspect for Pallet { + type AssetId = T::AssetId; + type Balance = T::Balance; + + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + pallet_assets::Pallet::::total_issuance(asset) + } + + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + pallet_assets::Pallet::::minimum_balance(asset) + } + + fn total_balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { + pallet_assets::Pallet::::total_balance(asset, who) + } + + fn balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { + pallet_assets::Pallet::::balance(asset, who) + } + + fn reducible_balance( + asset: Self::AssetId, + who: &T::AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance { + pallet_assets::Pallet::::reducible_balance(asset, who, preservation, force) + } + + fn can_deposit( + asset: Self::AssetId, + who: &T::AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { + pallet_assets::Pallet::::can_deposit(asset, who, amount, provenance) + } + + fn can_withdraw( + asset: Self::AssetId, + who: &T::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + pallet_assets::Pallet::::can_withdraw(asset, who, amount) + } + + fn asset_exists(asset: Self::AssetId) -> bool { + pallet_assets::Pallet::::asset_exists(asset) + } +} + +impl, I: 'static> InspectHold for Pallet { + type Reason = T::RuntimeHoldReason; + + fn total_balance_on_hold(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { + HeldBalances::::get(asset, who).unwrap_or_else(Zero::zero) + } + + fn balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &T::AccountId, + ) -> Self::Balance { + Holds::::get(asset, who) + .iter() + .find(|x| &x.id == reason) + .map(|x| x.amount) + .unwrap_or_else(Zero::zero) + } +} + +impl, I: 'static> Unbalanced for Pallet { + fn handle_dust(dust: Dust) { + let Dust(id, balance) = dust; + pallet_assets::Pallet::::handle_dust(Dust(id, balance)); + } + + fn write_balance( + asset: Self::AssetId, + who: &T::AccountId, + amount: Self::Balance, + ) -> Result, DispatchError> { + pallet_assets::Pallet::::write_balance(asset, who, amount) + } + + fn set_total_issuance(asset: Self::AssetId, amount: Self::Balance) { + pallet_assets::Pallet::::set_total_issuance(asset, amount) + } + + fn decrease_balance( + asset: Self::AssetId, + who: &T::AccountId, + amount: Self::Balance, + precision: Precision, + preservation: Preservation, + force: Fortitude, + ) -> Result { + pallet_assets::Pallet::::decrease_balance( + asset, + who, + amount, + precision, + preservation, + force, + ) + } + + fn increase_balance( + asset: Self::AssetId, + who: &T::AccountId, + amount: Self::Balance, + precision: Precision, + ) -> Result { + pallet_assets::Pallet::::increase_balance(asset, who, amount, precision) + } +} + +impl, I: 'static> UnbalancedHold for Pallet { + fn set_balance_on_hold( + asset: Self::AssetId, + reason: &Self::Reason, + who: &T::AccountId, + amount: Self::Balance, + ) -> DispatchResult { + let mut holds = Holds::::get(asset.clone(), who); + let mut increase = true; + let mut delta = amount; + + if let Some(item) = holds.iter_mut().find(|x| &x.id == reason) { + delta = item.amount.max(amount) - item.amount.min(amount); + increase = amount > item.amount; + item.amount = amount; + holds.retain(|x| !x.amount.is_zero()); + } else { + if !amount.is_zero() { + holds + .try_push(IdAmount { id: *reason, amount }) + .map_err(|_| Error::::TooManyHolds)?; + } + } + + let held_amount = HeldBalances::::get(asset.clone(), who).unwrap_or_else(Zero::zero); + + let held_amount = if increase { + held_amount.checked_add(&delta).ok_or(ArithmeticError::Overflow)? + } else { + held_amount.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? + }; + + if held_amount.is_zero() { + HeldBalances::::remove(asset.clone(), who); + } else { + HeldBalances::::insert(asset.clone(), who, held_amount); + } + + Holds::::insert(asset, who, holds); + + Ok(()) + } +} + +impl, I: 'static> MutateHold for Pallet { + fn done_hold( + asset_id: Self::AssetId, + reason: &Self::Reason, + who: &T::AccountId, + amount: Self::Balance, + ) { + Self::deposit_event(Event::::Held { + asset_id, + who: who.clone(), + reason: *reason, + amount, + }); + } + + fn done_release( + asset_id: Self::AssetId, + reason: &Self::Reason, + who: &T::AccountId, + amount: Self::Balance, + ) { + Self::deposit_event(Event::::Released { + asset_id, + who: who.clone(), + reason: *reason, + amount, + }); + } + + fn done_burn_held( + asset_id: Self::AssetId, + reason: &Self::Reason, + who: &T::AccountId, + amount: Self::Balance, + ) { + Self::deposit_event(Event::::Slashed { + asset_id, + who: who.clone(), + reason: *reason, + amount, + }); + } +} diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs new file mode 100644 index 000000000000..b50e6035f596 --- /dev/null +++ b/substrate/frame/assets-holder/src/lib.rs @@ -0,0 +1,168 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Assets Holder Pallet +//! +//! A pallet capable of holding fungibles from `pallet-assets`. This is an extension of +//! `pallet-assets`, wrapping [`fungibles::Inspect`](`frame_support::traits::fungibles::Inspect`). +//! It implements both +//! [`fungibles::hold::Inspect`](frame_support::traits::fungibles::hold::Inspect), +//! [`fungibles::hold::Mutate`](frame_support::traits::fungibles::hold::Mutate), and especially +//! [`fungibles::hold::Unbalanced`](frame_support::traits::fungibles::hold::Unbalanced). The +//! complexity of the operations is `O(1)`. +//! +//! ## Pallet API +//! +//! See the [`pallet`] module for more information about the interfaces this pallet exposes, +//! including its configuration trait, dispatchables, storage items, events and errors. +//! +//! ## Overview +//! +//! This pallet provides the following functionality: +//! +//! - Pallet hooks allowing [`pallet-assets`] to know the held balance for an account on a given +//! asset (see [`pallet_assets::HeldBalance`]). +//! - An implementation of +//! [`fungibles::hold::Inspect`](frame_support::traits::fungibles::hold::Inspect), +//! [`fungibles::hold::Mutate`](frame_support::traits::fungibles::hold::Mutate) and +//! [`fungibles::hold::Unbalanced`](frame_support::traits::fungibles::hold::Unbalanced), allowing +//! other pallets to manage holds for the `pallet-assets` assets. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + pallet_prelude::*, + traits::{tokens::IdAmount, VariantCount, VariantCountOf}, + BoundedVec, +}; +use frame_system::pallet_prelude::BlockNumberFor; + +pub use pallet::*; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +mod impls; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config(with_default)] + pub trait Config: frame_system::Config + pallet_assets::Config { + /// The overarching freeze reason. + #[pallet::no_default_bounds] + type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount; + + /// The overarching event type. + #[pallet::no_default_bounds] + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + } + + #[pallet::error] + pub enum Error { + /// Number of holds on an account would exceed the count of `RuntimeHoldReason`. + TooManyHolds, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + // `who`s held balance was increased by `amount`. + Held { + who: T::AccountId, + asset_id: T::AssetId, + reason: T::RuntimeHoldReason, + amount: T::Balance, + }, + // `who`s held balance was decreased by `amount`. + Released { + who: T::AccountId, + asset_id: T::AssetId, + reason: T::RuntimeHoldReason, + amount: T::Balance, + }, + // `who`s held balance was burned by `amount`. + Slashed { + who: T::AccountId, + asset_id: T::AssetId, + reason: T::RuntimeHoldReason, + amount: T::Balance, + }, + } + + /// A map that stores holds applied on an account for a given AssetId. + #[pallet::storage] + pub(super) type Holds, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::AssetId, + Blake2_128Concat, + T::AccountId, + BoundedVec< + IdAmount, + VariantCountOf, + >, + ValueQuery, + >; + + /// A map that stores the current total held balance for every account on a given AssetId. + #[pallet::storage] + pub(super) type HeldBalances, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::AssetId, + Blake2_128Concat, + T::AccountId, + T::Balance, + >; + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() + } + } +} + +impl, I: 'static> Pallet { + #[cfg(any(test, feature = "try-runtime"))] + fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + use sp_runtime::traits::{Saturating, Zero}; + + for (asset, who, _) in HeldBalances::::iter() { + let held_amount: T::Balance = Holds::::get(asset.clone(), who.clone()) + .iter() + .map(|l| l.amount) + .fold(Zero::zero(), |prev, amount| prev.saturating_add(amount)); + + frame_support::ensure!( + HeldBalances::::get(asset, who).unwrap_or_else(Zero::zero) == held_amount, + "The `HeldAmount` is not equal to the sum of `Holds` for (`asset`, `who`)" + ); + } + + Ok(()) + } +} diff --git a/substrate/frame/assets-holder/src/mock.rs b/substrate/frame/assets-holder/src/mock.rs new file mode 100644 index 000000000000..be9df18518b4 --- /dev/null +++ b/substrate/frame/assets-holder/src/mock.rs @@ -0,0 +1,156 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests mock for `pallet-assets-freezer`. + +use crate as pallet_assets_holder; +pub use crate::*; +use codec::{Compact, Decode, Encode, MaxEncodedLen}; +use frame_support::{ + derive_impl, + traits::{AsEnsureOriginWithArg, ConstU64}, +}; +use scale_info::TypeInfo; +use sp_core::{ConstU32, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +pub type AccountId = u64; +pub type Balance = u64; +pub type AssetId = u32; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Assets: pallet_assets, + AssetsHolder: pallet_assets_holder, + Balances: pallet_balances, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type Hash = H256; + type RuntimeCall = RuntimeCall; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +impl pallet_assets::Config for Test { + type AssetId = AssetId; + type AssetIdParameter = Compact; + type AssetDeposit = ConstU64<1>; + type Balance = Balance; + type AssetAccountDeposit = ConstU64<1>; + type MetadataDepositBase = (); + type MetadataDepositPerByte = (); + type ApprovalDeposit = (); + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type StringLimit = ConstU32<32>; + type Extra = (); + type RemoveItemsLimit = ConstU32<10>; + type CallbackHandle = (); + type Currency = Balances; + type Holder = AssetsHolder; + type Freezer = (); + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +#[derive( + Decode, Encode, MaxEncodedLen, PartialEq, Eq, Ord, PartialOrd, TypeInfo, Debug, Clone, Copy, +)] +pub enum DummyHoldReason { + Governance, + Staking, + Other, +} + +impl VariantCount for DummyHoldReason { + // Intentionally set below the actual count of variants, to allow testing for `can_freeze` + const VARIANT_COUNT: u32 = 2; +} + +impl Config for Test { + type RuntimeHoldReason = DummyHoldReason; + type RuntimeEvent = RuntimeEvent; +} + +pub fn new_test_ext(execute: impl FnOnce()) -> sp_io::TestExternalities { + let t = RuntimeGenesisConfig { + assets: pallet_assets::GenesisConfig { + assets: vec![(1, 0, true, 1)], + metadata: vec![], + accounts: vec![(1, 1, 100)], + next_asset_id: None, + }, + system: Default::default(), + balances: Default::default(), + } + .build_storage() + .unwrap(); + let mut ext: sp_io::TestExternalities = t.into(); + ext.execute_with(|| { + System::set_block_number(1); + execute(); + frame_support::assert_ok!(AssetsHolder::do_try_state()); + }); + + ext +} diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs new file mode 100644 index 000000000000..1f8addd4650a --- /dev/null +++ b/substrate/frame/assets-holder/src/tests.rs @@ -0,0 +1,480 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for pallet-assets-holder. + +use crate::mock::*; + +use frame_support::{ + assert_noop, assert_ok, + traits::tokens::fungibles::{Inspect, InspectHold, MutateHold, UnbalancedHold}, +}; +use pallet_assets::HeldBalance; + +const WHO: AccountId = 1; +const ASSET_ID: AssetId = 1; + +fn test_hold(id: DummyHoldReason, amount: Balance) { + assert_ok!(AssetsHolder::set_balance_on_hold(ASSET_ID, &id, &WHO, amount)); +} + +fn test_release(id: DummyHoldReason) { + assert_ok!(AssetsHolder::set_balance_on_hold(ASSET_ID, &id, &WHO, 0)); +} + +mod impl_held_balance { + use super::*; + + #[test] + fn held_balance_works() { + let _ = env_logger::init(); + new_test_ext(|| { + assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), None); + test_hold(DummyHoldReason::Governance, 1); + assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(1u64)); + test_hold(DummyHoldReason::Staking, 3); + assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(4u64)); + test_hold(DummyHoldReason::Governance, 2); + assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(5u64)); + // also test releasing works to reduce a balance, and finally releasing everything + // resets to None + test_release(DummyHoldReason::Governance); + assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(3u64)); + test_release(DummyHoldReason::Staking); + assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), None); + }); + } + + #[test] + fn died_works() { + new_test_ext(|| { + test_hold(DummyHoldReason::Governance, 1); + AssetsHolder::died(ASSET_ID, &WHO); + assert!(HeldBalances::::get(ASSET_ID, WHO).is_none()); + assert!(Holds::::get(ASSET_ID, WHO).is_empty()); + }); + } +} + +mod impl_hold_inspect { + use super::*; + + #[test] + fn total_balance_on_hold_works() { + new_test_ext(|| { + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 0u64); + test_hold(DummyHoldReason::Governance, 1); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 1u64); + test_hold(DummyHoldReason::Staking, 3); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 4u64); + test_hold(DummyHoldReason::Governance, 2); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 5u64); + // also test release to reduce a balance, and finally releasing everything resets to + // 0 + test_release(DummyHoldReason::Governance); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 3u64); + test_release(DummyHoldReason::Staking); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 0u64); + }); + } + + #[test] + fn balance_on_hold_works() { + new_test_ext(|| { + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + 0u64 + ); + test_hold(DummyHoldReason::Governance, 1); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + 1u64 + ); + test_hold(DummyHoldReason::Staking, 3); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + 3u64 + ); + test_hold(DummyHoldReason::Staking, 2); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + 2u64 + ); + // also test release to reduce a balance, and finally releasing everything resets to + // 0 + test_release(DummyHoldReason::Governance); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + 0u64 + ); + test_release(DummyHoldReason::Staking); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + 0u64 + ); + }); + } +} + +mod impl_hold_unbalanced { + use super::*; + + // Note: Tests for `handle_dust`, `write_balance`, `set_total_issuance`, `decrease_balance` + // and `increase_balance` are intentionally left out without testing, since: + // 1. It is expected these methods are tested within `pallet-assets`, and + // 2. There are no valid cases that can be directly asserted using those methods in + // the scope of this pallet. + + #[test] + fn set_balance_on_hold_works() { + new_test_ext(|| { + assert_eq!(Holds::::get(ASSET_ID, WHO).to_vec(), vec![]); + assert_eq!(HeldBalances::::get(ASSET_ID, WHO), None); + // Adding held balance works + assert_ok!(AssetsHolder::set_balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 1 + )); + assert_eq!( + Holds::::get(ASSET_ID, WHO).to_vec(), + vec![IdAmount { id: DummyHoldReason::Governance, amount: 1 }] + ); + assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(1)); + // Increasing hold works + assert_ok!(AssetsHolder::set_balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 3 + )); + assert_eq!( + Holds::::get(ASSET_ID, WHO).to_vec(), + vec![IdAmount { id: DummyHoldReason::Governance, amount: 3 }] + ); + assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(3)); + // Adding new held balance works + assert_ok!(AssetsHolder::set_balance_on_hold( + ASSET_ID, + &DummyHoldReason::Staking, + &WHO, + 2 + )); + assert_eq!( + Holds::::get(ASSET_ID, WHO).to_vec(), + vec![ + IdAmount { id: DummyHoldReason::Governance, amount: 3 }, + IdAmount { id: DummyHoldReason::Staking, amount: 2 } + ] + ); + assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(5)); + // Adding more than max holds fails + assert_noop!( + AssetsHolder::set_balance_on_hold(ASSET_ID, &DummyHoldReason::Other, &WHO, 1), + Error::::TooManyHolds + ); + // Decreasing held balance works + assert_ok!(AssetsHolder::set_balance_on_hold( + ASSET_ID, + &DummyHoldReason::Staking, + &WHO, + 1 + )); + assert_eq!( + Holds::::get(ASSET_ID, WHO).to_vec(), + vec![ + IdAmount { id: DummyHoldReason::Governance, amount: 3 }, + IdAmount { id: DummyHoldReason::Staking, amount: 1 } + ] + ); + assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(4)); + // Decreasing until removal of held balance works + assert_ok!(AssetsHolder::set_balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 0 + )); + assert_eq!( + Holds::::get(ASSET_ID, WHO).to_vec(), + vec![IdAmount { id: DummyHoldReason::Staking, amount: 1 }] + ); + assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(1)); + // Clearing ol all holds works + assert_ok!(AssetsHolder::set_balance_on_hold( + ASSET_ID, + &DummyHoldReason::Staking, + &WHO, + 0 + )); + assert_eq!(Holds::::get(ASSET_ID, WHO).to_vec(), vec![]); + assert_eq!(HeldBalances::::get(ASSET_ID, WHO), None); + }); + } +} + +mod impl_hold_mutate { + use super::*; + use frame_support::traits::tokens::{Fortitude, Precision, Preservation}; + use sp_runtime::TokenError; + + #[test] + fn hold_works() { + super::new_test_ext(|| { + // Holding some `amount` would decrease the asset account balance and change the + // reducible balance, while total issuance is preserved. + assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 10)); + assert_eq!(Assets::balance(ASSET_ID, &WHO), 90); + // Reducible balance is tested once to ensure token balance model is compliant. + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Expendable, + Fortitude::Force + ), + 89 + ); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + 10 + ); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 10); + assert_eq!(Assets::total_issuance(ASSET_ID), 100); + + // Increasing the amount held for the same reason has the same effect as described above + // in `set_balance_on_hold_works`, while total issuance is preserved. + // Consideration: holding for an amount `x` will increase the already held amount by + // `x`. + assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 20)); + assert_eq!(Assets::balance(ASSET_ID, &WHO), 70); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + 30 + ); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 30); + assert_eq!(Assets::total_issuance(ASSET_ID), 100); + + // Holding some amount for a different reason has the same effect as described above in + // `set_balance_on_hold_works`, while total issuance is preserved. + assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Staking, &WHO, 20)); + assert_eq!(Assets::balance(ASSET_ID, &WHO), 50); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + 20 + ); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 50); + assert_eq!(Assets::total_issuance(ASSET_ID), 100); + }); + } + + fn new_test_ext() -> sp_io::TestExternalities { + super::new_test_ext(|| { + assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 30)); + assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Staking, &WHO, 20)); + }) + } + + #[test] + fn release_works() { + // Releasing up to some amount will increase the balance by the released + // amount, while preserving total issuance. + new_test_ext().execute_with(|| { + assert_ok!(AssetsHolder::release( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 20, + Precision::Exact, + )); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + 10 + ); + assert_eq!(Assets::balance(ASSET_ID, WHO), 70); + }); + + // Releasing over the max held amount with `BestEffort` will increase the + // balance by the previously held amount, while preserving total issuance. + new_test_ext().execute_with(|| { + assert_ok!(AssetsHolder::release( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 31, + Precision::BestEffort, + )); + assert_eq!( + AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + 0 + ); + assert_eq!(Assets::balance(ASSET_ID, WHO), 80); + }); + + // Releasing over the max held amount with `Exact` will fail. + new_test_ext().execute_with(|| { + assert_noop!( + AssetsHolder::release( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 31, + Precision::Exact, + ), + TokenError::FundsUnavailable + ); + }); + } + + #[test] + fn burn_held_works() { + // Burning works, reducing total issuance. + new_test_ext().execute_with(|| { + assert_ok!(AssetsHolder::burn_held( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 1, + Precision::BestEffort, + Fortitude::Polite + )); + assert_eq!(Assets::total_issuance(ASSET_ID), 99); + }); + + // Burning by an amount up to the held balance with `Exact` works, reducing held balance up + // to the given amount. + new_test_ext().execute_with(|| { + assert_ok!(AssetsHolder::burn_held( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 10, + Precision::Exact, + Fortitude::Polite + )); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 40); + assert_eq!(Assets::balance(ASSET_ID, WHO), 50); + }); + + // Burning by an amount over the held balance with `BestEffort` works, reducing held balance + // up to the given amount. + new_test_ext().execute_with(|| { + assert_ok!(AssetsHolder::burn_held( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 31, + Precision::BestEffort, + Fortitude::Polite + )); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 20); + assert_eq!(Assets::balance(ASSET_ID, WHO), 50); + }); + + // Burning by an amount over the held balance with `Exact` fails. + new_test_ext().execute_with(|| { + assert_noop!( + AssetsHolder::burn_held( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 31, + Precision::Exact, + Fortitude::Polite + ), + TokenError::FundsUnavailable + ); + }); + } + + #[test] + fn burn_all_held_works() { + new_test_ext().execute_with(|| { + // Burning all held works as burning giving the held balance + // as amount with `BestEffort` + assert_ok!(AssetsHolder::burn_all_held( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + Precision::BestEffort, + Fortitude::Polite, + )); + assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 20); + assert_eq!(Assets::balance(ASSET_ID, WHO), 50); + }); + } + + #[test] + fn done_held_works() { + new_test_ext().execute_with(|| { + System::assert_has_event( + Event::::Held { + who: WHO, + asset_id: ASSET_ID, + reason: DummyHoldReason::Governance, + amount: 30, + } + .into(), + ); + }); + } + + #[test] + fn done_release_works() { + new_test_ext().execute_with(|| { + assert_ok!(AssetsHolder::release( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + 31, + Precision::BestEffort + )); + System::assert_has_event( + Event::::Released { + who: WHO, + asset_id: ASSET_ID, + reason: DummyHoldReason::Governance, + amount: 30, + } + .into(), + ); + }); + } + + #[test] + fn done_burn_held_works() { + new_test_ext().execute_with(|| { + assert_ok!(AssetsHolder::burn_all_held( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO, + Precision::BestEffort, + Fortitude::Polite, + )); + System::assert_has_event( + Event::::Slashed { + who: WHO, + asset_id: ASSET_ID, + reason: DummyHoldReason::Governance, + amount: 30, + } + .into(), + ); + }); + } +} From b93337ec1858421e6dec26ff71270bab9514f7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 31 Jul 2024 22:44:49 -0400 Subject: [PATCH 10/47] fix: fmt / cargo fmt --- Cargo.lock | 3 +- Cargo.toml | 3 +- .../assets/asset-hub-rococo/src/lib.rs | 3 +- .../assets/asset-hub-westend/src/lib.rs | 3 +- substrate/frame/assets-holder/Cargo.toml | 80 +++++++++---------- substrate/frame/assets-holder/src/tests.rs | 1 - substrate/frame/assets/Cargo.toml | 25 +++--- 7 files changed, 56 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06d8a661c26c..c7a72f4ba82c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9846,7 +9846,6 @@ dependencies = [ name = "pallet-assets" version = "29.1.0" dependencies = [ - "env_logger 0.11.3", "frame-benchmarking", "frame-support", "frame-system", @@ -9882,7 +9881,6 @@ dependencies = [ name = "pallet-assets-holder" version = "0.1.0" dependencies = [ - "env_logger 0.11.3", "frame-benchmarking", "frame-support", "frame-system", @@ -14260,6 +14258,7 @@ dependencies = [ "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", + "pallet-assets-holder", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", diff --git a/Cargo.toml b/Cargo.toml index fbead04d7385..c566967c2ece 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -311,8 +311,8 @@ members = [ "substrate/frame/asset-conversion/ops", "substrate/frame/asset-rate", "substrate/frame/assets", - "substrate/frame/assets-holder", "substrate/frame/assets-freezer", + "substrate/frame/assets-holder", "substrate/frame/atomic-swap", "substrate/frame/aura", "substrate/frame/authority-discovery", @@ -874,6 +874,7 @@ pallet-asset-rate = { path = "substrate/frame/asset-rate", default-features = fa pallet-asset-tx-payment = { path = "substrate/frame/transaction-payment/asset-tx-payment", default-features = false } pallet-assets = { path = "substrate/frame/assets", default-features = false } pallet-assets-freezer = { path = "substrate/frame/assets-freezer", default-features = false } +pallet-assets-holder = { path = "substrate/frame/assets-holder", default-features = false } pallet-atomic-swap = { default-features = false, path = "substrate/frame/atomic-swap" } pallet-aura = { path = "substrate/frame/aura", default-features = false } pallet-authority-discovery = { path = "substrate/frame/authority-discovery", default-features = false } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index c0427da22589..c9fee4626fac 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -545,8 +545,7 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } | RuntimeCall::NftFractionalization { .. } | - RuntimeCall::Nfts { .. } | - RuntimeCall::Uniques { .. } + RuntimeCall::Nfts { .. } | RuntimeCall::Uniques { .. } ) }, ProxyType::AssetOwner => matches!( diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 6cdc038b1df9..f3d3b29a23a8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -539,8 +539,7 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } | RuntimeCall::NftFractionalization { .. } | - RuntimeCall::Nfts { .. } | - RuntimeCall::Uniques { .. } + RuntimeCall::Nfts { .. } | RuntimeCall::Uniques { .. } ) }, ProxyType::AssetOwner => matches!( diff --git a/substrate/frame/assets-holder/Cargo.toml b/substrate/frame/assets-holder/Cargo.toml index a2501725d3c9..7e3aed6e17e9 100644 --- a/substrate/frame/assets-holder/Cargo.toml +++ b/substrate/frame/assets-holder/Cargo.toml @@ -1,12 +1,12 @@ [package] +name = "pallet-assets-holder" +version = "0.1.0" authors.workspace = true -description = "Provides holding features to `pallet-assets`" edition.workspace = true -homepage = "https://substrate.io" license = "MIT-0" -name = "pallet-assets-holder" +homepage.workspace = true repository.workspace = true -version = "0.1.0" +description = "Provides holding features to `pallet-assets`" [lints] workspace = true @@ -15,49 +15,47 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = {workspace = true} - -codec = {workspace = true} -frame-benchmarking = {optional = true, workspace = true} -frame-support = {workspace = true} -frame-system = {workspace = true} -pallet-assets = {workspace = true} -scale-info = {features = ["derive"], workspace = true} -sp-runtime = {workspace = true} +codec = { workspace = true } +log = { workspace = true } +scale-info = { features = ["derive"], workspace = true } +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-assets = { workspace = true } +sp-runtime = { workspace = true } [dev-dependencies] -env_logger = {workspace = true} -pallet-balances = {workspace = true} -sp-core = {workspace = true} -sp-io = {workspace = true} +sp-io = { workspace = true } +sp-core = { workspace = true } +pallet-balances = { workspace = true } [features] default = ["std"] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-assets/std", - "pallet-balances/std", - "scale-info/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-assets/std", + "pallet-balances/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-assets/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", ] diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index 1f8addd4650a..c4836647bb29 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -41,7 +41,6 @@ mod impl_held_balance { #[test] fn held_balance_works() { - let _ = env_logger::init(); new_test_ext(|| { assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), None); test_hold(DummyHoldReason::Governance, 1); diff --git a/substrate/frame/assets/Cargo.toml b/substrate/frame/assets/Cargo.toml index b93c224927b0..fcb151298f09 100644 --- a/substrate/frame/assets/Cargo.toml +++ b/substrate/frame/assets/Cargo.toml @@ -1,13 +1,13 @@ [package] +name = "pallet-assets" +version = "29.1.0" authors.workspace = true edition.workspace = true +license = "Apache-2.0" homepage.workspace = true repository.workspace = true description = "FRAME asset management pallet" -license = "Apache-2.0" -name = "pallet-assets" readme = "README.md" -version = "29.1.0" [lints] workspace = true @@ -30,7 +30,6 @@ frame-benchmarking = { optional = true, workspace = true } sp-core = { workspace = true } [dev-dependencies] -env_logger = { workspace = true } sp-std = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } pallet-balances = { workspace = true, default-features = true } @@ -50,15 +49,15 @@ std = [ "sp-runtime/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", ] From 4e0ced776b78551a720a61f5a1e5553c009fc3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 31 Jul 2024 22:44:53 -0400 Subject: [PATCH 11/47] chore: umbrella --- umbrella/Cargo.toml | 9 +++++++++ umbrella/src/lib.rs | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 8d85e26d8fe7..0bde9e518dbf 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -69,6 +69,7 @@ std = [ "pallet-asset-rate?/std", "pallet-asset-tx-payment?/std", "pallet-assets-freezer?/std", + "pallet-assets-holder?/std", "pallet-assets?/std", "pallet-atomic-swap?/std", "pallet-aura?/std", @@ -265,6 +266,7 @@ runtime-benchmarks = [ "pallet-asset-rate?/runtime-benchmarks", "pallet-asset-tx-payment?/runtime-benchmarks", "pallet-assets-freezer?/runtime-benchmarks", + "pallet-assets-holder?/runtime-benchmarks", "pallet-assets?/runtime-benchmarks", "pallet-babe?/runtime-benchmarks", "pallet-bags-list?/runtime-benchmarks", @@ -389,6 +391,7 @@ try-runtime = [ "pallet-asset-rate?/try-runtime", "pallet-asset-tx-payment?/try-runtime", "pallet-assets-freezer?/try-runtime", + "pallet-assets-holder?/try-runtime", "pallet-assets?/try-runtime", "pallet-atomic-swap?/try-runtime", "pallet-aura?/try-runtime", @@ -603,6 +606,7 @@ runtime = [ "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", + "pallet-assets-holder", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", @@ -1103,6 +1107,11 @@ path = "../substrate/frame/assets-freezer" default-features = false optional = true +[dependencies.pallet-assets-holder] +path = "../substrate/frame/assets-holder" +default-features = false +optional = true + [dependencies.pallet-atomic-swap] path = "../substrate/frame/atomic-swap" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 58a5691961d9..473c6114a5ac 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -364,6 +364,10 @@ pub use pallet_assets; #[cfg(feature = "pallet-assets-freezer")] pub use pallet_assets_freezer; +/// Provides holding features to `pallet-assets`. +#[cfg(feature = "pallet-assets-holder")] +pub use pallet_assets_holder; + /// FRAME atomic swap pallet. #[cfg(feature = "pallet-atomic-swap")] pub use pallet_atomic_swap; From c5b103a2601923a42c34d76a07e9b78e4772efd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 31 Jul 2024 23:10:30 -0400 Subject: [PATCH 12/47] chore: pr doc --- prdoc/pr_4530.prdoc | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 prdoc/pr_4530.prdoc diff --git a/prdoc/pr_4530.prdoc b/prdoc/pr_4530.prdoc new file mode 100644 index 000000000000..64f59d69ad4b --- /dev/null +++ b/prdoc/pr_4530.prdoc @@ -0,0 +1,37 @@ +title: "Implement `pallet-assets-holder`" + +doc: + - audience: Runtime Dev + description: | + This change creates the `pallet-assets-holder` pallet, as well as changes `pallet-assets` + to support querying held balances via a new trait: `HeldBalance`. + + It also adjusts the balance model implementation for fungible + sets (see [sdk docs](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/tokens/fungible/index.html#visualising-balance-components-together-)). + + The `pallet-assets-holder` implements `hold` traits for `pallet-assets`, by extending this + pallet and implementing the `HeldBalance` trait so the held balance can be queried by + `pallet-assets` to calculate the reducible (a.k.a. spendable) balance. + + These changes imply adding a configuration type in `pallet-assets` for `Holder` + + ## Default implementation of `Holder` + + Use `()` as the default value, when no holding capabilities are wanted in the runtime + implementation. + + ## Enable `pallet-assets-holder` + + Define an instance of `pallet-assets-holder` (we'll call it `AssetsHolder`) and use + `AssetsHolder` as the value for `Holder`, when intend to use holding capabilities are + wanted in the runtime implementation. + +crates: + - name: pallet-assets + bump: major + - name: pallet-assets-holder + bump: major + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor From f36b5402ab7a9a7ea6eeeb2eaafdc7f1360364fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 7 Aug 2024 21:00:06 -0500 Subject: [PATCH 13/47] fix(staging-xcm-builder): missing references to `Holder` Note: these mocks were introduced after the latest merge from `master`. --- .../xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs index 4d9809e84f88..91aa180fe1bd 100644 --- a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/mock.rs @@ -82,6 +82,7 @@ impl pallet_assets::Config for Runtime { type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; type Freezer = (); + type Holder = (); type CallbackHandle = (); } @@ -97,6 +98,7 @@ impl pallet_assets::Config for Runtime { type CreateOrigin = AsEnsureOriginWithArg>; type ForceOrigin = frame_system::EnsureRoot; type Freezer = (); + type Holder = (); type CallbackHandle = (); } From 5829558085d967a3ce629278e1b1819cebb1238e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 7 Aug 2024 21:00:24 -0500 Subject: [PATCH 14/47] change(pallet-assets): set `Holder` and `Freezer` to have default values, as per @gui1117's suggestion --- substrate/frame/assets/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index a77f0362ff76..aed4ed460bd4 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -295,6 +295,8 @@ pub mod pallet { type MetadataDepositPerByte = ConstU64<1>; type ApprovalDeposit = ConstU64<1>; type StringLimit = ConstU32<50>; + type Freezer = (); + type Holder = (); type Extra = (); type CallbackHandle = (); type WeightInfo = (); @@ -390,12 +392,10 @@ pub mod pallet { /// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be /// respected in all permissionless operations. - #[pallet::no_default] type Freezer: FrozenBalance; - /// A hook to allow a per-asset, per-account minimum balance to be enforced. This must be - /// respected in all permissionless operations. - #[pallet::no_default] + /// A hook to inspect a per-asset, per-account balance that is held. This goes in + /// accordance with balance model. type Holder: HeldBalance; /// Additional data to be stored with an account's asset balance. From 17ce1d92ba8c793e78d3dda65a880ff28657d39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 7 Aug 2024 21:00:39 -0500 Subject: [PATCH 15/47] change(pallet-assets-holder): apply @gui1117's suggestion of tightly coupling `pallet-assets-holder` with `pallet-assets` --- substrate/frame/assets-holder/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index b50e6035f596..c09b18b3a923 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -65,7 +65,9 @@ pub mod pallet { use super::*; #[pallet::config(with_default)] - pub trait Config: frame_system::Config + pallet_assets::Config { + pub trait Config: + frame_system::Config + pallet_assets::Config> + { /// The overarching freeze reason. #[pallet::no_default_bounds] type RuntimeHoldReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount; From f2a9e96297f61580f22f28be4c990a99f51f0776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 7 Aug 2024 22:34:03 -0500 Subject: [PATCH 16/47] change(pallet-assets): account for `held_balance` when calculating `total_balance`. Note: `transfer_all_works_3` changes, due to the change in the balance model --- substrate/frame/assets/src/impl_fungibles.rs | 3 ++- substrate/frame/assets/src/tests.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/substrate/frame/assets/src/impl_fungibles.rs b/substrate/frame/assets/src/impl_fungibles.rs index 578fa08c4e63..6ac4ef25e5c5 100644 --- a/substrate/frame/assets/src/impl_fungibles.rs +++ b/substrate/frame/assets/src/impl_fungibles.rs @@ -47,7 +47,8 @@ impl, I: 'static> fungibles::Inspect<::AccountId } fn total_balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { - Pallet::::balance(asset, who) + Pallet::::balance(asset.clone(), who) + .saturating_add(T::Holder::held_balance(asset, who).unwrap_or_default()) } fn reducible_balance( diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index c71cb261f19c..502f41f33bb8 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -837,8 +837,8 @@ fn transfer_all_works_3() { assert_ok!(Assets::mint(RuntimeOrigin::signed(0), 0, 2, 100)); // transfer all and allow death w/ frozen assert_ok!(Assets::transfer_all(Some(1).into(), 0, 2, false)); - assert_eq!(Assets::balance(0, &1), 110); - assert_eq!(Assets::balance(0, &2), 200); + assert_eq!(Assets::balance(0, &1), 100); + assert_eq!(Assets::balance(0, &2), 210); }); } From fc41634ea649013c772cbd96eee2123bc6d90f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 7 Aug 2024 22:47:06 -0500 Subject: [PATCH 17/47] change(pallet-assets-holder): test preservation of `total_balance`. --- substrate/frame/assets-holder/src/tests.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index c4836647bb29..12e7c4c92bf3 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -254,6 +254,9 @@ mod impl_hold_mutate { 10 ); assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 10); + // Holding preserves `total_balance` + assert_eq!(Assets::total_balance(ASSET_ID, &WHO), 100); + // Holding preserves `total_issuance` assert_eq!(Assets::total_issuance(ASSET_ID), 100); // Increasing the amount held for the same reason has the same effect as described above @@ -342,7 +345,7 @@ mod impl_hold_mutate { #[test] fn burn_held_works() { - // Burning works, reducing total issuance. + // Burning works, reducing total issuance and `total_balance`. new_test_ext().execute_with(|| { assert_ok!(AssetsHolder::burn_held( ASSET_ID, @@ -352,6 +355,7 @@ mod impl_hold_mutate { Precision::BestEffort, Fortitude::Polite )); + assert_eq!(Assets::total_balance(ASSET_ID, &WHO), 99); assert_eq!(Assets::total_issuance(ASSET_ID), 99); }); From 9e9edfbf7a07129eaf80b9a97ed7543d08d52333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 8 Aug 2024 08:28:25 -0500 Subject: [PATCH 18/47] change(pallet-assets-holder): resolve `VARIANT_COUNT` to match number of enum variants in mock --- substrate/frame/assets-holder/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/assets-holder/src/mock.rs b/substrate/frame/assets-holder/src/mock.rs index be9df18518b4..2b4978be6681 100644 --- a/substrate/frame/assets-holder/src/mock.rs +++ b/substrate/frame/assets-holder/src/mock.rs @@ -124,7 +124,7 @@ pub enum DummyHoldReason { impl VariantCount for DummyHoldReason { // Intentionally set below the actual count of variants, to allow testing for `can_freeze` - const VARIANT_COUNT: u32 = 2; + const VARIANT_COUNT: u32 = 3; } impl Config for Test { From e7155eb5279b26606a0f95166252bffabed43b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 9 Aug 2024 14:27:44 -0500 Subject: [PATCH 19/47] change(pallet-assets-holder): remove assertion in test that fails after changing variant count for `DummyHoldReason`. --- substrate/frame/assets-holder/src/tests.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index 12e7c4c92bf3..a10cc89ed83c 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -182,11 +182,15 @@ mod impl_hold_unbalanced { ] ); assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(5)); - // Adding more than max holds fails - assert_noop!( - AssetsHolder::set_balance_on_hold(ASSET_ID, &DummyHoldReason::Other, &WHO, 1), - Error::::TooManyHolds - ); + + // Note: Assertion skipped to meet @gavofyork's suggestion of matching the number of + // variant count with the number of enum's variants. + // // Adding more than max holds fails + // assert_noop!( + // AssetsHolder::set_balance_on_hold(ASSET_ID, &DummyHoldReason::Other, &WHO, 1), + // Error::::TooManyHolds + // ); + // Decreasing held balance works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, From 4268b76dbcf19e89aeb942874526fcfd8b29cd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Tue, 27 Aug 2024 19:58:55 -0500 Subject: [PATCH 20/47] change(pallet-assets-holder): implement changes suggested by @gui1117 --- substrate/frame/assets-holder/src/impls.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/substrate/frame/assets-holder/src/impls.rs b/substrate/frame/assets-holder/src/impls.rs index 6266b41c22aa..55258503151c 100644 --- a/substrate/frame/assets-holder/src/impls.rs +++ b/substrate/frame/assets-holder/src/impls.rs @@ -172,15 +172,20 @@ impl, I: 'static> UnbalancedHold for Pallet { amount: Self::Balance, ) -> DispatchResult { let mut holds = Holds::::get(asset.clone(), who); - let mut increase = true; - let mut delta = amount; + let increase; + let delta; - if let Some(item) = holds.iter_mut().find(|x| &x.id == reason) { + if let Some(pos) = holds.iter().position(|x| &x.id == reason) { + let item = &mut holds[pos]; delta = item.amount.max(amount) - item.amount.min(amount); increase = amount > item.amount; item.amount = amount; - holds.retain(|x| !x.amount.is_zero()); + if item.amount.is_zero() { + holds.swap_remove(pos); + } } else { + increase = true; + delta = amount; if !amount.is_zero() { holds .try_push(IdAmount { id: *reason, amount }) @@ -202,7 +207,11 @@ impl, I: 'static> UnbalancedHold for Pallet { HeldBalances::::insert(asset.clone(), who, held_amount); } - Holds::::insert(asset, who, holds); + if !holds.is_empty() { + Holds::::insert(asset, who, holds); + } else { + Holds::::remove(asset, who); + } Ok(()) } From 818e59048049ce51c6c3dca860653e533582769a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Tue, 27 Aug 2024 21:34:56 -0500 Subject: [PATCH 21/47] change(pallet-assets-holder): apply change from HeldBalance to BalanceOnHold, suggested by @gavofyork and @gui1117 --- substrate/frame/assets-holder/src/impls.rs | 8 +- substrate/frame/assets-holder/src/tests.rs | 102 +++++++++++++++---- substrate/frame/assets/src/functions.rs | 4 +- substrate/frame/assets/src/impl_fungibles.rs | 2 +- substrate/frame/assets/src/lib.rs | 2 +- substrate/frame/assets/src/mock.rs | 4 +- substrate/frame/assets/src/tests.rs | 2 +- substrate/frame/assets/src/types.rs | 8 +- 8 files changed, 98 insertions(+), 34 deletions(-) diff --git a/substrate/frame/assets-holder/src/impls.rs b/substrate/frame/assets-holder/src/impls.rs index 55258503151c..a5b371cc9c10 100644 --- a/substrate/frame/assets-holder/src/impls.rs +++ b/substrate/frame/assets-holder/src/impls.rs @@ -23,7 +23,7 @@ use frame_support::traits::{ DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence, }, }; -use pallet_assets::HeldBalance; +use pallet_assets::BalanceOnHold; use sp_runtime::{ traits::{CheckedAdd, CheckedSub, Zero}, ArithmeticError, @@ -32,8 +32,10 @@ use sp_runtime::{ // Implements [`HeldBalance`] from [`pallet-assets`], so it can understand whether there's a held // amount for an asset account, and is able to signal to this pallet when to clear the state of an // account. -impl, I: 'static> HeldBalance for Pallet { - fn held_balance(asset: T::AssetId, who: &T::AccountId) -> Option { +impl, I: 'static> BalanceOnHold + for Pallet +{ + fn balance_on_hold(asset: T::AssetId, who: &T::AccountId) -> Option { HeldBalances::::get(asset, who) } diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index a10cc89ed83c..ddd51b97059e 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -23,7 +23,7 @@ use frame_support::{ assert_noop, assert_ok, traits::tokens::fungibles::{Inspect, InspectHold, MutateHold, UnbalancedHold}, }; -use pallet_assets::HeldBalance; +use pallet_assets::BalanceOnHold; const WHO: AccountId = 1; const ASSET_ID: AssetId = 1; @@ -36,25 +36,43 @@ fn test_release(id: DummyHoldReason) { assert_ok!(AssetsHolder::set_balance_on_hold(ASSET_ID, &id, &WHO, 0)); } -mod impl_held_balance { +mod impl_balance_on_hold { use super::*; #[test] - fn held_balance_works() { + fn balance_on_hold_works() { new_test_ext(|| { - assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), None); + assert_eq!( + >::balance_on_hold(ASSET_ID, &WHO), + None + ); test_hold(DummyHoldReason::Governance, 1); - assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(1u64)); + assert_eq!( + >::balance_on_hold(ASSET_ID, &WHO), + Some(1u64) + ); test_hold(DummyHoldReason::Staking, 3); - assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(4u64)); + assert_eq!( + >::balance_on_hold(ASSET_ID, &WHO), + Some(4u64) + ); test_hold(DummyHoldReason::Governance, 2); - assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(5u64)); + assert_eq!( + >::balance_on_hold(ASSET_ID, &WHO), + Some(5u64) + ); // also test releasing works to reduce a balance, and finally releasing everything // resets to None test_release(DummyHoldReason::Governance); - assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), Some(3u64)); + assert_eq!( + >::balance_on_hold(ASSET_ID, &WHO), + Some(3u64) + ); test_release(DummyHoldReason::Staking); - assert_eq!(AssetsHolder::held_balance(ASSET_ID, &WHO), None); + assert_eq!( + >::balance_on_hold(ASSET_ID, &WHO), + None + ); }); } @@ -95,34 +113,58 @@ mod impl_hold_inspect { fn balance_on_hold_works() { new_test_ext(|| { assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO + ), 0u64 ); test_hold(DummyHoldReason::Governance, 1); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO + ), 1u64 ); test_hold(DummyHoldReason::Staking, 3); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Staking, + &WHO + ), 3u64 ); test_hold(DummyHoldReason::Staking, 2); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Staking, + &WHO + ), 2u64 ); // also test release to reduce a balance, and finally releasing everything resets to // 0 test_release(DummyHoldReason::Governance); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO + ), 0u64 ); test_release(DummyHoldReason::Staking); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Staking, + &WHO + ), 0u64 ); }); @@ -254,7 +296,11 @@ mod impl_hold_mutate { 89 ); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO + ), 10 ); assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 10); @@ -270,7 +316,11 @@ mod impl_hold_mutate { assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 20)); assert_eq!(Assets::balance(ASSET_ID, &WHO), 70); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO + ), 30 ); assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 30); @@ -281,7 +331,11 @@ mod impl_hold_mutate { assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Staking, &WHO, 20)); assert_eq!(Assets::balance(ASSET_ID, &WHO), 50); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Staking, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Staking, + &WHO + ), 20 ); assert_eq!(AssetsHolder::total_balance_on_hold(ASSET_ID, &WHO), 50); @@ -309,7 +363,11 @@ mod impl_hold_mutate { Precision::Exact, )); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO + ), 10 ); assert_eq!(Assets::balance(ASSET_ID, WHO), 70); @@ -326,7 +384,11 @@ mod impl_hold_mutate { Precision::BestEffort, )); assert_eq!( - AssetsHolder::balance_on_hold(ASSET_ID, &DummyHoldReason::Governance, &WHO), + >::balance_on_hold( + ASSET_ID, + &DummyHoldReason::Governance, + &WHO + ), 0 ); assert_eq!(Assets::balance(ASSET_ID, WHO), 80); diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index e0f7fd6bb079..ab82db86c36c 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -194,7 +194,7 @@ impl, I: 'static> Pallet { } if let Some(rest) = account.balance.checked_sub(&amount) { match ( - T::Holder::held_balance(id.clone(), who), + T::Holder::balance_on_hold(id.clone(), who), T::Freezer::frozen_balance(id.clone(), who), ) { (None, None) => @@ -238,7 +238,7 @@ impl, I: 'static> Pallet { ensure!(!account.status.is_frozen(), Error::::Frozen); let untouchable = match ( - T::Holder::held_balance(id.clone(), who), + T::Holder::balance_on_hold(id.clone(), who), T::Freezer::frozen_balance(id.clone(), who), keep_alive, ) { diff --git a/substrate/frame/assets/src/impl_fungibles.rs b/substrate/frame/assets/src/impl_fungibles.rs index 6ac4ef25e5c5..6ab7e941ea1a 100644 --- a/substrate/frame/assets/src/impl_fungibles.rs +++ b/substrate/frame/assets/src/impl_fungibles.rs @@ -48,7 +48,7 @@ impl, I: 'static> fungibles::Inspect<::AccountId fn total_balance(asset: Self::AssetId, who: &::AccountId) -> Self::Balance { Pallet::::balance(asset.clone(), who) - .saturating_add(T::Holder::held_balance(asset, who).unwrap_or_default()) + .saturating_add(T::Holder::balance_on_hold(asset, who).unwrap_or_default()) } fn reducible_balance( diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index aed4ed460bd4..297ba1f55bc1 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -396,7 +396,7 @@ pub mod pallet { /// A hook to inspect a per-asset, per-account balance that is held. This goes in /// accordance with balance model. - type Holder: HeldBalance; + type Holder: BalanceOnHold; /// Additional data to be stored with an account's asset balance. type Extra: Member + Parameter + Default + MaxEncodedLen; diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index e9a0306726d6..4a46b8edab01 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -120,8 +120,8 @@ parameter_types! { } pub struct TestHolder; -impl HeldBalance for TestHolder { - fn held_balance(asset: u32, who: &u64) -> Option { +impl BalanceOnHold for TestHolder { + fn balance_on_hold(asset: u32, who: &u64) -> Option { Held::get().get(&(asset, *who)).cloned() } diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index 502f41f33bb8..8444fb34d62b 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1385,7 +1385,7 @@ fn freezing_and_holds_work() { // Hold 50 of it set_held_balance(0, 1, 50); assert_eq!(Assets::balance(0, 1), 50); - assert_eq!(TestHolder::held_balance(0, &1), Some(50)); + assert_eq!(TestHolder::balance_on_hold(0, &1), Some(50)); // Can freeze up to held + min_balance without affecting reducible set_frozen_balance(0, 1, 59); diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index d1f8718c0b5f..d560da5319a8 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -236,14 +236,14 @@ impl FrozenBalance for /// This balance is then summed up with the balance of the account, and the /// `minimum_balance` (and frozen balance, if any) of the asset to calculate /// the reducible balance. -pub trait HeldBalance { +pub trait BalanceOnHold { /// Return the held balance. /// /// If `Some`, it means some balance is suspended, and it can be infallibly /// slashed. /// /// If `None` is returned, then nothing special is enforced. - fn held_balance(asset: AssetId, who: &AccountId) -> Option; + fn balance_on_hold(asset: AssetId, who: &AccountId) -> Option; /// Called after an account has been removed. /// @@ -251,8 +251,8 @@ pub trait HeldBalance { fn died(asset: AssetId, who: &AccountId); } -impl HeldBalance for () { - fn held_balance(_: AssetId, _: &AccountId) -> Option { +impl BalanceOnHold for () { + fn balance_on_hold(_: AssetId, _: &AccountId) -> Option { None } fn died(_: AssetId, _: &AccountId) {} From 06db70187ebea344a1db2f8e263f94be68d0b993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Tue, 27 Aug 2024 21:51:36 -0500 Subject: [PATCH 22/47] minor(pallet-assets-holder): small adjustments - `impls`: Minor codebase improvements - `lib`: Rename `HeldBalances` to `BalancesOnHold`. --- substrate/frame/assets-holder/src/impls.rs | 29 +++++++++++----------- substrate/frame/assets-holder/src/lib.rs | 6 ++--- substrate/frame/assets-holder/src/tests.rs | 16 ++++++------ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/substrate/frame/assets-holder/src/impls.rs b/substrate/frame/assets-holder/src/impls.rs index a5b371cc9c10..a53592e6683e 100644 --- a/substrate/frame/assets-holder/src/impls.rs +++ b/substrate/frame/assets-holder/src/impls.rs @@ -36,11 +36,11 @@ impl, I: 'static> BalanceOnHold { fn balance_on_hold(asset: T::AssetId, who: &T::AccountId) -> Option { - HeldBalances::::get(asset, who) + BalancesOnHold::::get(asset, who) } fn died(asset: T::AssetId, who: &T::AccountId) { - HeldBalances::::remove(asset.clone(), who); + BalancesOnHold::::remove(asset.clone(), who); Holds::::remove(asset, who); } } @@ -104,7 +104,7 @@ impl, I: 'static> InspectHold for Pallet { type Reason = T::RuntimeHoldReason; fn total_balance_on_hold(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { - HeldBalances::::get(asset, who).unwrap_or_else(Zero::zero) + BalancesOnHold::::get(asset, who).unwrap_or_else(Zero::zero) } fn balance_on_hold( @@ -174,28 +174,29 @@ impl, I: 'static> UnbalancedHold for Pallet { amount: Self::Balance, ) -> DispatchResult { let mut holds = Holds::::get(asset.clone(), who); - let increase; - let delta; - if let Some(pos) = holds.iter().position(|x| &x.id == reason) { + let (increase, delta) = if let Some(pos) = holds.iter().position(|x| &x.id == reason) { let item = &mut holds[pos]; - delta = item.amount.max(amount) - item.amount.min(amount); - increase = amount > item.amount; + let (increase, delta) = + (amount > item.amount, item.amount.max(amount) - item.amount.min(amount)); + item.amount = amount; if item.amount.is_zero() { holds.swap_remove(pos); } + + (increase, delta) } else { - increase = true; - delta = amount; if !amount.is_zero() { holds .try_push(IdAmount { id: *reason, amount }) .map_err(|_| Error::::TooManyHolds)?; } - } + (true, amount) + }; - let held_amount = HeldBalances::::get(asset.clone(), who).unwrap_or_else(Zero::zero); + let held_amount = + BalancesOnHold::::get(asset.clone(), who).unwrap_or_else(Zero::zero); let held_amount = if increase { held_amount.checked_add(&delta).ok_or(ArithmeticError::Overflow)? @@ -204,9 +205,9 @@ impl, I: 'static> UnbalancedHold for Pallet { }; if held_amount.is_zero() { - HeldBalances::::remove(asset.clone(), who); + BalancesOnHold::::remove(asset.clone(), who); } else { - HeldBalances::::insert(asset.clone(), who, held_amount); + BalancesOnHold::::insert(asset.clone(), who, held_amount); } if !holds.is_empty() { diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index c09b18b3a923..c4c6ddcb9897 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -130,7 +130,7 @@ pub mod pallet { /// A map that stores the current total held balance for every account on a given AssetId. #[pallet::storage] - pub(super) type HeldBalances, I: 'static = ()> = StorageDoubleMap< + pub(super) type BalancesOnHold, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, T::AssetId, @@ -153,14 +153,14 @@ impl, I: 'static> Pallet { fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { use sp_runtime::traits::{Saturating, Zero}; - for (asset, who, _) in HeldBalances::::iter() { + for (asset, who, _) in BalancesOnHold::::iter() { let held_amount: T::Balance = Holds::::get(asset.clone(), who.clone()) .iter() .map(|l| l.amount) .fold(Zero::zero(), |prev, amount| prev.saturating_add(amount)); frame_support::ensure!( - HeldBalances::::get(asset, who).unwrap_or_else(Zero::zero) == held_amount, + BalancesOnHold::::get(asset, who).unwrap_or_else(Zero::zero) == held_amount, "The `HeldAmount` is not equal to the sum of `Holds` for (`asset`, `who`)" ); } diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index ddd51b97059e..87d3b5f18d6c 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -81,7 +81,7 @@ mod impl_balance_on_hold { new_test_ext(|| { test_hold(DummyHoldReason::Governance, 1); AssetsHolder::died(ASSET_ID, &WHO); - assert!(HeldBalances::::get(ASSET_ID, WHO).is_none()); + assert!(BalancesOnHold::::get(ASSET_ID, WHO).is_none()); assert!(Holds::::get(ASSET_ID, WHO).is_empty()); }); } @@ -184,7 +184,7 @@ mod impl_hold_unbalanced { fn set_balance_on_hold_works() { new_test_ext(|| { assert_eq!(Holds::::get(ASSET_ID, WHO).to_vec(), vec![]); - assert_eq!(HeldBalances::::get(ASSET_ID, WHO), None); + assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), None); // Adding held balance works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, @@ -196,7 +196,7 @@ mod impl_hold_unbalanced { Holds::::get(ASSET_ID, WHO).to_vec(), vec![IdAmount { id: DummyHoldReason::Governance, amount: 1 }] ); - assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(1)); + assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(1)); // Increasing hold works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, @@ -208,7 +208,7 @@ mod impl_hold_unbalanced { Holds::::get(ASSET_ID, WHO).to_vec(), vec![IdAmount { id: DummyHoldReason::Governance, amount: 3 }] ); - assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(3)); + assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(3)); // Adding new held balance works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, @@ -223,7 +223,7 @@ mod impl_hold_unbalanced { IdAmount { id: DummyHoldReason::Staking, amount: 2 } ] ); - assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(5)); + assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(5)); // Note: Assertion skipped to meet @gavofyork's suggestion of matching the number of // variant count with the number of enum's variants. @@ -247,7 +247,7 @@ mod impl_hold_unbalanced { IdAmount { id: DummyHoldReason::Staking, amount: 1 } ] ); - assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(4)); + assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(4)); // Decreasing until removal of held balance works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, @@ -259,7 +259,7 @@ mod impl_hold_unbalanced { Holds::::get(ASSET_ID, WHO).to_vec(), vec![IdAmount { id: DummyHoldReason::Staking, amount: 1 }] ); - assert_eq!(HeldBalances::::get(ASSET_ID, WHO), Some(1)); + assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(1)); // Clearing ol all holds works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, @@ -268,7 +268,7 @@ mod impl_hold_unbalanced { 0 )); assert_eq!(Holds::::get(ASSET_ID, WHO).to_vec(), vec![]); - assert_eq!(HeldBalances::::get(ASSET_ID, WHO), None); + assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), None); }); } } From 7dff1117024b75938f4237867a5cde4d98930b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Tue, 27 Aug 2024 22:14:36 -0500 Subject: [PATCH 23/47] fix(pallet-revive-mock-network): include Holder on pallet_assets configuration --- substrate/frame/revive/mock-network/src/parachain.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/revive/mock-network/src/parachain.rs b/substrate/frame/revive/mock-network/src/parachain.rs index 3def48cca96b..112f51f4e9d9 100644 --- a/substrate/frame/revive/mock-network/src/parachain.rs +++ b/substrate/frame/revive/mock-network/src/parachain.rs @@ -119,6 +119,7 @@ impl pallet_assets::Config for Runtime { type AssetAccountDeposit = AssetAccountDeposit; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = (); From 5d04950ba9026de1d918645cefc4de0dc5fc1d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Tue, 27 Aug 2024 22:21:36 -0500 Subject: [PATCH 24/47] change(prdoc): update prdoc with all impacted crates --- prdoc/pr_4530.prdoc | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/prdoc/pr_4530.prdoc b/prdoc/pr_4530.prdoc index 64f59d69ad4b..c073ad5685ac 100644 --- a/prdoc/pr_4530.prdoc +++ b/prdoc/pr_4530.prdoc @@ -27,11 +27,37 @@ doc: wanted in the runtime implementation. crates: + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: pallet-asset-tx-payment + bump: patch + - name: pallet-asset-conversion-ops + bump: patch + - name: pallet-asset-conversion-tx-payment + bump: patch - name: pallet-assets bump: major - name: pallet-assets-holder bump: major - - name: asset-hub-rococo-runtime - bump: minor - - name: asset-hub-westend-runtime + - name: pallet-assets-freezer + bump: patch + - name: pallet-contracts-mock-network + bump: patch + - name: pallet-nft-fractionalization + bump: patch + - name: pallet-revive-mock-network + bump: patch + - name: pallet-xcm + bump: patch + - name: penpal-runtime + bump: patch + - name: rococo-parachain-runtime + bump: patch + - name: polkadot-sdk bump: minor + - name: staging-xcm-builder + bump: patch + - name: xcm-runtime-apis + bump: patch From 44e8f622e7841d4c3e8702bad304482ca3ebc736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 28 Aug 2024 08:27:19 -0500 Subject: [PATCH 25/47] change(pallet-assets-holder): add additional assertions to `try_state` (suggestion by @gui1117) --- substrate/frame/assets-holder/src/lib.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index c4c6ddcb9897..399c8ee29996 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -153,15 +153,18 @@ impl, I: 'static> Pallet { fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { use sp_runtime::traits::{Saturating, Zero}; - for (asset, who, _) in BalancesOnHold::::iter() { - let held_amount: T::Balance = Holds::::get(asset.clone(), who.clone()) - .iter() - .map(|l| l.amount) - .fold(Zero::zero(), |prev, amount| prev.saturating_add(amount)); + for (asset, who, balance_on_hold) in BalancesOnHold::::iter() { + ensure!(balance_on_hold != Zero::zero(), "zero on hold must not be in state"); + + let mut amount_from_holds: T::Balance = Zero::zero(); + for l in Holds::::get(asset.clone(), who.clone()).iter() { + ensure!(l.amount != Zero::zero(), "zero amount is invalid"); + amount_from_holds = amount_from_holds.saturating_add(l.amount); + } frame_support::ensure!( - BalancesOnHold::::get(asset, who).unwrap_or_else(Zero::zero) == held_amount, - "The `HeldAmount` is not equal to the sum of `Holds` for (`asset`, `who`)" + balance_on_hold == amount_from_holds, + "The `BalancesOnHold` amount is not equal to the sum of `Holds` for (`asset`, `who`)" ); } From bb1a98d7b073fe416bc8b08ed78899cf06ab1c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 28 Aug 2024 08:28:03 -0500 Subject: [PATCH 26/47] minor(pallet-assets-holder): rename _held_ references in comments with _on hold_ --- prdoc/pr_4530.prdoc | 4 +-- substrate/frame/assets-holder/src/impls.rs | 18 ++++++------ substrate/frame/assets-holder/src/lib.rs | 12 ++++---- substrate/frame/assets-holder/src/tests.rs | 33 +++++++++++----------- substrate/frame/assets/src/mock.rs | 10 +++---- 5 files changed, 38 insertions(+), 39 deletions(-) diff --git a/prdoc/pr_4530.prdoc b/prdoc/pr_4530.prdoc index c073ad5685ac..894b668c57de 100644 --- a/prdoc/pr_4530.prdoc +++ b/prdoc/pr_4530.prdoc @@ -4,13 +4,13 @@ doc: - audience: Runtime Dev description: | This change creates the `pallet-assets-holder` pallet, as well as changes `pallet-assets` - to support querying held balances via a new trait: `HeldBalance`. + to support querying held balances via a new trait: `BalanceOnHold`. It also adjusts the balance model implementation for fungible sets (see [sdk docs](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/tokens/fungible/index.html#visualising-balance-components-together-)). The `pallet-assets-holder` implements `hold` traits for `pallet-assets`, by extending this - pallet and implementing the `HeldBalance` trait so the held balance can be queried by + pallet and implementing the `BalanceOnHold` trait so the held balance can be queried by `pallet-assets` to calculate the reducible (a.k.a. spendable) balance. These changes imply adding a configuration type in `pallet-assets` for `Holder` diff --git a/substrate/frame/assets-holder/src/impls.rs b/substrate/frame/assets-holder/src/impls.rs index a53592e6683e..be7586075c1a 100644 --- a/substrate/frame/assets-holder/src/impls.rs +++ b/substrate/frame/assets-holder/src/impls.rs @@ -29,9 +29,9 @@ use sp_runtime::{ ArithmeticError, }; -// Implements [`HeldBalance`] from [`pallet-assets`], so it can understand whether there's a held -// amount for an asset account, and is able to signal to this pallet when to clear the state of an -// account. +// Implements [`BalanceOnHold`] from [`pallet-assets`], so it can understand whether there's some +// balance on hold for an asset account, and is able to signal to this pallet when to clear the +// state of an account. impl, I: 'static> BalanceOnHold for Pallet { @@ -195,19 +195,19 @@ impl, I: 'static> UnbalancedHold for Pallet { (true, amount) }; - let held_amount = + let amount_on_hold = BalancesOnHold::::get(asset.clone(), who).unwrap_or_else(Zero::zero); - let held_amount = if increase { - held_amount.checked_add(&delta).ok_or(ArithmeticError::Overflow)? + let amount_on_hold = if increase { + amount_on_hold.checked_add(&delta).ok_or(ArithmeticError::Overflow)? } else { - held_amount.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? + amount_on_hold.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? }; - if held_amount.is_zero() { + if amount_on_hold.is_zero() { BalancesOnHold::::remove(asset.clone(), who); } else { - BalancesOnHold::::insert(asset.clone(), who, held_amount); + BalancesOnHold::::insert(asset.clone(), who, amount_on_hold); } if !holds.is_empty() { diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index 399c8ee29996..8ea8639235ed 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -34,8 +34,8 @@ //! //! This pallet provides the following functionality: //! -//! - Pallet hooks allowing [`pallet-assets`] to know the held balance for an account on a given -//! asset (see [`pallet_assets::HeldBalance`]). +//! - Pallet hooks allowing [`pallet-assets`] to know the balance on hold for an account on a given +//! asset (see [`pallet_assets::BalanceOnHold`](pallet_assets::BalanceOnHold)). //! - An implementation of //! [`fungibles::hold::Inspect`](frame_support::traits::fungibles::hold::Inspect), //! [`fungibles::hold::Mutate`](frame_support::traits::fungibles::hold::Mutate) and @@ -90,21 +90,21 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { - // `who`s held balance was increased by `amount`. + // `who`s balance on hold was increased by `amount`. Held { who: T::AccountId, asset_id: T::AssetId, reason: T::RuntimeHoldReason, amount: T::Balance, }, - // `who`s held balance was decreased by `amount`. + // `who`s balance on hold was decreased by `amount`. Released { who: T::AccountId, asset_id: T::AssetId, reason: T::RuntimeHoldReason, amount: T::Balance, }, - // `who`s held balance was burned by `amount`. + // `who`s balance on hold was burned by `amount`. Slashed { who: T::AccountId, asset_id: T::AssetId, @@ -128,7 +128,7 @@ pub mod pallet { ValueQuery, >; - /// A map that stores the current total held balance for every account on a given AssetId. + /// A map that stores the current total balance on hold for every account on a given AssetId. #[pallet::storage] pub(super) type BalancesOnHold, I: 'static = ()> = StorageDoubleMap< _, diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index 87d3b5f18d6c..eb9f89feab66 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -185,7 +185,7 @@ mod impl_hold_unbalanced { new_test_ext(|| { assert_eq!(Holds::::get(ASSET_ID, WHO).to_vec(), vec![]); assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), None); - // Adding held balance works + // Adding balance on hold works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, &DummyHoldReason::Governance, @@ -209,7 +209,7 @@ mod impl_hold_unbalanced { vec![IdAmount { id: DummyHoldReason::Governance, amount: 3 }] ); assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(3)); - // Adding new held balance works + // Adding new balance on hold works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, &DummyHoldReason::Staking, @@ -233,7 +233,7 @@ mod impl_hold_unbalanced { // Error::::TooManyHolds // ); - // Decreasing held balance works + // Decreasing balance on hold works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, &DummyHoldReason::Staking, @@ -248,7 +248,7 @@ mod impl_hold_unbalanced { ] ); assert_eq!(BalancesOnHold::::get(ASSET_ID, WHO), Some(4)); - // Decreasing until removal of held balance works + // Decreasing until removal of balance on hold works assert_ok!(AssetsHolder::set_balance_on_hold( ASSET_ID, &DummyHoldReason::Governance, @@ -309,9 +309,9 @@ mod impl_hold_mutate { // Holding preserves `total_issuance` assert_eq!(Assets::total_issuance(ASSET_ID), 100); - // Increasing the amount held for the same reason has the same effect as described above - // in `set_balance_on_hold_works`, while total issuance is preserved. - // Consideration: holding for an amount `x` will increase the already held amount by + // Increasing the amount on hold for the same reason has the same effect as described + // above in `set_balance_on_hold_works`, while total issuance is preserved. + // Consideration: holding for an amount `x` will increase the already amount on hold by // `x`. assert_ok!(AssetsHolder::hold(ASSET_ID, &DummyHoldReason::Governance, &WHO, 20)); assert_eq!(Assets::balance(ASSET_ID, &WHO), 70); @@ -373,8 +373,8 @@ mod impl_hold_mutate { assert_eq!(Assets::balance(ASSET_ID, WHO), 70); }); - // Releasing over the max held amount with `BestEffort` will increase the - // balance by the previously held amount, while preserving total issuance. + // Releasing over the max amount on hold with `BestEffort` will increase the + // balance by the previously amount on hold, while preserving total issuance. new_test_ext().execute_with(|| { assert_ok!(AssetsHolder::release( ASSET_ID, @@ -394,7 +394,7 @@ mod impl_hold_mutate { assert_eq!(Assets::balance(ASSET_ID, WHO), 80); }); - // Releasing over the max held amount with `Exact` will fail. + // Releasing over the max amount on hold with `Exact` will fail. new_test_ext().execute_with(|| { assert_noop!( AssetsHolder::release( @@ -425,8 +425,8 @@ mod impl_hold_mutate { assert_eq!(Assets::total_issuance(ASSET_ID), 99); }); - // Burning by an amount up to the held balance with `Exact` works, reducing held balance up - // to the given amount. + // Burning by an amount up to the balance on hold with `Exact` works, reducing balance on + // hold up to the given amount. new_test_ext().execute_with(|| { assert_ok!(AssetsHolder::burn_held( ASSET_ID, @@ -440,8 +440,8 @@ mod impl_hold_mutate { assert_eq!(Assets::balance(ASSET_ID, WHO), 50); }); - // Burning by an amount over the held balance with `BestEffort` works, reducing held balance - // up to the given amount. + // Burning by an amount over the balance on hold with `BestEffort` works, reducing balance + // on hold up to the given amount. new_test_ext().execute_with(|| { assert_ok!(AssetsHolder::burn_held( ASSET_ID, @@ -455,7 +455,7 @@ mod impl_hold_mutate { assert_eq!(Assets::balance(ASSET_ID, WHO), 50); }); - // Burning by an amount over the held balance with `Exact` fails. + // Burning by an amount over the balance on hold with `Exact` fails. new_test_ext().execute_with(|| { assert_noop!( AssetsHolder::burn_held( @@ -474,8 +474,7 @@ mod impl_hold_mutate { #[test] fn burn_all_held_works() { new_test_ext().execute_with(|| { - // Burning all held works as burning giving the held balance - // as amount with `BestEffort` + // Burning all balance on hold works as burning passing it as amount with `BestEffort` assert_ok!(AssetsHolder::burn_all_held( ASSET_ID, &DummyHoldReason::Governance, diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index 4a46b8edab01..0f81eea47f57 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -132,16 +132,16 @@ impl BalanceOnHold for TestHolder { pub(crate) fn set_held_balance(asset: u32, who: u64, amount: u64) { Held::mutate(|v| { - let held_amount = v.get(&(asset, who)).unwrap_or(&0); + let amount_on_hold = v.get(&(asset, who)).unwrap_or(&0); - if &amount > held_amount { + if &amount > amount_on_hold { // Hold more funds - let amount = amount - held_amount; + let amount = amount - amount_on_hold; let f = DebitFlags { keep_alive: true, best_effort: false }; assert_ok!(Assets::decrease_balance(asset, &who, amount, f, |_, _| Ok(()))); } else { - // Release held funds - let amount = held_amount - amount; + // Release funds on hold + let amount = amount_on_hold - amount; assert_ok!(Assets::increase_balance(asset, &who, amount, |_| Ok(()))); } From 3688f89db57c86c7ba1bbca24109f063fa41e06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 4 Sep 2024 16:48:16 -0500 Subject: [PATCH 27/47] change(pallet-assets-holder): address changes requested by @muharem --- substrate/frame/assets-holder/src/impls.rs | 13 ++++++++++--- substrate/frame/assets-holder/src/lib.rs | 5 +++-- substrate/frame/assets-holder/src/tests.rs | 2 +- substrate/frame/assets/src/functions.rs | 6 ++++-- substrate/frame/assets/src/types.rs | 16 +++++++++------- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/substrate/frame/assets-holder/src/impls.rs b/substrate/frame/assets-holder/src/impls.rs index be7586075c1a..69951898ba42 100644 --- a/substrate/frame/assets-holder/src/impls.rs +++ b/substrate/frame/assets-holder/src/impls.rs @@ -40,8 +40,15 @@ impl, I: 'static> BalanceOnHold::remove(asset.clone(), who); - Holds::::remove(asset, who); + let _ = Holds::::try_mutate(asset.clone(), who, |holds| { + for l in holds.iter() { + Self::burn_all_held(asset.clone(), &l.id, who, Precision::Exact, Fortitude::Force)?; + } + + Holds::::remove(asset.clone(), who); + BalancesOnHold::::remove(asset.clone(), who); + Ok::<(), DispatchError>(()) + }); } } @@ -255,7 +262,7 @@ impl, I: 'static> MutateHold for Pallet { who: &T::AccountId, amount: Self::Balance, ) { - Self::deposit_event(Event::::Slashed { + Self::deposit_event(Event::::Burned { asset_id, who: who.clone(), reason: *reason, diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index 8ea8639235ed..99efe45a2277 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -105,7 +105,7 @@ pub mod pallet { amount: T::Balance, }, // `who`s balance on hold was burned by `amount`. - Slashed { + Burned { who: T::AccountId, asset_id: T::AssetId, reason: T::RuntimeHoldReason, @@ -159,7 +159,8 @@ impl, I: 'static> Pallet { let mut amount_from_holds: T::Balance = Zero::zero(); for l in Holds::::get(asset.clone(), who.clone()).iter() { ensure!(l.amount != Zero::zero(), "zero amount is invalid"); - amount_from_holds = amount_from_holds.saturating_add(l.amount); + amount_from_holds = + amount_from_holds.checked_add(l.amount).ok_or(ArithmeticError::Overflow)?; } frame_support::ensure!( diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index eb9f89feab66..a6abddbacb88 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -535,7 +535,7 @@ mod impl_hold_mutate { Fortitude::Polite, )); System::assert_has_event( - Event::::Slashed { + Event::::Burned { who: WHO, asset_id: ASSET_ID, reason: DummyHoldReason::Governance, diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index ab82db86c36c..75ff0013ce9d 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -211,8 +211,10 @@ impl, I: 'static> Pallet { let frozen = maybe_frozen.unwrap_or_default(); let held = maybe_held.unwrap_or_default(); - let required = frozen.saturating_sub(held).max(details.min_balance); - if rest < required { + // The `untouchable` balance of the asset account of `who`. This is described + // here: https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/tokens/fungible/index.html#visualising-balance-components-together- + let untouchable = frozen.saturating_sub(held).max(details.min_balance); + if rest < untouchable { return Frozen } diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index d560da5319a8..01fc4577cf6e 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -232,17 +232,19 @@ impl FrozenBalance for fn died(_: AssetId, _: &AccountId) {} } -/// Trait for specifying a balance that is distinct from the free balance. -/// This balance is then summed up with the balance of the account, and the -/// `minimum_balance` (and frozen balance, if any) of the asset to calculate -/// the reducible balance. +/// This trait indicates a balance that is _on hold_ for an asset account. +/// +/// A balance _on hold_ is a balance that, while is assigned to an account, +/// is outside the direct control of it. Instead, is being _held_ by the +/// system logic (i.e. Pallets) and can be eventually burned or released. pub trait BalanceOnHold { /// Return the held balance. /// - /// If `Some`, it means some balance is suspended, and it can be infallibly - /// slashed. + /// If `Some`, it means some balance is _on hold_, and it can be + /// infallibly burned. /// - /// If `None` is returned, then nothing special is enforced. + /// If `None` is returned, then no balance is _on hold_ for `who`'s asset + /// account. fn balance_on_hold(asset: AssetId, who: &AccountId) -> Option; /// Called after an account has been removed. From 900dbaad79e1b10fba6067c0e6b73731f1571f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 4 Sep 2024 16:50:14 -0500 Subject: [PATCH 28/47] change(pallet-assets-holder): impls -> impl_fungibles --- .../frame/assets-holder/src/{impls.rs => impl_fungibles.rs} | 0 substrate/frame/assets-holder/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename substrate/frame/assets-holder/src/{impls.rs => impl_fungibles.rs} (100%) diff --git a/substrate/frame/assets-holder/src/impls.rs b/substrate/frame/assets-holder/src/impl_fungibles.rs similarity index 100% rename from substrate/frame/assets-holder/src/impls.rs rename to substrate/frame/assets-holder/src/impl_fungibles.rs diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index 99efe45a2277..0756b25c4729 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -58,7 +58,7 @@ mod mock; #[cfg(test)] mod tests; -mod impls; +mod impl_fungibles; #[frame_support::pallet] pub mod pallet { From b689e2e97d9a72a99226666f433ff741c1988949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 4 Sep 2024 19:44:46 -0500 Subject: [PATCH 29/47] fix(pallet-assets-holder): missing `CheckedAdd` import --- substrate/frame/assets-holder/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index 0756b25c4729..13fcf17a9c79 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -50,6 +50,7 @@ use frame_support::{ BoundedVec, }; use frame_system::pallet_prelude::BlockNumberFor; +use sp_runtime::traits::CheckedAdd; pub use pallet::*; From 2f14b320d6b570cba4a789ce96242ddce05194d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 5 Sep 2024 09:52:10 -0500 Subject: [PATCH 30/47] change(pallet-assets-holder): make `died` burn balance on hold (if any). - now it returns a `DispatchResult`. - apply changes on usages and traits impls. - change tests to assert calling the method returns `Ok`. --- substrate/frame/assets-holder/src/impl_fungibles.rs | 10 +++++----- substrate/frame/assets-holder/src/lib.rs | 6 ++++-- substrate/frame/assets-holder/src/tests.rs | 2 +- substrate/frame/assets/src/functions.rs | 12 ++++++------ substrate/frame/assets/src/mock.rs | 3 ++- substrate/frame/assets/src/types.rs | 6 ++++-- 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/substrate/frame/assets-holder/src/impl_fungibles.rs b/substrate/frame/assets-holder/src/impl_fungibles.rs index 69951898ba42..71fbfa12438e 100644 --- a/substrate/frame/assets-holder/src/impl_fungibles.rs +++ b/substrate/frame/assets-holder/src/impl_fungibles.rs @@ -39,16 +39,16 @@ impl, I: 'static> BalanceOnHold::get(asset, who) } - fn died(asset: T::AssetId, who: &T::AccountId) { - let _ = Holds::::try_mutate(asset.clone(), who, |holds| { + fn died(asset: T::AssetId, who: &T::AccountId) -> DispatchResult { + Holds::::try_mutate(asset.clone(), who, |holds| { for l in holds.iter() { Self::burn_all_held(asset.clone(), &l.id, who, Precision::Exact, Fortitude::Force)?; } - Holds::::remove(asset.clone(), who); + *holds = BoundedVec::new(); BalancesOnHold::::remove(asset.clone(), who); - Ok::<(), DispatchError>(()) - }); + Ok(()) + }) } } diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index 13fcf17a9c79..b5ccb8f50a35 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -50,7 +50,9 @@ use frame_support::{ BoundedVec, }; use frame_system::pallet_prelude::BlockNumberFor; -use sp_runtime::traits::CheckedAdd; + +#[cfg(any(test, feature = "try-runtime"))] +use sp_runtime::{traits::CheckedAdd, ArithmeticError}; pub use pallet::*; @@ -161,7 +163,7 @@ impl, I: 'static> Pallet { for l in Holds::::get(asset.clone(), who.clone()).iter() { ensure!(l.amount != Zero::zero(), "zero amount is invalid"); amount_from_holds = - amount_from_holds.checked_add(l.amount).ok_or(ArithmeticError::Overflow)?; + amount_from_holds.checked_add(&l.amount).ok_or(ArithmeticError::Overflow)?; } frame_support::ensure!( diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index a6abddbacb88..8676105a49dc 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -80,7 +80,7 @@ mod impl_balance_on_hold { fn died_works() { new_test_ext(|| { test_hold(DummyHoldReason::Governance, 1); - AssetsHolder::died(ASSET_ID, &WHO); + assert_ok!(AssetsHolder::died(ASSET_ID, &WHO)); assert!(BalancesOnHold::::get(ASSET_ID, WHO).is_none()); assert!(Holds::::get(ASSET_ID, WHO).is_empty()); }); diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 75ff0013ce9d..54d544481cba 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -384,7 +384,7 @@ impl, I: 'static> Pallet { Asset::::insert(&id, details); // Executing a hook here is safe, since it is not in a `mutate`. T::Freezer::died(id.clone(), &who); - T::Holder::died(id, &who); + T::Holder::died(id, &who)?; Ok(()) } @@ -421,7 +421,7 @@ impl, I: 'static> Pallet { Asset::::insert(&id, details); // Executing a hook here is safe, since it is not in a `mutate`. T::Freezer::died(id.clone(), &who); - T::Holder::died(id, &who); + T::Holder::died(id, &who)?; return Ok(()) } @@ -590,7 +590,7 @@ impl, I: 'static> Pallet { // Execute hook outside of `mutate`. if let Some(Remove) = target_died { T::Freezer::died(id.clone(), target); - T::Holder::died(id, target); + T::Holder::died(id, target)?; } Ok(actual) } @@ -615,7 +615,7 @@ impl, I: 'static> Pallet { Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?; if let Some(Remove) = died { T::Freezer::died(id.clone(), source); - T::Holder::died(id, source); + T::Holder::died(id, source)?; } Ok(balance) } @@ -816,7 +816,7 @@ impl, I: 'static> Pallet { for who in &dead_accounts { T::Freezer::died(id.clone(), &who); - T::Holder::died(id.clone(), &who); + T::Holder::died(id.clone(), &who)?; } Self::deposit_event(Event::AccountsDestroyed { @@ -978,7 +978,7 @@ impl, I: 'static> Pallet { // Execute hook outside of `mutate`. if let Some(Remove) = owner_died { T::Freezer::died(id.clone(), owner); - T::Holder::died(id, owner); + T::Holder::died(id, owner)?; } Ok(()) } diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index 0f81eea47f57..6f9db628f92f 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -125,8 +125,9 @@ impl BalanceOnHold for TestHolder { Held::get().get(&(asset, *who)).cloned() } - fn died(_asset: u32, _who: &u64) { + fn died(_asset: u32, _who: &u64) -> DispatchResult { // TODO: Connect with existing hooks list + Ok(()) } } diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index 01fc4577cf6e..50e3fab91b6f 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -250,14 +250,16 @@ pub trait BalanceOnHold { /// Called after an account has been removed. /// /// NOTE: It is possible that the asset does no longer exist when this hook is called. - fn died(asset: AssetId, who: &AccountId); + fn died(asset: AssetId, who: &AccountId) -> DispatchResult; } impl BalanceOnHold for () { fn balance_on_hold(_: AssetId, _: &AccountId) -> Option { None } - fn died(_: AssetId, _: &AccountId) {} + fn died(_: AssetId, _: &AccountId) -> DispatchResult { + Ok(()) + } } #[derive(Copy, Clone, PartialEq, Eq)] From 5d7d87473c0b5c8cb3050fd855472cd444d29cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 5 Sep 2024 18:29:09 -0500 Subject: [PATCH 31/47] change(pallet-assets-holder): fast exit for `set_balance_on_hold`. --- .../frame/assets-holder/src/impl_fungibles.rs | 59 +++++++++++-------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/substrate/frame/assets-holder/src/impl_fungibles.rs b/substrate/frame/assets-holder/src/impl_fungibles.rs index 71fbfa12438e..0b6663647a49 100644 --- a/substrate/frame/assets-holder/src/impl_fungibles.rs +++ b/substrate/frame/assets-holder/src/impl_fungibles.rs @@ -181,46 +181,57 @@ impl, I: 'static> UnbalancedHold for Pallet { amount: Self::Balance, ) -> DispatchResult { let mut holds = Holds::::get(asset.clone(), who); + let amount_on_hold = + BalancesOnHold::::get(asset.clone(), who).unwrap_or_else(Zero::zero); - let (increase, delta) = if let Some(pos) = holds.iter().position(|x| &x.id == reason) { - let item = &mut holds[pos]; - let (increase, delta) = - (amount > item.amount, item.amount.max(amount) - item.amount.min(amount)); + let amount_on_hold = if amount.is_zero() { + if let Some(pos) = holds.iter().position(|x| &x.id == reason) { + let item = &mut holds[pos]; + let amount = item.amount; - item.amount = amount; - if item.amount.is_zero() { holds.swap_remove(pos); + amount_on_hold.checked_sub(&amount).ok_or(ArithmeticError::Underflow)? + } else { + amount_on_hold } - - (increase, delta) } else { - if !amount.is_zero() { + let (increase, delta) = if let Some(pos) = holds.iter().position(|x| &x.id == reason) { + let item = &mut holds[pos]; + let (increase, delta) = + (amount > item.amount, item.amount.max(amount) - item.amount.min(amount)); + + item.amount = amount; + if item.amount.is_zero() { + holds.swap_remove(pos); + } + + (increase, delta) + } else { holds .try_push(IdAmount { id: *reason, amount }) .map_err(|_| Error::::TooManyHolds)?; - } - (true, amount) - }; + (true, amount) + }; - let amount_on_hold = - BalancesOnHold::::get(asset.clone(), who).unwrap_or_else(Zero::zero); + let amount_on_hold = if increase { + amount_on_hold.checked_add(&delta).ok_or(ArithmeticError::Overflow)? + } else { + amount_on_hold.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? + }; - let amount_on_hold = if increase { - amount_on_hold.checked_add(&delta).ok_or(ArithmeticError::Overflow)? - } else { - amount_on_hold.checked_sub(&delta).ok_or(ArithmeticError::Underflow)? + amount_on_hold }; - if amount_on_hold.is_zero() { - BalancesOnHold::::remove(asset.clone(), who); + if !holds.is_empty() { + Holds::::insert(asset.clone(), who, holds); } else { - BalancesOnHold::::insert(asset.clone(), who, amount_on_hold); + Holds::::remove(asset.clone(), who); } - if !holds.is_empty() { - Holds::::insert(asset, who, holds); + if amount_on_hold.is_zero() { + BalancesOnHold::::remove(asset.clone(), who); } else { - Holds::::remove(asset, who); + BalancesOnHold::::insert(asset.clone(), who, amount_on_hold); } Ok(()) From 427a29bc5c5178299a66ef0fce3c071da7731585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 6 Sep 2024 07:21:42 -0500 Subject: [PATCH 32/47] fix(penpal-runtime): missing `Holder` in `PoolAssets` config --- cumulus/parachains/runtimes/testing/penpal/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 69466d6bd250..aa0c6d3b2be4 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -540,6 +540,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU128<0>; type ApprovalDeposit = ConstU128<0>; type StringLimit = ConstU32<50>; + type Holder = (); type Freezer = (); type Extra = (); type WeightInfo = pallet_assets::weights::SubstrateWeight; From 9ccc35ce546791028dab830e9ad98b5bb5bc70c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 6 Sep 2024 08:50:03 -0500 Subject: [PATCH 33/47] fix(pallet-assets-holder): lint --- substrate/frame/assets-holder/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index b5ccb8f50a35..cda267d639eb 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -51,9 +51,6 @@ use frame_support::{ }; use frame_system::pallet_prelude::BlockNumberFor; -#[cfg(any(test, feature = "try-runtime"))] -use sp_runtime::{traits::CheckedAdd, ArithmeticError}; - pub use pallet::*; #[cfg(test)] @@ -154,7 +151,10 @@ pub mod pallet { impl, I: 'static> Pallet { #[cfg(any(test, feature = "try-runtime"))] fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { - use sp_runtime::traits::{Saturating, Zero}; + use sp_runtime::{ + traits::{CheckedAdd, Zero}, + ArithmeticError, + }; for (asset, who, balance_on_hold) in BalancesOnHold::::iter() { ensure!(balance_on_hold != Zero::zero(), "zero on hold must not be in state"); From c65a4c2857bcee6b90f96a4cacc3f11c7313fa3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 6 Sep 2024 09:41:11 -0500 Subject: [PATCH 34/47] change(pallet-assets-holder): revert `died` to make it an infallible function --- .../frame/assets-holder/src/impl_fungibles.rs | 24 ++++++++++++------- substrate/frame/assets-holder/src/tests.rs | 12 +++++++++- substrate/frame/assets/src/functions.rs | 12 +++++----- substrate/frame/assets/src/mock.rs | 3 +-- substrate/frame/assets/src/types.rs | 11 +++++---- 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/substrate/frame/assets-holder/src/impl_fungibles.rs b/substrate/frame/assets-holder/src/impl_fungibles.rs index 0b6663647a49..ef277f8b8d06 100644 --- a/substrate/frame/assets-holder/src/impl_fungibles.rs +++ b/substrate/frame/assets-holder/src/impl_fungibles.rs @@ -39,16 +39,22 @@ impl, I: 'static> BalanceOnHold::get(asset, who) } - fn died(asset: T::AssetId, who: &T::AccountId) -> DispatchResult { - Holds::::try_mutate(asset.clone(), who, |holds| { - for l in holds.iter() { - Self::burn_all_held(asset.clone(), &l.id, who, Precision::Exact, Fortitude::Force)?; - } + fn died(asset: T::AssetId, who: &T::AccountId) { + if pallet_assets::Pallet::::can_withdraw(asset.clone(), who, Zero::zero()) == + WithdrawConsequence::Success + { + defensive_assert!( + Holds::::get(asset.clone(), who).is_empty(), + "The list of Holds should be empty before allowing an account to die" + ); + defensive_assert!( + BalancesOnHold::::get(asset.clone(), who).is_none(), + "The should not be a balance on hold before allowing to die" + ); + } - *holds = BoundedVec::new(); - BalancesOnHold::::remove(asset.clone(), who); - Ok(()) - }) + Holds::::remove(asset.clone(), who); + BalancesOnHold::::remove(asset, who); } } diff --git a/substrate/frame/assets-holder/src/tests.rs b/substrate/frame/assets-holder/src/tests.rs index 8676105a49dc..433ed664a144 100644 --- a/substrate/frame/assets-holder/src/tests.rs +++ b/substrate/frame/assets-holder/src/tests.rs @@ -76,11 +76,21 @@ mod impl_balance_on_hold { }); } + #[test] + #[should_panic = "The list of Holds should be empty before allowing an account to die"] + fn died_fails_if_holds_exist() { + new_test_ext(|| { + test_hold(DummyHoldReason::Governance, 1); + AssetsHolder::died(ASSET_ID, &WHO); + }); + } + #[test] fn died_works() { new_test_ext(|| { test_hold(DummyHoldReason::Governance, 1); - assert_ok!(AssetsHolder::died(ASSET_ID, &WHO)); + test_release(DummyHoldReason::Governance); + AssetsHolder::died(ASSET_ID, &WHO); assert!(BalancesOnHold::::get(ASSET_ID, WHO).is_none()); assert!(Holds::::get(ASSET_ID, WHO).is_empty()); }); diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 54d544481cba..75ff0013ce9d 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -384,7 +384,7 @@ impl, I: 'static> Pallet { Asset::::insert(&id, details); // Executing a hook here is safe, since it is not in a `mutate`. T::Freezer::died(id.clone(), &who); - T::Holder::died(id, &who)?; + T::Holder::died(id, &who); Ok(()) } @@ -421,7 +421,7 @@ impl, I: 'static> Pallet { Asset::::insert(&id, details); // Executing a hook here is safe, since it is not in a `mutate`. T::Freezer::died(id.clone(), &who); - T::Holder::died(id, &who)?; + T::Holder::died(id, &who); return Ok(()) } @@ -590,7 +590,7 @@ impl, I: 'static> Pallet { // Execute hook outside of `mutate`. if let Some(Remove) = target_died { T::Freezer::died(id.clone(), target); - T::Holder::died(id, target)?; + T::Holder::died(id, target); } Ok(actual) } @@ -615,7 +615,7 @@ impl, I: 'static> Pallet { Self::transfer_and_die(id.clone(), source, dest, amount, maybe_need_admin, f)?; if let Some(Remove) = died { T::Freezer::died(id.clone(), source); - T::Holder::died(id, source)?; + T::Holder::died(id, source); } Ok(balance) } @@ -816,7 +816,7 @@ impl, I: 'static> Pallet { for who in &dead_accounts { T::Freezer::died(id.clone(), &who); - T::Holder::died(id.clone(), &who)?; + T::Holder::died(id.clone(), &who); } Self::deposit_event(Event::AccountsDestroyed { @@ -978,7 +978,7 @@ impl, I: 'static> Pallet { // Execute hook outside of `mutate`. if let Some(Remove) = owner_died { T::Freezer::died(id.clone(), owner); - T::Holder::died(id, owner)?; + T::Holder::died(id, owner); } Ok(()) } diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index 6f9db628f92f..0f81eea47f57 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -125,9 +125,8 @@ impl BalanceOnHold for TestHolder { Held::get().get(&(asset, *who)).cloned() } - fn died(_asset: u32, _who: &u64) -> DispatchResult { + fn died(_asset: u32, _who: &u64) { // TODO: Connect with existing hooks list - Ok(()) } } diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index 50e3fab91b6f..162e88f5dc0d 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -249,17 +249,18 @@ pub trait BalanceOnHold { /// Called after an account has been removed. /// - /// NOTE: It is possible that the asset does no longer exist when this hook is called. - fn died(asset: AssetId, who: &AccountId) -> DispatchResult; + /// It is expected that this method is called only when there is not balance + /// on hold. Otherwise, an account should not be removed. + /// + /// NOTE: It is possible that the asset no longer exists when this hook is called. + fn died(asset: AssetId, who: &AccountId); } impl BalanceOnHold for () { fn balance_on_hold(_: AssetId, _: &AccountId) -> Option { None } - fn died(_: AssetId, _: &AccountId) -> DispatchResult { - Ok(()) - } + fn died(_: AssetId, _: &AccountId) {} } #[derive(Copy, Clone, PartialEq, Eq)] From f1c0983b768ad671ade34ca5726b93c1947415e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 9 Sep 2024 11:05:03 -0500 Subject: [PATCH 35/47] change(pallet-assets): remark the change of parameters in the `freezer_should_work` test --- substrate/frame/assets/src/tests.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index 8444fb34d62b..ff0f1951be97 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1345,6 +1345,12 @@ fn freezer_should_work() { // freeze 50 of it. set_frozen_balance(0, 1, 50); + // Note: The amount to be transferred in this step changed deliberately from 20 to 30 + // (https://github.com/paritytech/polkadot-sdk/pull/4530/commits/2ab35354d86904c035b21a2229452841b79b0457) + // to reflect the change in how `reducible_balance` is calculated: from untouchable = ed + + // frozen, to untouchalbe = max(ed, frozen) + // + // This is done in this line so most of the remaining test is preserved without changes assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 30)); // cannot transfer another 21 away as this would take the spendable balance (30) to below // zero. From 92769f1a825770364cf24cfa9a910903b7053897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 11 Sep 2024 08:46:17 -0500 Subject: [PATCH 36/47] change(pallet-assets): implement contains_holds / contains_freezes --- substrate/frame/assets-freezer/src/impls.rs | 4 +++ .../frame/assets-holder/src/impl_fungibles.rs | 25 ++++++++-------- substrate/frame/assets/src/functions.rs | 4 +++ substrate/frame/assets/src/lib.rs | 4 +++ substrate/frame/assets/src/mock.rs | 23 ++++++++++++--- substrate/frame/assets/src/tests.rs | 29 +++++++++++++++++-- substrate/frame/assets/src/types.rs | 16 +++++++--- 7 files changed, 83 insertions(+), 22 deletions(-) diff --git a/substrate/frame/assets-freezer/src/impls.rs b/substrate/frame/assets-freezer/src/impls.rs index cd383f1c3cd1..a73ab46eaf04 100644 --- a/substrate/frame/assets-freezer/src/impls.rs +++ b/substrate/frame/assets-freezer/src/impls.rs @@ -38,6 +38,10 @@ impl, I: 'static> FrozenBalance::remove(asset.clone(), who); Freezes::::remove(asset, who); } + + fn contains_freezes(asset: T::AssetId) -> bool { + Freezes::::contains_prefix(asset) + } } // Implement [`fungibles::Inspect`](frame_support::traits::fungibles::Inspect) as it is bound by diff --git a/substrate/frame/assets-holder/src/impl_fungibles.rs b/substrate/frame/assets-holder/src/impl_fungibles.rs index ef277f8b8d06..b286cbb2eb49 100644 --- a/substrate/frame/assets-holder/src/impl_fungibles.rs +++ b/substrate/frame/assets-holder/src/impl_fungibles.rs @@ -28,6 +28,7 @@ use sp_runtime::{ traits::{CheckedAdd, CheckedSub, Zero}, ArithmeticError, }; +use storage::StorageDoubleMap; // Implements [`BalanceOnHold`] from [`pallet-assets`], so it can understand whether there's some // balance on hold for an asset account, and is able to signal to this pallet when to clear the @@ -40,22 +41,22 @@ impl, I: 'static> BalanceOnHold::can_withdraw(asset.clone(), who, Zero::zero()) == - WithdrawConsequence::Success - { - defensive_assert!( - Holds::::get(asset.clone(), who).is_empty(), - "The list of Holds should be empty before allowing an account to die" - ); - defensive_assert!( - BalancesOnHold::::get(asset.clone(), who).is_none(), - "The should not be a balance on hold before allowing to die" - ); - } + defensive_assert!( + Holds::::get(asset.clone(), who).is_empty(), + "The list of Holds should be empty before allowing an account to die" + ); + defensive_assert!( + BalancesOnHold::::get(asset.clone(), who).is_none(), + "The should not be a balance on hold before allowing to die" + ); Holds::::remove(asset.clone(), who); BalancesOnHold::::remove(asset, who); } + + fn contains_holds(asset: T::AssetId) -> bool { + Holds::::contains_prefix(asset) + } } // Implement [`fungibles::Inspect`](frame_support::traits::fungibles::Inspect) as it is bound by diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 75ff0013ce9d..b5c80358da08 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -769,6 +769,10 @@ impl, I: 'static> Pallet { if let Some(check_owner) = maybe_check_owner { ensure!(details.owner == check_owner, Error::::NoPermission); } + + ensure!(!T::Holder::contains_holds(id.clone()), Error::::ContainsHolds); + ensure!(!T::Freezer::contains_freezes(id.clone()), Error::::ContainsFreezes); + details.status = AssetStatus::Destroying; Self::deposit_event(Event::DestructionStarted { asset_id: id }); diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index 8d2f9a63b69b..7dbb55569026 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -693,6 +693,10 @@ pub mod pallet { CallbackFailed, /// The asset ID must be equal to the [`NextAssetId`]. BadAssetId, + /// The asset cannot be destroyed because some accounts for this asset contain holds. + ContainsHolds, + /// The asset cannot be destroyed because some accounts for this asset contain freezes. + ContainsFreezes, } #[pallet::call(weight(>::WeightInfo))] diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index 0f81eea47f57..388da8a3894f 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -115,23 +115,27 @@ pub enum Hook { } parameter_types! { static Frozen: HashMap<(u32, u64), u64> = Default::default(); - static Held: HashMap<(u32, u64), u64> = Default::default(); + static OnHold: HashMap<(u32, u64), u64> = Default::default(); static Hooks: Vec = Default::default(); } pub struct TestHolder; impl BalanceOnHold for TestHolder { fn balance_on_hold(asset: u32, who: &u64) -> Option { - Held::get().get(&(asset, *who)).cloned() + OnHold::get().get(&(asset, *who)).cloned() } fn died(_asset: u32, _who: &u64) { // TODO: Connect with existing hooks list } + + fn contains_holds(asset: AssetId) -> bool { + !OnHold::get().iter().find(|((k, _), _)| &asset == k).is_none() + } } -pub(crate) fn set_held_balance(asset: u32, who: u64, amount: u64) { - Held::mutate(|v| { +pub(crate) fn set_balance_on_hold(asset: u32, who: u64, amount: u64) { + OnHold::mutate(|v| { let amount_on_hold = v.get(&(asset, who)).unwrap_or(&0); if &amount > amount_on_hold { @@ -149,6 +153,12 @@ pub(crate) fn set_held_balance(asset: u32, who: u64, amount: u64) { v.insert((asset, who), amount); }); } + +pub(crate) fn clear_balance_on_hold(asset: u32, who: u64) { + OnHold::mutate(|v| { + v.remove(&(asset, who)); + }); +} pub struct TestFreezer; impl FrozenBalance for TestFreezer { fn frozen_balance(asset: u32, who: &u64) -> Option { @@ -161,6 +171,11 @@ impl FrozenBalance for TestFreezer { // Sanity check: dead accounts have no balance. assert!(Assets::balance(asset, *who).is_zero()); } + + /// Return a value that indicates if there are registered freezes for a given asset. + fn contains_freezes(asset: AssetId) -> bool { + !Frozen::get().iter().find(|((k, _), _)| &asset == k).is_none() + } } pub(crate) fn set_frozen_balance(asset: u32, who: u64, amount: u64) { diff --git a/substrate/frame/assets/src/tests.rs b/substrate/frame/assets/src/tests.rs index ff0f1951be97..def1d52346c2 100644 --- a/substrate/frame/assets/src/tests.rs +++ b/substrate/frame/assets/src/tests.rs @@ -1389,7 +1389,7 @@ fn freezing_and_holds_work() { assert_eq!(Assets::balance(0, 1), 100); // Hold 50 of it - set_held_balance(0, 1, 50); + set_balance_on_hold(0, 1, 50); assert_eq!(Assets::balance(0, 1), 50); assert_eq!(TestHolder::balance_on_hold(0, &1), Some(50)); @@ -1400,7 +1400,7 @@ fn freezing_and_holds_work() { assert_eq!(Assets::reducible_balance(0, &1, true), Ok(39)); // Increasing hold is not necessarily restricted by the frozen balance - set_held_balance(0, 1, 62); + set_balance_on_hold(0, 1, 62); assert_eq!(Assets::reducible_balance(0, &1, true), Ok(28)); // Transfers are bound to the spendable amount @@ -1785,6 +1785,31 @@ fn root_asset_create_should_work() { }); } +#[test] +fn asset_start_destroy_fails_if_there_are_holds_or_freezes() { + new_test_ext().execute_with(|| { + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100)); + + set_frozen_balance(0, 1, 50); + assert_noop!( + Assets::start_destroy(RuntimeOrigin::signed(1), 0), + Error::::ContainsFreezes + ); + + set_balance_on_hold(0, 1, 50); + assert_noop!( + Assets::start_destroy(RuntimeOrigin::signed(1), 0), + Error::::ContainsHolds + ); + + clear_frozen_balance(0, 1); + clear_balance_on_hold(0, 1); + + assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); + }); +} + #[test] fn asset_create_and_destroy_is_reverted_if_callback_fails() { new_test_ext().execute_with(|| { diff --git a/substrate/frame/assets/src/types.rs b/substrate/frame/assets/src/types.rs index 162e88f5dc0d..3b4b82ad7273 100644 --- a/substrate/frame/assets/src/types.rs +++ b/substrate/frame/assets/src/types.rs @@ -220,9 +220,10 @@ pub trait FrozenBalance { fn frozen_balance(asset: AssetId, who: &AccountId) -> Option; /// Called after an account has been removed. - /// - /// NOTE: It is possible that the asset does no longer exist when this hook is called. fn died(asset: AssetId, who: &AccountId); + + /// Return a value that indicates if there are registered freezes for a given asset. + fn contains_freezes(asset: AssetId) -> bool; } impl FrozenBalance for () { @@ -230,6 +231,9 @@ impl FrozenBalance for None } fn died(_: AssetId, _: &AccountId) {} + fn contains_freezes(_: AssetId) -> bool { + false + } } /// This trait indicates a balance that is _on hold_ for an asset account. @@ -251,9 +255,10 @@ pub trait BalanceOnHold { /// /// It is expected that this method is called only when there is not balance /// on hold. Otherwise, an account should not be removed. - /// - /// NOTE: It is possible that the asset no longer exists when this hook is called. fn died(asset: AssetId, who: &AccountId); + + /// Return a value that indicates if there are registered holds for a given asset. + fn contains_holds(asset: AssetId) -> bool; } impl BalanceOnHold for () { @@ -261,6 +266,9 @@ impl BalanceOnHold for None } fn died(_: AssetId, _: &AccountId) {} + fn contains_holds(_: AssetId) -> bool { + false + } } #[derive(Copy, Clone, PartialEq, Eq)] From 0e848ac34ec0162032a797ba58f28839f692119c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Wed, 11 Sep 2024 18:36:42 -0500 Subject: [PATCH 37/47] fix(pallet-assets-freezer): missing import --- substrate/frame/assets-freezer/src/impls.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/assets-freezer/src/impls.rs b/substrate/frame/assets-freezer/src/impls.rs index a73ab46eaf04..11fb4d77ad87 100644 --- a/substrate/frame/assets-freezer/src/impls.rs +++ b/substrate/frame/assets-freezer/src/impls.rs @@ -23,6 +23,7 @@ use frame_support::traits::{ }; use pallet_assets::FrozenBalance; use sp_runtime::traits::Zero; +use storage::StorageDoubleMap; // Implements [`FrozenBalance`] from [`pallet-assets`], so it can understand how much of an // account balance is frozen, and is able to signal to this pallet when to clear the state of an From 3bd72414a3fece4b03de70ded6e7eca476a7481d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 12 Sep 2024 22:39:18 -0500 Subject: [PATCH 38/47] change(pallet-assets): disallow `refund`/`refund_other` for an account's deposit if there are holds or freezes in place / update documentation for `start_destroy` --- substrate/frame/assets/src/functions.rs | 19 +++++++++++++++++++ substrate/frame/assets/src/lib.rs | 3 +++ 2 files changed, 22 insertions(+) diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index b5c80358da08..b38cdf8e6874 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -363,6 +363,16 @@ impl, I: 'static> Pallet { pub(super) fn do_refund(id: T::AssetId, who: T::AccountId, allow_burn: bool) -> DispatchResult { use AssetStatus::*; use ExistenceReason::*; + + ensure!( + T::Holder::balance_on_hold(id.clone(), who).is_none(), + Error::::ContainsHolds + ); + ensure!( + T::Freezer::frozen_balance(id.clone(), who).is_none(), + Error::::ContainsFreezes + ); + let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::::NoDeposit); let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; @@ -397,6 +407,15 @@ impl, I: 'static> Pallet { who: &T::AccountId, maybe_check_caller: Option, ) -> DispatchResult { + ensure!( + T::Holder::balance_on_hold(id.clone(), who).is_none(), + Error::::ContainsHolds + ); + ensure!( + T::Freezer::frozen_balance(id.clone(), who).is_none(), + Error::::ContainsFreezes + ); + let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; let (depositor, deposit) = account.reason.take_deposit_from().ok_or(Error::::NoDeposit)?; diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index 7dbb55569026..f7990e29c45b 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -810,6 +810,9 @@ pub mod pallet { /// /// - `id`: The identifier of the asset to be destroyed. This must identify an existing /// asset. + /// + /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if + /// an account has holds or freezes in place. #[pallet::call_index(2)] pub fn start_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { From 3233d4819c213a0e027d11506e8f26f79a93cc88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 12 Sep 2024 22:47:04 -0500 Subject: [PATCH 39/47] fix(umbrella): revert unintended misformatting of Cargo.toml --- umbrella/Cargo.toml | 1512 ++++++++++++++++++++++++++++--------------- 1 file changed, 980 insertions(+), 532 deletions(-) diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index f5764ac44efe..1d5cbbca875e 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -1,169 +1,617 @@ [package] -description = "Polkadot SDK umbrella crate." -license = "Apache-2.0" name = "polkadot-sdk" version = "0.1.0" +description = "Polkadot SDK umbrella crate." +license = "Apache-2.0" [features] default = ["std"] +std = [ + "asset-test-utils?/std", + "assets-common?/std", + "binary-merkle-tree?/std", + "bp-header-chain?/std", + "bp-messages?/std", + "bp-parachains?/std", + "bp-polkadot-core?/std", + "bp-polkadot?/std", + "bp-relayers?/std", + "bp-runtime?/std", + "bp-test-utils?/std", + "bp-xcm-bridge-hub-router?/std", + "bp-xcm-bridge-hub?/std", + "bridge-hub-common?/std", + "bridge-hub-test-utils?/std", + "bridge-runtime-common?/std", + "cumulus-pallet-aura-ext?/std", + "cumulus-pallet-dmp-queue?/std", + "cumulus-pallet-parachain-system-proc-macro?/std", + "cumulus-pallet-parachain-system?/std", + "cumulus-pallet-session-benchmarking?/std", + "cumulus-pallet-solo-to-para?/std", + "cumulus-pallet-xcm?/std", + "cumulus-pallet-xcmp-queue?/std", + "cumulus-ping?/std", + "cumulus-primitives-aura?/std", + "cumulus-primitives-core?/std", + "cumulus-primitives-parachain-inherent?/std", + "cumulus-primitives-proof-size-hostfunction?/std", + "cumulus-primitives-storage-weight-reclaim?/std", + "cumulus-primitives-timestamp?/std", + "cumulus-primitives-utility?/std", + "cumulus-test-relay-sproof-builder?/std", + "frame-benchmarking-pallet-pov?/std", + "frame-benchmarking?/std", + "frame-election-provider-support?/std", + "frame-executive?/std", + "frame-metadata-hash-extension?/std", + "frame-support-procedural?/std", + "frame-support?/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api?/std", + "frame-system?/std", + "frame-try-runtime?/std", + "pallet-alliance?/std", + "pallet-asset-conversion-ops?/std", + "pallet-asset-conversion-tx-payment?/std", + "pallet-asset-conversion?/std", + "pallet-asset-rate?/std", + "pallet-asset-tx-payment?/std", + "pallet-assets-freezer?/std", + "pallet-assets-holder?/std", + "pallet-assets?/std", + "pallet-atomic-swap?/std", + "pallet-aura?/std", + "pallet-authority-discovery?/std", + "pallet-authorship?/std", + "pallet-babe?/std", + "pallet-bags-list?/std", + "pallet-balances?/std", + "pallet-beefy-mmr?/std", + "pallet-beefy?/std", + "pallet-bounties?/std", + "pallet-bridge-grandpa?/std", + "pallet-bridge-messages?/std", + "pallet-bridge-parachains?/std", + "pallet-bridge-relayers?/std", + "pallet-broker?/std", + "pallet-child-bounties?/std", + "pallet-collator-selection?/std", + "pallet-collective-content?/std", + "pallet-collective?/std", + "pallet-contracts-mock-network?/std", + "pallet-contracts?/std", + "pallet-conviction-voting?/std", + "pallet-core-fellowship?/std", + "pallet-delegated-staking?/std", + "pallet-democracy?/std", + "pallet-dev-mode?/std", + "pallet-election-provider-multi-phase?/std", + "pallet-election-provider-support-benchmarking?/std", + "pallet-elections-phragmen?/std", + "pallet-fast-unstake?/std", + "pallet-glutton?/std", + "pallet-grandpa?/std", + "pallet-identity?/std", + "pallet-im-online?/std", + "pallet-indices?/std", + "pallet-insecure-randomness-collective-flip?/std", + "pallet-lottery?/std", + "pallet-membership?/std", + "pallet-message-queue?/std", + "pallet-migrations?/std", + "pallet-mixnet?/std", + "pallet-mmr?/std", + "pallet-multisig?/std", + "pallet-nft-fractionalization?/std", + "pallet-nfts-runtime-api?/std", + "pallet-nfts?/std", + "pallet-nis?/std", + "pallet-node-authorization?/std", + "pallet-nomination-pools-benchmarking?/std", + "pallet-nomination-pools-runtime-api?/std", + "pallet-nomination-pools?/std", + "pallet-offences-benchmarking?/std", + "pallet-offences?/std", + "pallet-paged-list?/std", + "pallet-parameters?/std", + "pallet-preimage?/std", + "pallet-proxy?/std", + "pallet-ranked-collective?/std", + "pallet-recovery?/std", + "pallet-referenda?/std", + "pallet-remark?/std", + "pallet-revive-fixtures?/std", + "pallet-revive-mock-network?/std", + "pallet-revive?/std", + "pallet-root-offences?/std", + "pallet-root-testing?/std", + "pallet-safe-mode?/std", + "pallet-salary?/std", + "pallet-scheduler?/std", + "pallet-scored-pool?/std", + "pallet-session-benchmarking?/std", + "pallet-session?/std", + "pallet-skip-feeless-payment?/std", + "pallet-society?/std", + "pallet-staking-reward-fn?/std", + "pallet-staking-runtime-api?/std", + "pallet-staking?/std", + "pallet-state-trie-migration?/std", + "pallet-statement?/std", + "pallet-sudo?/std", + "pallet-timestamp?/std", + "pallet-tips?/std", + "pallet-transaction-payment-rpc-runtime-api?/std", + "pallet-transaction-payment?/std", + "pallet-transaction-storage?/std", + "pallet-treasury?/std", + "pallet-tx-pause?/std", + "pallet-uniques?/std", + "pallet-utility?/std", + "pallet-vesting?/std", + "pallet-whitelist?/std", + "pallet-xcm-benchmarks?/std", + "pallet-xcm-bridge-hub-router?/std", + "pallet-xcm-bridge-hub?/std", + "pallet-xcm?/std", + "parachains-common?/std", + "parachains-runtimes-test-utils?/std", + "polkadot-core-primitives?/std", + "polkadot-parachain-primitives?/std", + "polkadot-primitives?/std", + "polkadot-runtime-common?/std", + "polkadot-runtime-metrics?/std", + "polkadot-runtime-parachains?/std", + "polkadot-sdk-frame?/std", + "sc-executor?/std", + "slot-range-helper?/std", + "snowbridge-beacon-primitives?/std", + "snowbridge-core?/std", + "snowbridge-ethereum?/std", + "snowbridge-outbound-queue-merkle-tree?/std", + "snowbridge-outbound-queue-runtime-api?/std", + "snowbridge-pallet-ethereum-client-fixtures?/std", + "snowbridge-pallet-ethereum-client?/std", + "snowbridge-pallet-inbound-queue-fixtures?/std", + "snowbridge-pallet-inbound-queue?/std", + "snowbridge-pallet-outbound-queue?/std", + "snowbridge-pallet-system?/std", + "snowbridge-router-primitives?/std", + "snowbridge-runtime-common?/std", + "snowbridge-runtime-test-common?/std", + "snowbridge-system-runtime-api?/std", + "sp-api-proc-macro?/std", + "sp-api?/std", + "sp-application-crypto?/std", + "sp-arithmetic?/std", + "sp-authority-discovery?/std", + "sp-block-builder?/std", + "sp-consensus-aura?/std", + "sp-consensus-babe?/std", + "sp-consensus-beefy?/std", + "sp-consensus-grandpa?/std", + "sp-consensus-pow?/std", + "sp-consensus-slots?/std", + "sp-core-hashing?/std", + "sp-core?/std", + "sp-crypto-ec-utils?/std", + "sp-crypto-hashing?/std", + "sp-debug-derive?/std", + "sp-externalities?/std", + "sp-genesis-builder?/std", + "sp-inherents?/std", + "sp-io?/std", + "sp-keyring?/std", + "sp-keystore?/std", + "sp-metadata-ir?/std", + "sp-mixnet?/std", + "sp-mmr-primitives?/std", + "sp-npos-elections?/std", + "sp-offchain?/std", + "sp-runtime-interface?/std", + "sp-runtime?/std", + "sp-session?/std", + "sp-staking?/std", + "sp-state-machine?/std", + "sp-statement-store?/std", + "sp-std?/std", + "sp-storage?/std", + "sp-timestamp?/std", + "sp-tracing?/std", + "sp-transaction-pool?/std", + "sp-transaction-storage-proof?/std", + "sp-trie?/std", + "sp-version?/std", + "sp-wasm-interface?/std", + "sp-weights?/std", + "staging-parachain-info?/std", + "staging-xcm-builder?/std", + "staging-xcm-executor?/std", + "staging-xcm?/std", + "substrate-bip39?/std", + "testnet-parachains-constants?/std", + "xcm-runtime-apis?/std", +] +runtime-benchmarks = [ + "assets-common?/runtime-benchmarks", + "bridge-hub-common?/runtime-benchmarks", + "bridge-runtime-common?/runtime-benchmarks", + "cumulus-pallet-dmp-queue?/runtime-benchmarks", + "cumulus-pallet-parachain-system?/runtime-benchmarks", + "cumulus-pallet-session-benchmarking?/runtime-benchmarks", + "cumulus-pallet-xcmp-queue?/runtime-benchmarks", + "cumulus-primitives-core?/runtime-benchmarks", + "cumulus-primitives-utility?/runtime-benchmarks", + "frame-benchmarking-cli?/runtime-benchmarks", + "frame-benchmarking-pallet-pov?/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-election-provider-support?/runtime-benchmarks", + "frame-support?/runtime-benchmarks", + "frame-system-benchmarking?/runtime-benchmarks", + "frame-system?/runtime-benchmarks", + "pallet-alliance?/runtime-benchmarks", + "pallet-asset-conversion-ops?/runtime-benchmarks", + "pallet-asset-conversion?/runtime-benchmarks", + "pallet-asset-rate?/runtime-benchmarks", + "pallet-asset-tx-payment?/runtime-benchmarks", + "pallet-assets-freezer?/runtime-benchmarks", + "pallet-assets-holder?/runtime-benchmarks", + "pallet-assets?/runtime-benchmarks", + "pallet-babe?/runtime-benchmarks", + "pallet-bags-list?/runtime-benchmarks", + "pallet-balances?/runtime-benchmarks", + "pallet-beefy-mmr?/runtime-benchmarks", + "pallet-bounties?/runtime-benchmarks", + "pallet-bridge-grandpa?/runtime-benchmarks", + "pallet-bridge-messages?/runtime-benchmarks", + "pallet-bridge-parachains?/runtime-benchmarks", + "pallet-bridge-relayers?/runtime-benchmarks", + "pallet-broker?/runtime-benchmarks", + "pallet-child-bounties?/runtime-benchmarks", + "pallet-collator-selection?/runtime-benchmarks", + "pallet-collective-content?/runtime-benchmarks", + "pallet-collective?/runtime-benchmarks", + "pallet-contracts-mock-network?/runtime-benchmarks", + "pallet-contracts?/runtime-benchmarks", + "pallet-conviction-voting?/runtime-benchmarks", + "pallet-core-fellowship?/runtime-benchmarks", + "pallet-delegated-staking?/runtime-benchmarks", + "pallet-democracy?/runtime-benchmarks", + "pallet-election-provider-multi-phase?/runtime-benchmarks", + "pallet-election-provider-support-benchmarking?/runtime-benchmarks", + "pallet-elections-phragmen?/runtime-benchmarks", + "pallet-fast-unstake?/runtime-benchmarks", + "pallet-glutton?/runtime-benchmarks", + "pallet-grandpa?/runtime-benchmarks", + "pallet-identity?/runtime-benchmarks", + "pallet-im-online?/runtime-benchmarks", + "pallet-indices?/runtime-benchmarks", + "pallet-lottery?/runtime-benchmarks", + "pallet-membership?/runtime-benchmarks", + "pallet-message-queue?/runtime-benchmarks", + "pallet-migrations?/runtime-benchmarks", + "pallet-mixnet?/runtime-benchmarks", + "pallet-mmr?/runtime-benchmarks", + "pallet-multisig?/runtime-benchmarks", + "pallet-nft-fractionalization?/runtime-benchmarks", + "pallet-nfts?/runtime-benchmarks", + "pallet-nis?/runtime-benchmarks", + "pallet-nomination-pools-benchmarking?/runtime-benchmarks", + "pallet-nomination-pools?/runtime-benchmarks", + "pallet-offences-benchmarking?/runtime-benchmarks", + "pallet-offences?/runtime-benchmarks", + "pallet-paged-list?/runtime-benchmarks", + "pallet-parameters?/runtime-benchmarks", + "pallet-preimage?/runtime-benchmarks", + "pallet-proxy?/runtime-benchmarks", + "pallet-ranked-collective?/runtime-benchmarks", + "pallet-recovery?/runtime-benchmarks", + "pallet-referenda?/runtime-benchmarks", + "pallet-remark?/runtime-benchmarks", + "pallet-revive-mock-network?/runtime-benchmarks", + "pallet-revive?/runtime-benchmarks", + "pallet-root-offences?/runtime-benchmarks", + "pallet-safe-mode?/runtime-benchmarks", + "pallet-salary?/runtime-benchmarks", + "pallet-scheduler?/runtime-benchmarks", + "pallet-session-benchmarking?/runtime-benchmarks", + "pallet-skip-feeless-payment?/runtime-benchmarks", + "pallet-society?/runtime-benchmarks", + "pallet-staking?/runtime-benchmarks", + "pallet-state-trie-migration?/runtime-benchmarks", + "pallet-sudo?/runtime-benchmarks", + "pallet-timestamp?/runtime-benchmarks", + "pallet-tips?/runtime-benchmarks", + "pallet-transaction-storage?/runtime-benchmarks", + "pallet-treasury?/runtime-benchmarks", + "pallet-tx-pause?/runtime-benchmarks", + "pallet-uniques?/runtime-benchmarks", + "pallet-utility?/runtime-benchmarks", + "pallet-vesting?/runtime-benchmarks", + "pallet-whitelist?/runtime-benchmarks", + "pallet-xcm-benchmarks?/runtime-benchmarks", + "pallet-xcm-bridge-hub-router?/runtime-benchmarks", + "pallet-xcm-bridge-hub?/runtime-benchmarks", + "pallet-xcm?/runtime-benchmarks", + "parachains-common?/runtime-benchmarks", + "polkadot-cli?/runtime-benchmarks", + "polkadot-node-metrics?/runtime-benchmarks", + "polkadot-parachain-lib?/runtime-benchmarks", + "polkadot-parachain-primitives?/runtime-benchmarks", + "polkadot-primitives?/runtime-benchmarks", + "polkadot-runtime-common?/runtime-benchmarks", + "polkadot-runtime-parachains?/runtime-benchmarks", + "polkadot-sdk-frame?/runtime-benchmarks", + "polkadot-service?/runtime-benchmarks", + "sc-client-db?/runtime-benchmarks", + "sc-service?/runtime-benchmarks", + "snowbridge-core?/runtime-benchmarks", + "snowbridge-pallet-ethereum-client-fixtures?/runtime-benchmarks", + "snowbridge-pallet-ethereum-client?/runtime-benchmarks", + "snowbridge-pallet-inbound-queue-fixtures?/runtime-benchmarks", + "snowbridge-pallet-inbound-queue?/runtime-benchmarks", + "snowbridge-pallet-outbound-queue?/runtime-benchmarks", + "snowbridge-pallet-system?/runtime-benchmarks", + "snowbridge-router-primitives?/runtime-benchmarks", + "snowbridge-runtime-common?/runtime-benchmarks", + "snowbridge-runtime-test-common?/runtime-benchmarks", + "sp-runtime?/runtime-benchmarks", + "sp-staking?/runtime-benchmarks", + "staging-node-inspect?/runtime-benchmarks", + "staging-xcm-builder?/runtime-benchmarks", + "staging-xcm-executor?/runtime-benchmarks", + "xcm-runtime-apis?/runtime-benchmarks", +] +try-runtime = [ + "cumulus-pallet-aura-ext?/try-runtime", + "cumulus-pallet-dmp-queue?/try-runtime", + "cumulus-pallet-parachain-system?/try-runtime", + "cumulus-pallet-solo-to-para?/try-runtime", + "cumulus-pallet-xcm?/try-runtime", + "cumulus-pallet-xcmp-queue?/try-runtime", + "cumulus-ping?/try-runtime", + "frame-benchmarking-pallet-pov?/try-runtime", + "frame-election-provider-support?/try-runtime", + "frame-executive?/try-runtime", + "frame-support?/try-runtime", + "frame-system?/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-alliance?/try-runtime", + "pallet-asset-conversion-ops?/try-runtime", + "pallet-asset-conversion-tx-payment?/try-runtime", + "pallet-asset-conversion?/try-runtime", + "pallet-asset-rate?/try-runtime", + "pallet-asset-tx-payment?/try-runtime", + "pallet-assets-freezer?/try-runtime", + "pallet-assets-holder?/try-runtime", + "pallet-assets?/try-runtime", + "pallet-atomic-swap?/try-runtime", + "pallet-aura?/try-runtime", + "pallet-authority-discovery?/try-runtime", + "pallet-authorship?/try-runtime", + "pallet-babe?/try-runtime", + "pallet-bags-list?/try-runtime", + "pallet-balances?/try-runtime", + "pallet-beefy-mmr?/try-runtime", + "pallet-beefy?/try-runtime", + "pallet-bounties?/try-runtime", + "pallet-bridge-grandpa?/try-runtime", + "pallet-bridge-messages?/try-runtime", + "pallet-bridge-parachains?/try-runtime", + "pallet-bridge-relayers?/try-runtime", + "pallet-broker?/try-runtime", + "pallet-child-bounties?/try-runtime", + "pallet-collator-selection?/try-runtime", + "pallet-collective-content?/try-runtime", + "pallet-collective?/try-runtime", + "pallet-contracts?/try-runtime", + "pallet-conviction-voting?/try-runtime", + "pallet-core-fellowship?/try-runtime", + "pallet-delegated-staking?/try-runtime", + "pallet-democracy?/try-runtime", + "pallet-dev-mode?/try-runtime", + "pallet-election-provider-multi-phase?/try-runtime", + "pallet-elections-phragmen?/try-runtime", + "pallet-fast-unstake?/try-runtime", + "pallet-glutton?/try-runtime", + "pallet-grandpa?/try-runtime", + "pallet-identity?/try-runtime", + "pallet-im-online?/try-runtime", + "pallet-indices?/try-runtime", + "pallet-insecure-randomness-collective-flip?/try-runtime", + "pallet-lottery?/try-runtime", + "pallet-membership?/try-runtime", + "pallet-message-queue?/try-runtime", + "pallet-migrations?/try-runtime", + "pallet-mixnet?/try-runtime", + "pallet-mmr?/try-runtime", + "pallet-multisig?/try-runtime", + "pallet-nft-fractionalization?/try-runtime", + "pallet-nfts?/try-runtime", + "pallet-nis?/try-runtime", + "pallet-node-authorization?/try-runtime", + "pallet-nomination-pools?/try-runtime", + "pallet-offences?/try-runtime", + "pallet-paged-list?/try-runtime", + "pallet-parameters?/try-runtime", + "pallet-preimage?/try-runtime", + "pallet-proxy?/try-runtime", + "pallet-ranked-collective?/try-runtime", + "pallet-recovery?/try-runtime", + "pallet-referenda?/try-runtime", + "pallet-remark?/try-runtime", + "pallet-revive?/try-runtime", + "pallet-root-offences?/try-runtime", + "pallet-root-testing?/try-runtime", + "pallet-safe-mode?/try-runtime", + "pallet-salary?/try-runtime", + "pallet-scheduler?/try-runtime", + "pallet-scored-pool?/try-runtime", + "pallet-session?/try-runtime", + "pallet-skip-feeless-payment?/try-runtime", + "pallet-society?/try-runtime", + "pallet-staking?/try-runtime", + "pallet-state-trie-migration?/try-runtime", + "pallet-statement?/try-runtime", + "pallet-sudo?/try-runtime", + "pallet-timestamp?/try-runtime", + "pallet-tips?/try-runtime", + "pallet-transaction-payment?/try-runtime", + "pallet-transaction-storage?/try-runtime", + "pallet-treasury?/try-runtime", + "pallet-tx-pause?/try-runtime", + "pallet-uniques?/try-runtime", + "pallet-utility?/try-runtime", + "pallet-vesting?/try-runtime", + "pallet-whitelist?/try-runtime", + "pallet-xcm-bridge-hub-router?/try-runtime", + "pallet-xcm-bridge-hub?/try-runtime", + "pallet-xcm?/try-runtime", + "polkadot-cli?/try-runtime", + "polkadot-parachain-lib?/try-runtime", + "polkadot-runtime-common?/try-runtime", + "polkadot-runtime-parachains?/try-runtime", + "polkadot-sdk-frame?/try-runtime", + "polkadot-service?/try-runtime", + "snowbridge-pallet-ethereum-client?/try-runtime", + "snowbridge-pallet-inbound-queue?/try-runtime", + "snowbridge-pallet-outbound-queue?/try-runtime", + "snowbridge-pallet-system?/try-runtime", + "sp-runtime?/try-runtime", + "staging-parachain-info?/try-runtime", +] +serde = [ + "bp-polkadot-core?/serde", + "frame-benchmarking?/serde", + "pallet-asset-tx-payment?/serde", + "pallet-beefy-mmr?/serde", + "pallet-beefy?/serde", + "pallet-contracts?/serde", + "pallet-conviction-voting?/serde", + "pallet-democracy?/serde", + "pallet-message-queue?/serde", + "pallet-offences?/serde", + "pallet-parameters?/serde", + "pallet-referenda?/serde", + "pallet-remark?/serde", + "pallet-revive?/serde", + "pallet-state-trie-migration?/serde", + "pallet-tips?/serde", + "pallet-transaction-payment?/serde", + "pallet-transaction-storage?/serde", + "pallet-treasury?/serde", + "pallet-xcm?/serde", + "snowbridge-beacon-primitives?/serde", + "snowbridge-core?/serde", + "snowbridge-ethereum?/serde", + "snowbridge-pallet-ethereum-client?/serde", + "snowbridge-pallet-inbound-queue?/serde", + "sp-application-crypto?/serde", + "sp-arithmetic?/serde", + "sp-authority-discovery?/serde", + "sp-consensus-aura?/serde", + "sp-consensus-babe?/serde", + "sp-consensus-beefy?/serde", + "sp-consensus-grandpa?/serde", + "sp-consensus-slots?/serde", + "sp-core?/serde", + "sp-mmr-primitives?/serde", + "sp-npos-elections?/serde", + "sp-runtime?/serde", + "sp-staking?/serde", + "sp-statement-store?/serde", + "sp-storage?/serde", + "sp-version?/serde", + "sp-weights?/serde", +] experimental = [ - "frame-support-procedural?/experimental", - "frame-support?/experimental", - "frame-system?/experimental", - "polkadot-sdk-frame?/experimental", + "frame-support-procedural?/experimental", + "frame-support?/experimental", + "frame-system?/experimental", + "polkadot-sdk-frame?/experimental", ] -node = [ - "asset-test-utils", - "bridge-hub-test-utils", - "cumulus-client-cli", - "cumulus-client-collator", - "cumulus-client-consensus-aura", - "cumulus-client-consensus-common", - "cumulus-client-consensus-proposer", - "cumulus-client-consensus-relay-chain", - "cumulus-client-network", - "cumulus-client-parachain-inherent", - "cumulus-client-pov-recovery", - "cumulus-client-service", - "cumulus-relay-chain-inprocess-interface", - "cumulus-relay-chain-interface", - "cumulus-relay-chain-minimal-node", - "cumulus-relay-chain-rpc-interface", - "cumulus-test-relay-sproof-builder", - "emulated-integration-tests-common", - "fork-tree", - "frame-benchmarking-cli", - "frame-remote-externalities", - "frame-support-procedural-tools", - "generate-bags", - "mmr-gadget", - "mmr-rpc", - "pallet-contracts-mock-network", - "pallet-revive-mock-network", - "pallet-transaction-payment-rpc", - "parachains-runtimes-test-utils", - "polkadot-approval-distribution", - "polkadot-availability-bitfield-distribution", - "polkadot-availability-distribution", - "polkadot-availability-recovery", - "polkadot-cli", - "polkadot-collator-protocol", - "polkadot-dispute-distribution", - "polkadot-erasure-coding", - "polkadot-gossip-support", - "polkadot-network-bridge", - "polkadot-node-collation-generation", - "polkadot-node-core-approval-voting", - "polkadot-node-core-av-store", - "polkadot-node-core-backing", - "polkadot-node-core-bitfield-signing", - "polkadot-node-core-candidate-validation", - "polkadot-node-core-chain-api", - "polkadot-node-core-chain-selection", - "polkadot-node-core-dispute-coordinator", - "polkadot-node-core-parachains-inherent", - "polkadot-node-core-prospective-parachains", - "polkadot-node-core-provisioner", - "polkadot-node-core-pvf", - "polkadot-node-core-pvf-checker", - "polkadot-node-core-pvf-common", - "polkadot-node-core-pvf-execute-worker", - "polkadot-node-core-pvf-prepare-worker", - "polkadot-node-core-runtime-api", - "polkadot-node-jaeger", - "polkadot-node-metrics", - "polkadot-node-network-protocol", - "polkadot-node-primitives", - "polkadot-node-subsystem", - "polkadot-node-subsystem-types", - "polkadot-node-subsystem-util", - "polkadot-overseer", - "polkadot-parachain-lib", - "polkadot-rpc", - "polkadot-service", - "polkadot-statement-distribution", - "polkadot-statement-table", - "sc-allocator", - "sc-authority-discovery", - "sc-basic-authorship", - "sc-block-builder", - "sc-chain-spec", - "sc-cli", - "sc-client-api", - "sc-client-db", - "sc-consensus", - "sc-consensus-aura", - "sc-consensus-babe", - "sc-consensus-babe-rpc", - "sc-consensus-beefy", - "sc-consensus-beefy-rpc", - "sc-consensus-epochs", - "sc-consensus-grandpa", - "sc-consensus-grandpa-rpc", - "sc-consensus-manual-seal", - "sc-consensus-pow", - "sc-consensus-slots", - "sc-executor", - "sc-executor-common", - "sc-executor-polkavm", - "sc-executor-wasmtime", - "sc-informant", - "sc-keystore", - "sc-mixnet", - "sc-network", - "sc-network-common", - "sc-network-gossip", - "sc-network-light", - "sc-network-statement", - "sc-network-sync", - "sc-network-transactions", - "sc-network-types", - "sc-offchain", - "sc-proposer-metrics", - "sc-rpc", - "sc-rpc-api", - "sc-rpc-server", - "sc-rpc-spec-v2", - "sc-service", - "sc-state-db", - "sc-statement-store", - "sc-storage-monitor", - "sc-sync-state-rpc", - "sc-sysinfo", - "sc-telemetry", - "sc-tracing", - "sc-transaction-pool", - "sc-transaction-pool-api", - "sc-utils", - "snowbridge-runtime-test-common", - "sp-blockchain", - "sp-consensus", - "sp-core-hashing", - "sp-core-hashing-proc-macro", - "sp-database", - "sp-maybe-compressed-blob", - "sp-panic-handler", - "sp-rpc", - "staging-chain-spec-builder", - "staging-node-inspect", - "staging-tracking-allocator", - "std", - "subkey", - "substrate-build-script-utils", - "substrate-frame-rpc-support", - "substrate-frame-rpc-system", - "substrate-prometheus-endpoint", - "substrate-rpc-client", - "substrate-state-trie-migration-rpc", - "substrate-wasm-builder", - "tracing-gum", - "xcm-emulator", - "xcm-simulator", +with-tracing = [ + "frame-executive?/with-tracing", + "frame-executive?/with-tracing", + "sp-io?/with-tracing", + "sp-io?/with-tracing", + "sp-tracing?/with-tracing", + "sp-tracing?/with-tracing", +] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-assets-holder", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-fixtures", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] +runtime = [ + "frame-benchmarking", + "frame-benchmarking-pallet-pov", + "frame-election-provider-solution-type", + "frame-election-provider-support", + "frame-executive", + "frame-metadata-hash-extension", + "frame-support", + "frame-support-procedural", + "frame-support-procedural-tools-derive", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "polkadot-sdk-frame", + "polkadot-sdk-frame?/runtime", + "sp-api", + "sp-api-proc-macro", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-block-builder", + "sp-consensus-aura", + "sp-consensus-babe", + "sp-consensus-beefy", + "sp-consensus-grandpa", + "sp-consensus-pow", + "sp-consensus-slots", + "sp-core", + "sp-crypto-ec-utils", + "sp-crypto-hashing", + "sp-crypto-hashing-proc-macro", + "sp-debug-derive", + "sp-externalities", + "sp-genesis-builder", + "sp-inherents", + "sp-io", + "sp-keyring", + "sp-keystore", + "sp-metadata-ir", + "sp-mixnet", + "sp-mmr-primitives", + "sp-npos-elections", + "sp-offchain", + "sp-runtime", + "sp-runtime-interface", + "sp-runtime-interface-proc-macro", + "sp-session", + "sp-staking", + "sp-state-machine", + "sp-statement-store", + "sp-std", + "sp-storage", + "sp-timestamp", + "sp-tracing", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "sp-trie", + "sp-version", + "sp-version-proc-macro", + "sp-wasm-interface", + "sp-weights", +] +node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain-lib", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] +tuples-96 = [ + "frame-support-procedural?/tuples-96", + "frame-support?/tuples-96", ] riscv = [ - "pallet-revive-fixtures?/riscv", - "pallet-revive-mock-network?/riscv", - "pallet-revive?/riscv", + "pallet-revive-fixtures?/riscv", + "pallet-revive-mock-network?/riscv", + "pallet-revive?/riscv", ] [package.edition] @@ -173,1880 +621,1880 @@ workspace = true workspace = true [dependencies.assets-common] +path = "../cumulus/parachains/runtimes/assets/common" default-features = false optional = true -path = "../cumulus/parachains/runtimes/assets/common" [dependencies.binary-merkle-tree] +path = "../substrate/utils/binary-merkle-tree" default-features = false optional = true -path = "../substrate/utils/binary-merkle-tree" [dependencies.bp-header-chain] +path = "../bridges/primitives/header-chain" default-features = false optional = true -path = "../bridges/primitives/header-chain" [dependencies.bp-messages] +path = "../bridges/primitives/messages" default-features = false optional = true -path = "../bridges/primitives/messages" [dependencies.bp-parachains] +path = "../bridges/primitives/parachains" default-features = false optional = true -path = "../bridges/primitives/parachains" [dependencies.bp-polkadot] +path = "../bridges/chains/chain-polkadot" default-features = false optional = true -path = "../bridges/chains/chain-polkadot" [dependencies.bp-polkadot-core] +path = "../bridges/primitives/polkadot-core" default-features = false optional = true -path = "../bridges/primitives/polkadot-core" [dependencies.bp-relayers] +path = "../bridges/primitives/relayers" default-features = false optional = true -path = "../bridges/primitives/relayers" [dependencies.bp-runtime] +path = "../bridges/primitives/runtime" default-features = false optional = true -path = "../bridges/primitives/runtime" [dependencies.bp-test-utils] +path = "../bridges/primitives/test-utils" default-features = false optional = true -path = "../bridges/primitives/test-utils" [dependencies.bp-xcm-bridge-hub] +path = "../bridges/primitives/xcm-bridge-hub" default-features = false optional = true -path = "../bridges/primitives/xcm-bridge-hub" [dependencies.bp-xcm-bridge-hub-router] +path = "../bridges/primitives/xcm-bridge-hub-router" default-features = false optional = true -path = "../bridges/primitives/xcm-bridge-hub-router" [dependencies.bridge-hub-common] +path = "../cumulus/parachains/runtimes/bridge-hubs/common" default-features = false optional = true -path = "../cumulus/parachains/runtimes/bridge-hubs/common" [dependencies.bridge-runtime-common] +path = "../bridges/bin/runtime-common" default-features = false optional = true -path = "../bridges/bin/runtime-common" [dependencies.cumulus-pallet-aura-ext] +path = "../cumulus/pallets/aura-ext" default-features = false optional = true -path = "../cumulus/pallets/aura-ext" [dependencies.cumulus-pallet-dmp-queue] +path = "../cumulus/pallets/dmp-queue" default-features = false optional = true -path = "../cumulus/pallets/dmp-queue" [dependencies.cumulus-pallet-parachain-system] +path = "../cumulus/pallets/parachain-system" default-features = false optional = true -path = "../cumulus/pallets/parachain-system" [dependencies.cumulus-pallet-parachain-system-proc-macro] +path = "../cumulus/pallets/parachain-system/proc-macro" default-features = false optional = true -path = "../cumulus/pallets/parachain-system/proc-macro" [dependencies.cumulus-pallet-session-benchmarking] +path = "../cumulus/pallets/session-benchmarking" default-features = false optional = true -path = "../cumulus/pallets/session-benchmarking" [dependencies.cumulus-pallet-solo-to-para] +path = "../cumulus/pallets/solo-to-para" default-features = false optional = true -path = "../cumulus/pallets/solo-to-para" [dependencies.cumulus-pallet-xcm] +path = "../cumulus/pallets/xcm" default-features = false optional = true -path = "../cumulus/pallets/xcm" [dependencies.cumulus-pallet-xcmp-queue] +path = "../cumulus/pallets/xcmp-queue" default-features = false optional = true -path = "../cumulus/pallets/xcmp-queue" [dependencies.cumulus-ping] +path = "../cumulus/parachains/pallets/ping" default-features = false optional = true -path = "../cumulus/parachains/pallets/ping" [dependencies.cumulus-primitives-aura] +path = "../cumulus/primitives/aura" default-features = false optional = true -path = "../cumulus/primitives/aura" [dependencies.cumulus-primitives-core] +path = "../cumulus/primitives/core" default-features = false optional = true -path = "../cumulus/primitives/core" [dependencies.cumulus-primitives-parachain-inherent] +path = "../cumulus/primitives/parachain-inherent" default-features = false optional = true -path = "../cumulus/primitives/parachain-inherent" [dependencies.cumulus-primitives-proof-size-hostfunction] +path = "../cumulus/primitives/proof-size-hostfunction" default-features = false optional = true -path = "../cumulus/primitives/proof-size-hostfunction" [dependencies.cumulus-primitives-storage-weight-reclaim] +path = "../cumulus/primitives/storage-weight-reclaim" default-features = false optional = true -path = "../cumulus/primitives/storage-weight-reclaim" [dependencies.cumulus-primitives-timestamp] +path = "../cumulus/primitives/timestamp" default-features = false optional = true -path = "../cumulus/primitives/timestamp" [dependencies.cumulus-primitives-utility] +path = "../cumulus/primitives/utility" default-features = false optional = true -path = "../cumulus/primitives/utility" [dependencies.frame-benchmarking] +path = "../substrate/frame/benchmarking" default-features = false optional = true -path = "../substrate/frame/benchmarking" [dependencies.frame-benchmarking-pallet-pov] +path = "../substrate/frame/benchmarking/pov" default-features = false optional = true -path = "../substrate/frame/benchmarking/pov" [dependencies.frame-election-provider-solution-type] +path = "../substrate/frame/election-provider-support/solution-type" default-features = false optional = true -path = "../substrate/frame/election-provider-support/solution-type" [dependencies.frame-election-provider-support] +path = "../substrate/frame/election-provider-support" default-features = false optional = true -path = "../substrate/frame/election-provider-support" [dependencies.frame-executive] +path = "../substrate/frame/executive" default-features = false optional = true -path = "../substrate/frame/executive" [dependencies.frame-metadata-hash-extension] +path = "../substrate/frame/metadata-hash-extension" default-features = false optional = true -path = "../substrate/frame/metadata-hash-extension" [dependencies.frame-support] +path = "../substrate/frame/support" default-features = false optional = true -path = "../substrate/frame/support" [dependencies.frame-support-procedural] +path = "../substrate/frame/support/procedural" default-features = false optional = true -path = "../substrate/frame/support/procedural" [dependencies.frame-support-procedural-tools-derive] +path = "../substrate/frame/support/procedural/tools/derive" default-features = false optional = true -path = "../substrate/frame/support/procedural/tools/derive" [dependencies.frame-system] +path = "../substrate/frame/system" default-features = false optional = true -path = "../substrate/frame/system" [dependencies.frame-system-benchmarking] +path = "../substrate/frame/system/benchmarking" default-features = false optional = true -path = "../substrate/frame/system/benchmarking" [dependencies.frame-system-rpc-runtime-api] +path = "../substrate/frame/system/rpc/runtime-api" default-features = false optional = true -path = "../substrate/frame/system/rpc/runtime-api" [dependencies.frame-try-runtime] +path = "../substrate/frame/try-runtime" default-features = false optional = true -path = "../substrate/frame/try-runtime" [dependencies.pallet-alliance] +path = "../substrate/frame/alliance" default-features = false optional = true -path = "../substrate/frame/alliance" [dependencies.pallet-asset-conversion] +path = "../substrate/frame/asset-conversion" default-features = false optional = true -path = "../substrate/frame/asset-conversion" [dependencies.pallet-asset-conversion-ops] +path = "../substrate/frame/asset-conversion/ops" default-features = false optional = true -path = "../substrate/frame/asset-conversion/ops" [dependencies.pallet-asset-conversion-tx-payment] +path = "../substrate/frame/transaction-payment/asset-conversion-tx-payment" default-features = false optional = true -path = "../substrate/frame/transaction-payment/asset-conversion-tx-payment" [dependencies.pallet-asset-rate] +path = "../substrate/frame/asset-rate" default-features = false optional = true -path = "../substrate/frame/asset-rate" [dependencies.pallet-asset-tx-payment] +path = "../substrate/frame/transaction-payment/asset-tx-payment" default-features = false optional = true -path = "../substrate/frame/transaction-payment/asset-tx-payment" [dependencies.pallet-assets] +path = "../substrate/frame/assets" default-features = false optional = true -path = "../substrate/frame/assets" [dependencies.pallet-assets-freezer] +path = "../substrate/frame/assets-freezer" default-features = false optional = true -path = "../substrate/frame/assets-freezer" [dependencies.pallet-assets-holder] +path = "../substrate/frame/assets-holder" default-features = false optional = true -path = "../substrate/frame/assets-holder" [dependencies.pallet-atomic-swap] +path = "../substrate/frame/atomic-swap" default-features = false optional = true -path = "../substrate/frame/atomic-swap" [dependencies.pallet-aura] +path = "../substrate/frame/aura" default-features = false optional = true -path = "../substrate/frame/aura" [dependencies.pallet-authority-discovery] +path = "../substrate/frame/authority-discovery" default-features = false optional = true -path = "../substrate/frame/authority-discovery" [dependencies.pallet-authorship] +path = "../substrate/frame/authorship" default-features = false optional = true -path = "../substrate/frame/authorship" [dependencies.pallet-babe] +path = "../substrate/frame/babe" default-features = false optional = true -path = "../substrate/frame/babe" [dependencies.pallet-bags-list] +path = "../substrate/frame/bags-list" default-features = false optional = true -path = "../substrate/frame/bags-list" [dependencies.pallet-balances] +path = "../substrate/frame/balances" default-features = false optional = true -path = "../substrate/frame/balances" [dependencies.pallet-beefy] +path = "../substrate/frame/beefy" default-features = false optional = true -path = "../substrate/frame/beefy" [dependencies.pallet-beefy-mmr] +path = "../substrate/frame/beefy-mmr" default-features = false optional = true -path = "../substrate/frame/beefy-mmr" [dependencies.pallet-bounties] +path = "../substrate/frame/bounties" default-features = false optional = true -path = "../substrate/frame/bounties" [dependencies.pallet-bridge-grandpa] +path = "../bridges/modules/grandpa" default-features = false optional = true -path = "../bridges/modules/grandpa" [dependencies.pallet-bridge-messages] +path = "../bridges/modules/messages" default-features = false optional = true -path = "../bridges/modules/messages" [dependencies.pallet-bridge-parachains] +path = "../bridges/modules/parachains" default-features = false optional = true -path = "../bridges/modules/parachains" [dependencies.pallet-bridge-relayers] +path = "../bridges/modules/relayers" default-features = false optional = true -path = "../bridges/modules/relayers" [dependencies.pallet-broker] +path = "../substrate/frame/broker" default-features = false optional = true -path = "../substrate/frame/broker" [dependencies.pallet-child-bounties] +path = "../substrate/frame/child-bounties" default-features = false optional = true -path = "../substrate/frame/child-bounties" [dependencies.pallet-collator-selection] +path = "../cumulus/pallets/collator-selection" default-features = false optional = true -path = "../cumulus/pallets/collator-selection" [dependencies.pallet-collective] +path = "../substrate/frame/collective" default-features = false optional = true -path = "../substrate/frame/collective" [dependencies.pallet-collective-content] +path = "../cumulus/parachains/pallets/collective-content" default-features = false optional = true -path = "../cumulus/parachains/pallets/collective-content" [dependencies.pallet-contracts] +path = "../substrate/frame/contracts" default-features = false optional = true -path = "../substrate/frame/contracts" [dependencies.pallet-contracts-proc-macro] +path = "../substrate/frame/contracts/proc-macro" default-features = false optional = true -path = "../substrate/frame/contracts/proc-macro" [dependencies.pallet-contracts-uapi] +path = "../substrate/frame/contracts/uapi" default-features = false optional = true -path = "../substrate/frame/contracts/uapi" [dependencies.pallet-conviction-voting] +path = "../substrate/frame/conviction-voting" default-features = false optional = true -path = "../substrate/frame/conviction-voting" [dependencies.pallet-core-fellowship] +path = "../substrate/frame/core-fellowship" default-features = false optional = true -path = "../substrate/frame/core-fellowship" [dependencies.pallet-delegated-staking] +path = "../substrate/frame/delegated-staking" default-features = false optional = true -path = "../substrate/frame/delegated-staking" [dependencies.pallet-democracy] +path = "../substrate/frame/democracy" default-features = false optional = true -path = "../substrate/frame/democracy" [dependencies.pallet-dev-mode] +path = "../substrate/frame/examples/dev-mode" default-features = false optional = true -path = "../substrate/frame/examples/dev-mode" [dependencies.pallet-election-provider-multi-phase] +path = "../substrate/frame/election-provider-multi-phase" default-features = false optional = true -path = "../substrate/frame/election-provider-multi-phase" [dependencies.pallet-election-provider-support-benchmarking] +path = "../substrate/frame/election-provider-support/benchmarking" default-features = false optional = true -path = "../substrate/frame/election-provider-support/benchmarking" [dependencies.pallet-elections-phragmen] +path = "../substrate/frame/elections-phragmen" default-features = false optional = true -path = "../substrate/frame/elections-phragmen" [dependencies.pallet-fast-unstake] +path = "../substrate/frame/fast-unstake" default-features = false optional = true -path = "../substrate/frame/fast-unstake" [dependencies.pallet-glutton] +path = "../substrate/frame/glutton" default-features = false optional = true -path = "../substrate/frame/glutton" [dependencies.pallet-grandpa] +path = "../substrate/frame/grandpa" default-features = false optional = true -path = "../substrate/frame/grandpa" [dependencies.pallet-identity] +path = "../substrate/frame/identity" default-features = false optional = true -path = "../substrate/frame/identity" [dependencies.pallet-im-online] +path = "../substrate/frame/im-online" default-features = false optional = true -path = "../substrate/frame/im-online" [dependencies.pallet-indices] +path = "../substrate/frame/indices" default-features = false optional = true -path = "../substrate/frame/indices" [dependencies.pallet-insecure-randomness-collective-flip] +path = "../substrate/frame/insecure-randomness-collective-flip" default-features = false optional = true -path = "../substrate/frame/insecure-randomness-collective-flip" [dependencies.pallet-lottery] +path = "../substrate/frame/lottery" default-features = false optional = true -path = "../substrate/frame/lottery" [dependencies.pallet-membership] +path = "../substrate/frame/membership" default-features = false optional = true -path = "../substrate/frame/membership" [dependencies.pallet-message-queue] +path = "../substrate/frame/message-queue" default-features = false optional = true -path = "../substrate/frame/message-queue" [dependencies.pallet-migrations] +path = "../substrate/frame/migrations" default-features = false optional = true -path = "../substrate/frame/migrations" [dependencies.pallet-mixnet] +path = "../substrate/frame/mixnet" default-features = false optional = true -path = "../substrate/frame/mixnet" [dependencies.pallet-mmr] +path = "../substrate/frame/merkle-mountain-range" default-features = false optional = true -path = "../substrate/frame/merkle-mountain-range" [dependencies.pallet-multisig] +path = "../substrate/frame/multisig" default-features = false optional = true -path = "../substrate/frame/multisig" [dependencies.pallet-nft-fractionalization] +path = "../substrate/frame/nft-fractionalization" default-features = false optional = true -path = "../substrate/frame/nft-fractionalization" [dependencies.pallet-nfts] +path = "../substrate/frame/nfts" default-features = false optional = true -path = "../substrate/frame/nfts" [dependencies.pallet-nfts-runtime-api] +path = "../substrate/frame/nfts/runtime-api" default-features = false optional = true -path = "../substrate/frame/nfts/runtime-api" [dependencies.pallet-nis] +path = "../substrate/frame/nis" default-features = false optional = true -path = "../substrate/frame/nis" [dependencies.pallet-node-authorization] +path = "../substrate/frame/node-authorization" default-features = false optional = true -path = "../substrate/frame/node-authorization" [dependencies.pallet-nomination-pools] +path = "../substrate/frame/nomination-pools" default-features = false optional = true -path = "../substrate/frame/nomination-pools" [dependencies.pallet-nomination-pools-benchmarking] +path = "../substrate/frame/nomination-pools/benchmarking" default-features = false optional = true -path = "../substrate/frame/nomination-pools/benchmarking" [dependencies.pallet-nomination-pools-runtime-api] +path = "../substrate/frame/nomination-pools/runtime-api" default-features = false optional = true -path = "../substrate/frame/nomination-pools/runtime-api" [dependencies.pallet-offences] +path = "../substrate/frame/offences" default-features = false optional = true -path = "../substrate/frame/offences" [dependencies.pallet-offences-benchmarking] +path = "../substrate/frame/offences/benchmarking" default-features = false optional = true -path = "../substrate/frame/offences/benchmarking" [dependencies.pallet-paged-list] +path = "../substrate/frame/paged-list" default-features = false optional = true -path = "../substrate/frame/paged-list" [dependencies.pallet-parameters] +path = "../substrate/frame/parameters" default-features = false optional = true -path = "../substrate/frame/parameters" [dependencies.pallet-preimage] +path = "../substrate/frame/preimage" default-features = false optional = true -path = "../substrate/frame/preimage" [dependencies.pallet-proxy] +path = "../substrate/frame/proxy" default-features = false optional = true -path = "../substrate/frame/proxy" [dependencies.pallet-ranked-collective] +path = "../substrate/frame/ranked-collective" default-features = false optional = true -path = "../substrate/frame/ranked-collective" [dependencies.pallet-recovery] +path = "../substrate/frame/recovery" default-features = false optional = true -path = "../substrate/frame/recovery" [dependencies.pallet-referenda] +path = "../substrate/frame/referenda" default-features = false optional = true -path = "../substrate/frame/referenda" [dependencies.pallet-remark] +path = "../substrate/frame/remark" default-features = false optional = true -path = "../substrate/frame/remark" [dependencies.pallet-revive] +path = "../substrate/frame/revive" default-features = false optional = true -path = "../substrate/frame/revive" [dependencies.pallet-revive-fixtures] +path = "../substrate/frame/revive/fixtures" default-features = false optional = true -path = "../substrate/frame/revive/fixtures" [dependencies.pallet-revive-proc-macro] +path = "../substrate/frame/revive/proc-macro" default-features = false optional = true -path = "../substrate/frame/revive/proc-macro" [dependencies.pallet-revive-uapi] +path = "../substrate/frame/revive/uapi" default-features = false optional = true -path = "../substrate/frame/revive/uapi" [dependencies.pallet-root-offences] +path = "../substrate/frame/root-offences" default-features = false optional = true -path = "../substrate/frame/root-offences" [dependencies.pallet-root-testing] +path = "../substrate/frame/root-testing" default-features = false optional = true -path = "../substrate/frame/root-testing" [dependencies.pallet-safe-mode] +path = "../substrate/frame/safe-mode" default-features = false optional = true -path = "../substrate/frame/safe-mode" [dependencies.pallet-salary] +path = "../substrate/frame/salary" default-features = false optional = true -path = "../substrate/frame/salary" [dependencies.pallet-scheduler] +path = "../substrate/frame/scheduler" default-features = false optional = true -path = "../substrate/frame/scheduler" [dependencies.pallet-scored-pool] +path = "../substrate/frame/scored-pool" default-features = false optional = true -path = "../substrate/frame/scored-pool" [dependencies.pallet-session] +path = "../substrate/frame/session" default-features = false optional = true -path = "../substrate/frame/session" [dependencies.pallet-session-benchmarking] +path = "../substrate/frame/session/benchmarking" default-features = false optional = true -path = "../substrate/frame/session/benchmarking" [dependencies.pallet-skip-feeless-payment] +path = "../substrate/frame/transaction-payment/skip-feeless-payment" default-features = false optional = true -path = "../substrate/frame/transaction-payment/skip-feeless-payment" [dependencies.pallet-society] +path = "../substrate/frame/society" default-features = false optional = true -path = "../substrate/frame/society" [dependencies.pallet-staking] +path = "../substrate/frame/staking" default-features = false optional = true -path = "../substrate/frame/staking" [dependencies.pallet-staking-reward-curve] +path = "../substrate/frame/staking/reward-curve" default-features = false optional = true -path = "../substrate/frame/staking/reward-curve" [dependencies.pallet-staking-reward-fn] +path = "../substrate/frame/staking/reward-fn" default-features = false optional = true -path = "../substrate/frame/staking/reward-fn" [dependencies.pallet-staking-runtime-api] +path = "../substrate/frame/staking/runtime-api" default-features = false optional = true -path = "../substrate/frame/staking/runtime-api" [dependencies.pallet-state-trie-migration] +path = "../substrate/frame/state-trie-migration" default-features = false optional = true -path = "../substrate/frame/state-trie-migration" [dependencies.pallet-statement] +path = "../substrate/frame/statement" default-features = false optional = true -path = "../substrate/frame/statement" [dependencies.pallet-sudo] +path = "../substrate/frame/sudo" default-features = false optional = true -path = "../substrate/frame/sudo" [dependencies.pallet-timestamp] +path = "../substrate/frame/timestamp" default-features = false optional = true -path = "../substrate/frame/timestamp" [dependencies.pallet-tips] +path = "../substrate/frame/tips" default-features = false optional = true -path = "../substrate/frame/tips" [dependencies.pallet-transaction-payment] +path = "../substrate/frame/transaction-payment" default-features = false optional = true -path = "../substrate/frame/transaction-payment" [dependencies.pallet-transaction-payment-rpc-runtime-api] +path = "../substrate/frame/transaction-payment/rpc/runtime-api" default-features = false optional = true -path = "../substrate/frame/transaction-payment/rpc/runtime-api" [dependencies.pallet-transaction-storage] +path = "../substrate/frame/transaction-storage" default-features = false optional = true -path = "../substrate/frame/transaction-storage" [dependencies.pallet-treasury] +path = "../substrate/frame/treasury" default-features = false optional = true -path = "../substrate/frame/treasury" [dependencies.pallet-tx-pause] +path = "../substrate/frame/tx-pause" default-features = false optional = true -path = "../substrate/frame/tx-pause" [dependencies.pallet-uniques] +path = "../substrate/frame/uniques" default-features = false optional = true -path = "../substrate/frame/uniques" [dependencies.pallet-utility] +path = "../substrate/frame/utility" default-features = false optional = true -path = "../substrate/frame/utility" [dependencies.pallet-vesting] +path = "../substrate/frame/vesting" default-features = false optional = true -path = "../substrate/frame/vesting" [dependencies.pallet-whitelist] +path = "../substrate/frame/whitelist" default-features = false optional = true -path = "../substrate/frame/whitelist" [dependencies.pallet-xcm] +path = "../polkadot/xcm/pallet-xcm" default-features = false optional = true -path = "../polkadot/xcm/pallet-xcm" [dependencies.pallet-xcm-benchmarks] +path = "../polkadot/xcm/pallet-xcm-benchmarks" default-features = false optional = true -path = "../polkadot/xcm/pallet-xcm-benchmarks" [dependencies.pallet-xcm-bridge-hub] +path = "../bridges/modules/xcm-bridge-hub" default-features = false optional = true -path = "../bridges/modules/xcm-bridge-hub" [dependencies.pallet-xcm-bridge-hub-router] +path = "../bridges/modules/xcm-bridge-hub-router" default-features = false optional = true -path = "../bridges/modules/xcm-bridge-hub-router" [dependencies.parachains-common] +path = "../cumulus/parachains/common" default-features = false optional = true -path = "../cumulus/parachains/common" [dependencies.polkadot-core-primitives] +path = "../polkadot/core-primitives" default-features = false optional = true -path = "../polkadot/core-primitives" [dependencies.polkadot-parachain-primitives] +path = "../polkadot/parachain" default-features = false optional = true -path = "../polkadot/parachain" [dependencies.polkadot-primitives] +path = "../polkadot/primitives" default-features = false optional = true -path = "../polkadot/primitives" [dependencies.polkadot-runtime-common] +path = "../polkadot/runtime/common" default-features = false optional = true -path = "../polkadot/runtime/common" [dependencies.polkadot-runtime-metrics] +path = "../polkadot/runtime/metrics" default-features = false optional = true -path = "../polkadot/runtime/metrics" [dependencies.polkadot-runtime-parachains] +path = "../polkadot/runtime/parachains" default-features = false optional = true -path = "../polkadot/runtime/parachains" [dependencies.polkadot-sdk-frame] +path = "../substrate/frame" default-features = false optional = true -path = "../substrate/frame" [dependencies.sc-chain-spec-derive] +path = "../substrate/client/chain-spec/derive" default-features = false optional = true -path = "../substrate/client/chain-spec/derive" [dependencies.sc-tracing-proc-macro] +path = "../substrate/client/tracing/proc-macro" default-features = false optional = true -path = "../substrate/client/tracing/proc-macro" [dependencies.slot-range-helper] +path = "../polkadot/runtime/common/slot_range_helper" default-features = false optional = true -path = "../polkadot/runtime/common/slot_range_helper" [dependencies.snowbridge-beacon-primitives] +path = "../bridges/snowbridge/primitives/beacon" default-features = false optional = true -path = "../bridges/snowbridge/primitives/beacon" [dependencies.snowbridge-core] +path = "../bridges/snowbridge/primitives/core" default-features = false optional = true -path = "../bridges/snowbridge/primitives/core" [dependencies.snowbridge-ethereum] +path = "../bridges/snowbridge/primitives/ethereum" default-features = false optional = true -path = "../bridges/snowbridge/primitives/ethereum" [dependencies.snowbridge-outbound-queue-merkle-tree] +path = "../bridges/snowbridge/pallets/outbound-queue/merkle-tree" default-features = false optional = true -path = "../bridges/snowbridge/pallets/outbound-queue/merkle-tree" [dependencies.snowbridge-outbound-queue-runtime-api] +path = "../bridges/snowbridge/pallets/outbound-queue/runtime-api" default-features = false optional = true -path = "../bridges/snowbridge/pallets/outbound-queue/runtime-api" [dependencies.snowbridge-pallet-ethereum-client] +path = "../bridges/snowbridge/pallets/ethereum-client" default-features = false optional = true -path = "../bridges/snowbridge/pallets/ethereum-client" [dependencies.snowbridge-pallet-ethereum-client-fixtures] +path = "../bridges/snowbridge/pallets/ethereum-client/fixtures" default-features = false optional = true -path = "../bridges/snowbridge/pallets/ethereum-client/fixtures" [dependencies.snowbridge-pallet-inbound-queue] +path = "../bridges/snowbridge/pallets/inbound-queue" default-features = false optional = true -path = "../bridges/snowbridge/pallets/inbound-queue" [dependencies.snowbridge-pallet-inbound-queue-fixtures] +path = "../bridges/snowbridge/pallets/inbound-queue/fixtures" default-features = false optional = true -path = "../bridges/snowbridge/pallets/inbound-queue/fixtures" [dependencies.snowbridge-pallet-outbound-queue] +path = "../bridges/snowbridge/pallets/outbound-queue" default-features = false optional = true -path = "../bridges/snowbridge/pallets/outbound-queue" [dependencies.snowbridge-pallet-system] +path = "../bridges/snowbridge/pallets/system" default-features = false optional = true -path = "../bridges/snowbridge/pallets/system" [dependencies.snowbridge-router-primitives] +path = "../bridges/snowbridge/primitives/router" default-features = false optional = true -path = "../bridges/snowbridge/primitives/router" [dependencies.snowbridge-runtime-common] +path = "../bridges/snowbridge/runtime/runtime-common" default-features = false optional = true -path = "../bridges/snowbridge/runtime/runtime-common" [dependencies.snowbridge-system-runtime-api] +path = "../bridges/snowbridge/pallets/system/runtime-api" default-features = false optional = true -path = "../bridges/snowbridge/pallets/system/runtime-api" [dependencies.sp-api] +path = "../substrate/primitives/api" default-features = false optional = true -path = "../substrate/primitives/api" [dependencies.sp-api-proc-macro] +path = "../substrate/primitives/api/proc-macro" default-features = false optional = true -path = "../substrate/primitives/api/proc-macro" [dependencies.sp-application-crypto] +path = "../substrate/primitives/application-crypto" default-features = false optional = true -path = "../substrate/primitives/application-crypto" [dependencies.sp-arithmetic] +path = "../substrate/primitives/arithmetic" default-features = false optional = true -path = "../substrate/primitives/arithmetic" [dependencies.sp-authority-discovery] +path = "../substrate/primitives/authority-discovery" default-features = false optional = true -path = "../substrate/primitives/authority-discovery" [dependencies.sp-block-builder] +path = "../substrate/primitives/block-builder" default-features = false optional = true -path = "../substrate/primitives/block-builder" [dependencies.sp-consensus-aura] +path = "../substrate/primitives/consensus/aura" default-features = false optional = true -path = "../substrate/primitives/consensus/aura" [dependencies.sp-consensus-babe] +path = "../substrate/primitives/consensus/babe" default-features = false optional = true -path = "../substrate/primitives/consensus/babe" [dependencies.sp-consensus-beefy] +path = "../substrate/primitives/consensus/beefy" default-features = false optional = true -path = "../substrate/primitives/consensus/beefy" [dependencies.sp-consensus-grandpa] +path = "../substrate/primitives/consensus/grandpa" default-features = false optional = true -path = "../substrate/primitives/consensus/grandpa" [dependencies.sp-consensus-pow] +path = "../substrate/primitives/consensus/pow" default-features = false optional = true -path = "../substrate/primitives/consensus/pow" [dependencies.sp-consensus-slots] +path = "../substrate/primitives/consensus/slots" default-features = false optional = true -path = "../substrate/primitives/consensus/slots" [dependencies.sp-core] +path = "../substrate/primitives/core" default-features = false optional = true -path = "../substrate/primitives/core" [dependencies.sp-crypto-ec-utils] +path = "../substrate/primitives/crypto/ec-utils" default-features = false optional = true -path = "../substrate/primitives/crypto/ec-utils" [dependencies.sp-crypto-hashing] +path = "../substrate/primitives/crypto/hashing" default-features = false optional = true -path = "../substrate/primitives/crypto/hashing" [dependencies.sp-crypto-hashing-proc-macro] +path = "../substrate/primitives/crypto/hashing/proc-macro" default-features = false optional = true -path = "../substrate/primitives/crypto/hashing/proc-macro" [dependencies.sp-debug-derive] +path = "../substrate/primitives/debug-derive" default-features = false optional = true -path = "../substrate/primitives/debug-derive" [dependencies.sp-externalities] +path = "../substrate/primitives/externalities" default-features = false optional = true -path = "../substrate/primitives/externalities" [dependencies.sp-genesis-builder] +path = "../substrate/primitives/genesis-builder" default-features = false optional = true -path = "../substrate/primitives/genesis-builder" [dependencies.sp-inherents] +path = "../substrate/primitives/inherents" default-features = false optional = true -path = "../substrate/primitives/inherents" [dependencies.sp-io] +path = "../substrate/primitives/io" default-features = false optional = true -path = "../substrate/primitives/io" [dependencies.sp-keyring] +path = "../substrate/primitives/keyring" default-features = false optional = true -path = "../substrate/primitives/keyring" [dependencies.sp-keystore] +path = "../substrate/primitives/keystore" default-features = false optional = true -path = "../substrate/primitives/keystore" [dependencies.sp-metadata-ir] +path = "../substrate/primitives/metadata-ir" default-features = false optional = true -path = "../substrate/primitives/metadata-ir" [dependencies.sp-mixnet] +path = "../substrate/primitives/mixnet" default-features = false optional = true -path = "../substrate/primitives/mixnet" [dependencies.sp-mmr-primitives] +path = "../substrate/primitives/merkle-mountain-range" default-features = false optional = true -path = "../substrate/primitives/merkle-mountain-range" [dependencies.sp-npos-elections] +path = "../substrate/primitives/npos-elections" default-features = false optional = true -path = "../substrate/primitives/npos-elections" [dependencies.sp-offchain] +path = "../substrate/primitives/offchain" default-features = false optional = true -path = "../substrate/primitives/offchain" [dependencies.sp-runtime] +path = "../substrate/primitives/runtime" default-features = false optional = true -path = "../substrate/primitives/runtime" [dependencies.sp-runtime-interface] +path = "../substrate/primitives/runtime-interface" default-features = false optional = true -path = "../substrate/primitives/runtime-interface" [dependencies.sp-runtime-interface-proc-macro] +path = "../substrate/primitives/runtime-interface/proc-macro" default-features = false optional = true -path = "../substrate/primitives/runtime-interface/proc-macro" [dependencies.sp-session] +path = "../substrate/primitives/session" default-features = false optional = true -path = "../substrate/primitives/session" [dependencies.sp-staking] +path = "../substrate/primitives/staking" default-features = false optional = true -path = "../substrate/primitives/staking" [dependencies.sp-state-machine] +path = "../substrate/primitives/state-machine" default-features = false optional = true -path = "../substrate/primitives/state-machine" [dependencies.sp-statement-store] +path = "../substrate/primitives/statement-store" default-features = false optional = true -path = "../substrate/primitives/statement-store" [dependencies.sp-std] +path = "../substrate/primitives/std" default-features = false optional = true -path = "../substrate/primitives/std" [dependencies.sp-storage] +path = "../substrate/primitives/storage" default-features = false optional = true -path = "../substrate/primitives/storage" [dependencies.sp-timestamp] +path = "../substrate/primitives/timestamp" default-features = false optional = true -path = "../substrate/primitives/timestamp" [dependencies.sp-tracing] +path = "../substrate/primitives/tracing" default-features = false optional = true -path = "../substrate/primitives/tracing" [dependencies.sp-transaction-pool] +path = "../substrate/primitives/transaction-pool" default-features = false optional = true -path = "../substrate/primitives/transaction-pool" [dependencies.sp-transaction-storage-proof] +path = "../substrate/primitives/transaction-storage-proof" default-features = false optional = true -path = "../substrate/primitives/transaction-storage-proof" [dependencies.sp-trie] +path = "../substrate/primitives/trie" default-features = false optional = true -path = "../substrate/primitives/trie" [dependencies.sp-version] +path = "../substrate/primitives/version" default-features = false optional = true -path = "../substrate/primitives/version" [dependencies.sp-version-proc-macro] +path = "../substrate/primitives/version/proc-macro" default-features = false optional = true -path = "../substrate/primitives/version/proc-macro" [dependencies.sp-wasm-interface] +path = "../substrate/primitives/wasm-interface" default-features = false optional = true -path = "../substrate/primitives/wasm-interface" [dependencies.sp-weights] +path = "../substrate/primitives/weights" default-features = false optional = true -path = "../substrate/primitives/weights" [dependencies.staging-parachain-info] +path = "../cumulus/parachains/pallets/parachain-info" default-features = false optional = true -path = "../cumulus/parachains/pallets/parachain-info" [dependencies.staging-xcm] +path = "../polkadot/xcm" default-features = false optional = true -path = "../polkadot/xcm" [dependencies.staging-xcm-builder] +path = "../polkadot/xcm/xcm-builder" default-features = false optional = true -path = "../polkadot/xcm/xcm-builder" [dependencies.staging-xcm-executor] +path = "../polkadot/xcm/xcm-executor" default-features = false optional = true -path = "../polkadot/xcm/xcm-executor" [dependencies.substrate-bip39] +path = "../substrate/utils/substrate-bip39" default-features = false optional = true -path = "../substrate/utils/substrate-bip39" [dependencies.testnet-parachains-constants] +path = "../cumulus/parachains/runtimes/constants" default-features = false optional = true -path = "../cumulus/parachains/runtimes/constants" [dependencies.tracing-gum-proc-macro] +path = "../polkadot/node/gum/proc-macro" default-features = false optional = true -path = "../polkadot/node/gum/proc-macro" [dependencies.xcm-procedural] +path = "../polkadot/xcm/procedural" default-features = false optional = true -path = "../polkadot/xcm/procedural" [dependencies.xcm-runtime-apis] +path = "../polkadot/xcm/xcm-runtime-apis" default-features = false optional = true -path = "../polkadot/xcm/xcm-runtime-apis" [dependencies.asset-test-utils] +path = "../cumulus/parachains/runtimes/assets/test-utils" default-features = false optional = true -path = "../cumulus/parachains/runtimes/assets/test-utils" [dependencies.bridge-hub-test-utils] +path = "../cumulus/parachains/runtimes/bridge-hubs/test-utils" default-features = false optional = true -path = "../cumulus/parachains/runtimes/bridge-hubs/test-utils" [dependencies.cumulus-client-cli] +path = "../cumulus/client/cli" default-features = false optional = true -path = "../cumulus/client/cli" [dependencies.cumulus-client-collator] +path = "../cumulus/client/collator" default-features = false optional = true -path = "../cumulus/client/collator" [dependencies.cumulus-client-consensus-aura] +path = "../cumulus/client/consensus/aura" default-features = false optional = true -path = "../cumulus/client/consensus/aura" [dependencies.cumulus-client-consensus-common] +path = "../cumulus/client/consensus/common" default-features = false optional = true -path = "../cumulus/client/consensus/common" [dependencies.cumulus-client-consensus-proposer] +path = "../cumulus/client/consensus/proposer" default-features = false optional = true -path = "../cumulus/client/consensus/proposer" [dependencies.cumulus-client-consensus-relay-chain] +path = "../cumulus/client/consensus/relay-chain" default-features = false optional = true -path = "../cumulus/client/consensus/relay-chain" [dependencies.cumulus-client-network] +path = "../cumulus/client/network" default-features = false optional = true -path = "../cumulus/client/network" [dependencies.cumulus-client-parachain-inherent] +path = "../cumulus/client/parachain-inherent" default-features = false optional = true -path = "../cumulus/client/parachain-inherent" [dependencies.cumulus-client-pov-recovery] +path = "../cumulus/client/pov-recovery" default-features = false optional = true -path = "../cumulus/client/pov-recovery" [dependencies.cumulus-client-service] +path = "../cumulus/client/service" default-features = false optional = true -path = "../cumulus/client/service" [dependencies.cumulus-relay-chain-inprocess-interface] +path = "../cumulus/client/relay-chain-inprocess-interface" default-features = false optional = true -path = "../cumulus/client/relay-chain-inprocess-interface" [dependencies.cumulus-relay-chain-interface] +path = "../cumulus/client/relay-chain-interface" default-features = false optional = true -path = "../cumulus/client/relay-chain-interface" [dependencies.cumulus-relay-chain-minimal-node] +path = "../cumulus/client/relay-chain-minimal-node" default-features = false optional = true -path = "../cumulus/client/relay-chain-minimal-node" [dependencies.cumulus-relay-chain-rpc-interface] +path = "../cumulus/client/relay-chain-rpc-interface" default-features = false optional = true -path = "../cumulus/client/relay-chain-rpc-interface" [dependencies.cumulus-test-relay-sproof-builder] +path = "../cumulus/test/relay-sproof-builder" default-features = false optional = true -path = "../cumulus/test/relay-sproof-builder" [dependencies.emulated-integration-tests-common] +path = "../cumulus/parachains/integration-tests/emulated/common" default-features = false optional = true -path = "../cumulus/parachains/integration-tests/emulated/common" [dependencies.fork-tree] +path = "../substrate/utils/fork-tree" default-features = false optional = true -path = "../substrate/utils/fork-tree" [dependencies.frame-benchmarking-cli] +path = "../substrate/utils/frame/benchmarking-cli" default-features = false optional = true -path = "../substrate/utils/frame/benchmarking-cli" [dependencies.frame-remote-externalities] +path = "../substrate/utils/frame/remote-externalities" default-features = false optional = true -path = "../substrate/utils/frame/remote-externalities" [dependencies.frame-support-procedural-tools] +path = "../substrate/frame/support/procedural/tools" default-features = false optional = true -path = "../substrate/frame/support/procedural/tools" [dependencies.generate-bags] +path = "../substrate/utils/frame/generate-bags" default-features = false optional = true -path = "../substrate/utils/frame/generate-bags" [dependencies.mmr-gadget] +path = "../substrate/client/merkle-mountain-range" default-features = false optional = true -path = "../substrate/client/merkle-mountain-range" [dependencies.mmr-rpc] +path = "../substrate/client/merkle-mountain-range/rpc" default-features = false optional = true -path = "../substrate/client/merkle-mountain-range/rpc" [dependencies.pallet-contracts-mock-network] +path = "../substrate/frame/contracts/mock-network" default-features = false optional = true -path = "../substrate/frame/contracts/mock-network" [dependencies.pallet-revive-mock-network] +path = "../substrate/frame/revive/mock-network" default-features = false optional = true -path = "../substrate/frame/revive/mock-network" [dependencies.pallet-transaction-payment-rpc] +path = "../substrate/frame/transaction-payment/rpc" default-features = false optional = true -path = "../substrate/frame/transaction-payment/rpc" [dependencies.parachains-runtimes-test-utils] +path = "../cumulus/parachains/runtimes/test-utils" default-features = false optional = true -path = "../cumulus/parachains/runtimes/test-utils" [dependencies.polkadot-approval-distribution] +path = "../polkadot/node/network/approval-distribution" default-features = false optional = true -path = "../polkadot/node/network/approval-distribution" [dependencies.polkadot-availability-bitfield-distribution] +path = "../polkadot/node/network/bitfield-distribution" default-features = false optional = true -path = "../polkadot/node/network/bitfield-distribution" [dependencies.polkadot-availability-distribution] +path = "../polkadot/node/network/availability-distribution" default-features = false optional = true -path = "../polkadot/node/network/availability-distribution" [dependencies.polkadot-availability-recovery] +path = "../polkadot/node/network/availability-recovery" default-features = false optional = true -path = "../polkadot/node/network/availability-recovery" [dependencies.polkadot-cli] +path = "../polkadot/cli" default-features = false optional = true -path = "../polkadot/cli" [dependencies.polkadot-collator-protocol] +path = "../polkadot/node/network/collator-protocol" default-features = false optional = true -path = "../polkadot/node/network/collator-protocol" [dependencies.polkadot-dispute-distribution] +path = "../polkadot/node/network/dispute-distribution" default-features = false optional = true -path = "../polkadot/node/network/dispute-distribution" [dependencies.polkadot-erasure-coding] +path = "../polkadot/erasure-coding" default-features = false optional = true -path = "../polkadot/erasure-coding" [dependencies.polkadot-gossip-support] +path = "../polkadot/node/network/gossip-support" default-features = false optional = true -path = "../polkadot/node/network/gossip-support" [dependencies.polkadot-network-bridge] +path = "../polkadot/node/network/bridge" default-features = false optional = true -path = "../polkadot/node/network/bridge" [dependencies.polkadot-node-collation-generation] +path = "../polkadot/node/collation-generation" default-features = false optional = true -path = "../polkadot/node/collation-generation" [dependencies.polkadot-node-core-approval-voting] +path = "../polkadot/node/core/approval-voting" default-features = false optional = true -path = "../polkadot/node/core/approval-voting" [dependencies.polkadot-node-core-av-store] +path = "../polkadot/node/core/av-store" default-features = false optional = true -path = "../polkadot/node/core/av-store" [dependencies.polkadot-node-core-backing] +path = "../polkadot/node/core/backing" default-features = false optional = true -path = "../polkadot/node/core/backing" [dependencies.polkadot-node-core-bitfield-signing] +path = "../polkadot/node/core/bitfield-signing" default-features = false optional = true -path = "../polkadot/node/core/bitfield-signing" [dependencies.polkadot-node-core-candidate-validation] +path = "../polkadot/node/core/candidate-validation" default-features = false optional = true -path = "../polkadot/node/core/candidate-validation" [dependencies.polkadot-node-core-chain-api] +path = "../polkadot/node/core/chain-api" default-features = false optional = true -path = "../polkadot/node/core/chain-api" [dependencies.polkadot-node-core-chain-selection] +path = "../polkadot/node/core/chain-selection" default-features = false optional = true -path = "../polkadot/node/core/chain-selection" [dependencies.polkadot-node-core-dispute-coordinator] +path = "../polkadot/node/core/dispute-coordinator" default-features = false optional = true -path = "../polkadot/node/core/dispute-coordinator" [dependencies.polkadot-node-core-parachains-inherent] +path = "../polkadot/node/core/parachains-inherent" default-features = false optional = true -path = "../polkadot/node/core/parachains-inherent" [dependencies.polkadot-node-core-prospective-parachains] +path = "../polkadot/node/core/prospective-parachains" default-features = false optional = true -path = "../polkadot/node/core/prospective-parachains" [dependencies.polkadot-node-core-provisioner] +path = "../polkadot/node/core/provisioner" default-features = false optional = true -path = "../polkadot/node/core/provisioner" [dependencies.polkadot-node-core-pvf] +path = "../polkadot/node/core/pvf" default-features = false optional = true -path = "../polkadot/node/core/pvf" [dependencies.polkadot-node-core-pvf-checker] +path = "../polkadot/node/core/pvf-checker" default-features = false optional = true -path = "../polkadot/node/core/pvf-checker" [dependencies.polkadot-node-core-pvf-common] +path = "../polkadot/node/core/pvf/common" default-features = false optional = true -path = "../polkadot/node/core/pvf/common" [dependencies.polkadot-node-core-pvf-execute-worker] +path = "../polkadot/node/core/pvf/execute-worker" default-features = false optional = true -path = "../polkadot/node/core/pvf/execute-worker" [dependencies.polkadot-node-core-pvf-prepare-worker] +path = "../polkadot/node/core/pvf/prepare-worker" default-features = false optional = true -path = "../polkadot/node/core/pvf/prepare-worker" [dependencies.polkadot-node-core-runtime-api] +path = "../polkadot/node/core/runtime-api" default-features = false optional = true -path = "../polkadot/node/core/runtime-api" [dependencies.polkadot-node-jaeger] +path = "../polkadot/node/jaeger" default-features = false optional = true -path = "../polkadot/node/jaeger" [dependencies.polkadot-node-metrics] +path = "../polkadot/node/metrics" default-features = false optional = true -path = "../polkadot/node/metrics" [dependencies.polkadot-node-network-protocol] +path = "../polkadot/node/network/protocol" default-features = false optional = true -path = "../polkadot/node/network/protocol" [dependencies.polkadot-node-primitives] +path = "../polkadot/node/primitives" default-features = false optional = true -path = "../polkadot/node/primitives" [dependencies.polkadot-node-subsystem] +path = "../polkadot/node/subsystem" default-features = false optional = true -path = "../polkadot/node/subsystem" [dependencies.polkadot-node-subsystem-types] +path = "../polkadot/node/subsystem-types" default-features = false optional = true -path = "../polkadot/node/subsystem-types" [dependencies.polkadot-node-subsystem-util] +path = "../polkadot/node/subsystem-util" default-features = false optional = true -path = "../polkadot/node/subsystem-util" [dependencies.polkadot-overseer] +path = "../polkadot/node/overseer" default-features = false optional = true -path = "../polkadot/node/overseer" [dependencies.polkadot-parachain-lib] +path = "../cumulus/polkadot-parachain/polkadot-parachain-lib" default-features = false optional = true -path = "../cumulus/polkadot-parachain/polkadot-parachain-lib" [dependencies.polkadot-rpc] +path = "../polkadot/rpc" default-features = false optional = true -path = "../polkadot/rpc" [dependencies.polkadot-service] +path = "../polkadot/node/service" default-features = false optional = true -path = "../polkadot/node/service" [dependencies.polkadot-statement-distribution] +path = "../polkadot/node/network/statement-distribution" default-features = false optional = true -path = "../polkadot/node/network/statement-distribution" [dependencies.polkadot-statement-table] +path = "../polkadot/statement-table" default-features = false optional = true -path = "../polkadot/statement-table" [dependencies.sc-allocator] +path = "../substrate/client/allocator" default-features = false optional = true -path = "../substrate/client/allocator" [dependencies.sc-authority-discovery] +path = "../substrate/client/authority-discovery" default-features = false optional = true -path = "../substrate/client/authority-discovery" [dependencies.sc-basic-authorship] +path = "../substrate/client/basic-authorship" default-features = false optional = true -path = "../substrate/client/basic-authorship" [dependencies.sc-block-builder] +path = "../substrate/client/block-builder" default-features = false optional = true -path = "../substrate/client/block-builder" [dependencies.sc-chain-spec] +path = "../substrate/client/chain-spec" default-features = false optional = true -path = "../substrate/client/chain-spec" [dependencies.sc-cli] +path = "../substrate/client/cli" default-features = false optional = true -path = "../substrate/client/cli" [dependencies.sc-client-api] +path = "../substrate/client/api" default-features = false optional = true -path = "../substrate/client/api" [dependencies.sc-client-db] +path = "../substrate/client/db" default-features = false optional = true -path = "../substrate/client/db" [dependencies.sc-consensus] +path = "../substrate/client/consensus/common" default-features = false optional = true -path = "../substrate/client/consensus/common" [dependencies.sc-consensus-aura] +path = "../substrate/client/consensus/aura" default-features = false optional = true -path = "../substrate/client/consensus/aura" [dependencies.sc-consensus-babe] +path = "../substrate/client/consensus/babe" default-features = false optional = true -path = "../substrate/client/consensus/babe" [dependencies.sc-consensus-babe-rpc] +path = "../substrate/client/consensus/babe/rpc" default-features = false optional = true -path = "../substrate/client/consensus/babe/rpc" [dependencies.sc-consensus-beefy] +path = "../substrate/client/consensus/beefy" default-features = false optional = true -path = "../substrate/client/consensus/beefy" [dependencies.sc-consensus-beefy-rpc] +path = "../substrate/client/consensus/beefy/rpc" default-features = false optional = true -path = "../substrate/client/consensus/beefy/rpc" [dependencies.sc-consensus-epochs] +path = "../substrate/client/consensus/epochs" default-features = false optional = true -path = "../substrate/client/consensus/epochs" [dependencies.sc-consensus-grandpa] +path = "../substrate/client/consensus/grandpa" default-features = false optional = true -path = "../substrate/client/consensus/grandpa" [dependencies.sc-consensus-grandpa-rpc] +path = "../substrate/client/consensus/grandpa/rpc" default-features = false optional = true -path = "../substrate/client/consensus/grandpa/rpc" [dependencies.sc-consensus-manual-seal] +path = "../substrate/client/consensus/manual-seal" default-features = false optional = true -path = "../substrate/client/consensus/manual-seal" [dependencies.sc-consensus-pow] +path = "../substrate/client/consensus/pow" default-features = false optional = true -path = "../substrate/client/consensus/pow" [dependencies.sc-consensus-slots] +path = "../substrate/client/consensus/slots" default-features = false optional = true -path = "../substrate/client/consensus/slots" [dependencies.sc-executor] +path = "../substrate/client/executor" default-features = false optional = true -path = "../substrate/client/executor" [dependencies.sc-executor-common] +path = "../substrate/client/executor/common" default-features = false optional = true -path = "../substrate/client/executor/common" [dependencies.sc-executor-polkavm] +path = "../substrate/client/executor/polkavm" default-features = false optional = true -path = "../substrate/client/executor/polkavm" [dependencies.sc-executor-wasmtime] +path = "../substrate/client/executor/wasmtime" default-features = false optional = true -path = "../substrate/client/executor/wasmtime" [dependencies.sc-informant] +path = "../substrate/client/informant" default-features = false optional = true -path = "../substrate/client/informant" [dependencies.sc-keystore] +path = "../substrate/client/keystore" default-features = false optional = true -path = "../substrate/client/keystore" [dependencies.sc-mixnet] +path = "../substrate/client/mixnet" default-features = false optional = true -path = "../substrate/client/mixnet" [dependencies.sc-network] +path = "../substrate/client/network" default-features = false optional = true -path = "../substrate/client/network" [dependencies.sc-network-common] +path = "../substrate/client/network/common" default-features = false optional = true -path = "../substrate/client/network/common" [dependencies.sc-network-gossip] +path = "../substrate/client/network-gossip" default-features = false optional = true -path = "../substrate/client/network-gossip" [dependencies.sc-network-light] +path = "../substrate/client/network/light" default-features = false optional = true -path = "../substrate/client/network/light" [dependencies.sc-network-statement] +path = "../substrate/client/network/statement" default-features = false optional = true -path = "../substrate/client/network/statement" [dependencies.sc-network-sync] +path = "../substrate/client/network/sync" default-features = false optional = true -path = "../substrate/client/network/sync" [dependencies.sc-network-transactions] +path = "../substrate/client/network/transactions" default-features = false optional = true -path = "../substrate/client/network/transactions" [dependencies.sc-network-types] +path = "../substrate/client/network/types" default-features = false optional = true -path = "../substrate/client/network/types" [dependencies.sc-offchain] +path = "../substrate/client/offchain" default-features = false optional = true -path = "../substrate/client/offchain" [dependencies.sc-proposer-metrics] +path = "../substrate/client/proposer-metrics" default-features = false optional = true -path = "../substrate/client/proposer-metrics" [dependencies.sc-rpc] +path = "../substrate/client/rpc" default-features = false optional = true -path = "../substrate/client/rpc" [dependencies.sc-rpc-api] +path = "../substrate/client/rpc-api" default-features = false optional = true -path = "../substrate/client/rpc-api" [dependencies.sc-rpc-server] +path = "../substrate/client/rpc-servers" default-features = false optional = true -path = "../substrate/client/rpc-servers" [dependencies.sc-rpc-spec-v2] +path = "../substrate/client/rpc-spec-v2" default-features = false optional = true -path = "../substrate/client/rpc-spec-v2" [dependencies.sc-service] +path = "../substrate/client/service" default-features = false optional = true -path = "../substrate/client/service" [dependencies.sc-state-db] +path = "../substrate/client/state-db" default-features = false optional = true -path = "../substrate/client/state-db" [dependencies.sc-statement-store] +path = "../substrate/client/statement-store" default-features = false optional = true -path = "../substrate/client/statement-store" [dependencies.sc-storage-monitor] +path = "../substrate/client/storage-monitor" default-features = false optional = true -path = "../substrate/client/storage-monitor" [dependencies.sc-sync-state-rpc] +path = "../substrate/client/sync-state-rpc" default-features = false optional = true -path = "../substrate/client/sync-state-rpc" [dependencies.sc-sysinfo] +path = "../substrate/client/sysinfo" default-features = false optional = true -path = "../substrate/client/sysinfo" [dependencies.sc-telemetry] +path = "../substrate/client/telemetry" default-features = false optional = true -path = "../substrate/client/telemetry" [dependencies.sc-tracing] +path = "../substrate/client/tracing" default-features = false optional = true -path = "../substrate/client/tracing" [dependencies.sc-transaction-pool] +path = "../substrate/client/transaction-pool" default-features = false optional = true -path = "../substrate/client/transaction-pool" [dependencies.sc-transaction-pool-api] +path = "../substrate/client/transaction-pool/api" default-features = false optional = true -path = "../substrate/client/transaction-pool/api" [dependencies.sc-utils] +path = "../substrate/client/utils" default-features = false optional = true -path = "../substrate/client/utils" [dependencies.snowbridge-runtime-test-common] +path = "../bridges/snowbridge/runtime/test-common" default-features = false optional = true -path = "../bridges/snowbridge/runtime/test-common" [dependencies.sp-blockchain] +path = "../substrate/primitives/blockchain" default-features = false optional = true -path = "../substrate/primitives/blockchain" [dependencies.sp-consensus] +path = "../substrate/primitives/consensus/common" default-features = false optional = true -path = "../substrate/primitives/consensus/common" [dependencies.sp-core-hashing] +path = "../substrate/deprecated/hashing" default-features = false optional = true -path = "../substrate/deprecated/hashing" [dependencies.sp-core-hashing-proc-macro] +path = "../substrate/deprecated/hashing/proc-macro" default-features = false optional = true -path = "../substrate/deprecated/hashing/proc-macro" [dependencies.sp-database] +path = "../substrate/primitives/database" default-features = false optional = true -path = "../substrate/primitives/database" [dependencies.sp-maybe-compressed-blob] +path = "../substrate/primitives/maybe-compressed-blob" default-features = false optional = true -path = "../substrate/primitives/maybe-compressed-blob" [dependencies.sp-panic-handler] +path = "../substrate/primitives/panic-handler" default-features = false optional = true -path = "../substrate/primitives/panic-handler" [dependencies.sp-rpc] +path = "../substrate/primitives/rpc" default-features = false optional = true -path = "../substrate/primitives/rpc" [dependencies.staging-chain-spec-builder] +path = "../substrate/bin/utils/chain-spec-builder" default-features = false optional = true -path = "../substrate/bin/utils/chain-spec-builder" [dependencies.staging-node-inspect] +path = "../substrate/bin/node/inspect" default-features = false optional = true -path = "../substrate/bin/node/inspect" [dependencies.staging-tracking-allocator] +path = "../polkadot/node/tracking-allocator" default-features = false optional = true -path = "../polkadot/node/tracking-allocator" [dependencies.subkey] +path = "../substrate/bin/utils/subkey" default-features = false optional = true -path = "../substrate/bin/utils/subkey" [dependencies.substrate-build-script-utils] +path = "../substrate/utils/build-script-utils" default-features = false optional = true -path = "../substrate/utils/build-script-utils" [dependencies.substrate-frame-rpc-support] +path = "../substrate/utils/frame/rpc/support" default-features = false optional = true -path = "../substrate/utils/frame/rpc/support" [dependencies.substrate-frame-rpc-system] +path = "../substrate/utils/frame/rpc/system" default-features = false optional = true -path = "../substrate/utils/frame/rpc/system" [dependencies.substrate-prometheus-endpoint] +path = "../substrate/utils/prometheus" default-features = false optional = true -path = "../substrate/utils/prometheus" [dependencies.substrate-rpc-client] +path = "../substrate/utils/frame/rpc/client" default-features = false optional = true -path = "../substrate/utils/frame/rpc/client" [dependencies.substrate-state-trie-migration-rpc] +path = "../substrate/utils/frame/rpc/state-trie-migration-rpc" default-features = false optional = true -path = "../substrate/utils/frame/rpc/state-trie-migration-rpc" [dependencies.substrate-wasm-builder] +path = "../substrate/utils/wasm-builder" default-features = false optional = true -path = "../substrate/utils/wasm-builder" [dependencies.tracing-gum] +path = "../polkadot/node/gum" default-features = false optional = true -path = "../polkadot/node/gum" [dependencies.xcm-emulator] +path = "../cumulus/xcm/xcm-emulator" default-features = false optional = true -path = "../cumulus/xcm/xcm-emulator" [dependencies.xcm-simulator] +path = "../polkadot/xcm/xcm-simulator" default-features = false optional = true -path = "../polkadot/xcm/xcm-simulator" [package.metadata.docs.rs] features = ["node", "runtime-full"] -targets = ["x86_64-unknown-linux-gnu"] +targets = ["x86_64-unknown-linux-gnu"] \ No newline at end of file From a80e6e32c1610857cf0e98d854ef8432cc86869a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 12 Sep 2024 22:49:43 -0500 Subject: [PATCH 40/47] fix(Cargo): revert unintended regression in Cargo lockfile --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d22a56d08c8..ebbe7af20e84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16889,9 +16889,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -16913,9 +16913,9 @@ dependencies = [ [[package]] name = "ruint-macro" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" From 668c148dce325048cde81409e541e621bede58da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Thu, 12 Sep 2024 23:04:27 -0500 Subject: [PATCH 41/47] fix(pallet-assets): missing documentation changes --- substrate/frame/assets/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index f7990e29c45b..9b3b43a8fcda 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -812,7 +812,7 @@ pub mod pallet { /// asset. /// /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if - /// an account has holds or freezes in place. + /// an account contains holds or freezes in place. #[pallet::call_index(2)] pub fn start_destroy(origin: OriginFor, id: T::AssetIdParameter) -> DispatchResult { let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { @@ -1627,6 +1627,9 @@ pub mod pallet { /// refunded. /// - `allow_burn`: If `true` then assets may be destroyed in order to complete the refund. /// + /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if + /// the asset account contains holds or freezes in place. + /// /// Emits `Refunded` event when successful. #[pallet::call_index(27)] #[pallet::weight(T::WeightInfo::refund())] @@ -1717,6 +1720,9 @@ pub mod pallet { /// - `id`: The identifier of the asset for the account holding a deposit. /// - `who`: The account to refund. /// + /// It will fail with either [`Error::ContainsHolds`] or [`Error::ContainsFreezes`] if + /// the asset account contains holds or freezes in place. + /// /// Emits `Refunded` event when successful. #[pallet::call_index(30)] #[pallet::weight(T::WeightInfo::refund_other())] From 4f2772537040ef7fd2eb7602d99fa9dfc8774ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 13 Sep 2024 08:44:41 -0500 Subject: [PATCH 42/47] fix(pallet-assets): missing borrowing --- substrate/frame/assets/src/functions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index b38cdf8e6874..ebebb4105edb 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -365,11 +365,11 @@ impl, I: 'static> Pallet { use ExistenceReason::*; ensure!( - T::Holder::balance_on_hold(id.clone(), who).is_none(), + T::Holder::balance_on_hold(id.clone(), &who).is_none(), Error::::ContainsHolds ); ensure!( - T::Freezer::frozen_balance(id.clone(), who).is_none(), + T::Freezer::frozen_balance(id.clone(), &who).is_none(), Error::::ContainsFreezes ); From dec2a4c9c38879680c21d54f8aaf6a49b0d5122a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 16 Sep 2024 09:10:41 -0500 Subject: [PATCH 43/47] change: resolve issues raised by ci checks --- substrate/frame/assets-holder/src/lib.rs | 2 +- substrate/frame/assets/src/mock.rs | 4 ++-- umbrella/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/assets-holder/src/lib.rs b/substrate/frame/assets-holder/src/lib.rs index cda267d639eb..26890e47c33e 100644 --- a/substrate/frame/assets-holder/src/lib.rs +++ b/substrate/frame/assets-holder/src/lib.rs @@ -35,7 +35,7 @@ //! This pallet provides the following functionality: //! //! - Pallet hooks allowing [`pallet-assets`] to know the balance on hold for an account on a given -//! asset (see [`pallet_assets::BalanceOnHold`](pallet_assets::BalanceOnHold)). +//! asset (see [`pallet_assets::BalanceOnHold`]). //! - An implementation of //! [`fungibles::hold::Inspect`](frame_support::traits::fungibles::hold::Inspect), //! [`fungibles::hold::Mutate`](frame_support::traits::fungibles::hold::Mutate) and diff --git a/substrate/frame/assets/src/mock.rs b/substrate/frame/assets/src/mock.rs index 388da8a3894f..a65a50c5f079 100644 --- a/substrate/frame/assets/src/mock.rs +++ b/substrate/frame/assets/src/mock.rs @@ -130,7 +130,7 @@ impl BalanceOnHold for TestHolder { } fn contains_holds(asset: AssetId) -> bool { - !OnHold::get().iter().find(|((k, _), _)| &asset == k).is_none() + OnHold::get().iter().any(|((k, _), _)| &asset == k) } } @@ -174,7 +174,7 @@ impl FrozenBalance for TestFreezer { /// Return a value that indicates if there are registered freezes for a given asset. fn contains_freezes(asset: AssetId) -> bool { - !Frozen::get().iter().find(|((k, _), _)| &asset == k).is_none() + Frozen::get().iter().any(|((k, _), _)| &asset == k) } } diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 1d5cbbca875e..28ee76936b4a 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -2497,4 +2497,4 @@ optional = true [package.metadata.docs.rs] features = ["node", "runtime-full"] -targets = ["x86_64-unknown-linux-gnu"] \ No newline at end of file +targets = ["x86_64-unknown-linux-gnu"] From 25d9bb050df9347a71ad02e89b08f00ed4ba5400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 23 Sep 2024 11:19:54 -0500 Subject: [PATCH 44/47] change(pallet-assets): make `dead_account` return either `DeadConsequence` or fail if account cannot die. --- substrate/frame/assets/src/functions.rs | 45 +++++++++++-------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index ebebb4105edb..3bf704d3124f 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -96,12 +96,20 @@ impl, I: 'static> Pallet { } pub(super) fn dead_account( + id: T::AssetId, who: &T::AccountId, d: &mut AssetDetails>, reason: &ExistenceReasonOf, force: bool, - ) -> DeadConsequence { + ) -> Result { use ExistenceReason::*; + + ensure!( + T::Holder::balance_on_hold(id.clone(), &who).is_none(), + Error::::ContainsHolds + ); + ensure!(T::Freezer::frozen_balance(id, &who).is_none(), Error::::ContainsFreezes); + match *reason { Consumer => frame_system::Pallet::::dec_consumers(who), Sufficient => { @@ -109,11 +117,11 @@ impl, I: 'static> Pallet { frame_system::Pallet::::dec_sufficients(who); }, DepositRefunded => {}, - DepositHeld(_) | DepositFrom(..) if !force => return Keep, + DepositHeld(_) | DepositFrom(..) if !force => return Ok(Keep), DepositHeld(_) | DepositFrom(..) => {}, } d.accounts = d.accounts.saturating_sub(1); - Remove + Ok(Remove) } /// Returns `true` when the balance of `account` can be increased by `amount`. @@ -364,15 +372,6 @@ impl, I: 'static> Pallet { use AssetStatus::*; use ExistenceReason::*; - ensure!( - T::Holder::balance_on_hold(id.clone(), &who).is_none(), - Error::::ContainsHolds - ); - ensure!( - T::Freezer::frozen_balance(id.clone(), &who).is_none(), - Error::::ContainsFreezes - ); - let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::::NoDeposit); let mut details = Asset::::get(&id).ok_or(Error::::Unknown)?; @@ -383,7 +382,8 @@ impl, I: 'static> Pallet { T::Currency::unreserve(&who, deposit); } - if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { + if let Remove = Self::dead_account(id.clone(), &who, &mut details, &account.reason, false)? + { Account::::remove(&id, &who); } else { debug_assert!(false, "refund did not result in dead account?!"); @@ -407,15 +407,6 @@ impl, I: 'static> Pallet { who: &T::AccountId, maybe_check_caller: Option, ) -> DispatchResult { - ensure!( - T::Holder::balance_on_hold(id.clone(), who).is_none(), - Error::::ContainsHolds - ); - ensure!( - T::Freezer::frozen_balance(id.clone(), who).is_none(), - Error::::ContainsFreezes - ); - let mut account = Account::::get(&id, &who).ok_or(Error::::NoDeposit)?; let (depositor, deposit) = account.reason.take_deposit_from().ok_or(Error::::NoDeposit)?; @@ -429,7 +420,8 @@ impl, I: 'static> Pallet { T::Currency::unreserve(&depositor, deposit); - if let Remove = Self::dead_account(&who, &mut details, &account.reason, false) { + if let Remove = Self::dead_account(id.clone(), &who, &mut details, &account.reason, false)? + { Account::::remove(&id, &who); } else { debug_assert!(false, "refund did not result in dead account?!"); @@ -594,7 +586,8 @@ impl, I: 'static> Pallet { account.balance = account.balance.saturating_sub(actual); if account.balance < details.min_balance { debug_assert!(account.balance.is_zero(), "checked in prep; qed"); - target_died = Some(Self::dead_account(target, details, &account.reason, false)); + target_died = + Some(Self::dead_account(id, target, details, &account.reason, false)?); if let Some(Remove) = target_died { return Ok(()) } @@ -716,7 +709,7 @@ impl, I: 'static> Pallet { if source_account.balance < details.min_balance { debug_assert!(source_account.balance.is_zero(), "checked in prep; qed"); source_died = - Some(Self::dead_account(source, details, &source_account.reason, false)); + Some(Self::dead_account(id, source, details, &source_account.reason, false)?); if let Some(Remove) = source_died { Account::::remove(&id, &source); return Ok(()) @@ -821,7 +814,7 @@ impl, I: 'static> Pallet { } else if let Some(deposit) = v.reason.take_deposit() { T::Currency::unreserve(&who, deposit); } - if let Remove = Self::dead_account(&who, &mut details, &v.reason, false) { + if let Remove = Self::dead_account(id, &who, &mut details, &v.reason, false)? { Account::::remove(&id, &who); dead_accounts.push(who); } else { From c81ad85d040a3ce31c4700264871dad2c34af487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 23 Sep 2024 11:33:47 -0500 Subject: [PATCH 45/47] change(Cargo): update lockfile --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30fb076f5d50..50e37eab9fcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10779,9 +10779,9 @@ dependencies = [ "pallet-balances", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", - "sp-runtime", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", ] [[package]] From bd72de1fa2c0c2c60b9d9f27b0a994129c741aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 23 Sep 2024 11:39:59 -0500 Subject: [PATCH 46/47] fix(pallet-pass): missing clone for asset id --- substrate/frame/assets/src/functions.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 3bf704d3124f..743b4cf0b47b 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -814,7 +814,9 @@ impl, I: 'static> Pallet { } else if let Some(deposit) = v.reason.take_deposit() { T::Currency::unreserve(&who, deposit); } - if let Remove = Self::dead_account(id, &who, &mut details, &v.reason, false)? { + if let Remove = + Self::dead_account(id.clone(), &who, &mut details, &v.reason, false)? + { Account::::remove(&id, &who); dead_accounts.push(who); } else { From 4bab6f1a23188226c8be505ce047af1e9c61ed3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Mon, 23 Sep 2024 13:13:55 -0500 Subject: [PATCH 47/47] make ci happy --- substrate/frame/assets/src/functions.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/substrate/frame/assets/src/functions.rs b/substrate/frame/assets/src/functions.rs index 743b4cf0b47b..d66658e01113 100644 --- a/substrate/frame/assets/src/functions.rs +++ b/substrate/frame/assets/src/functions.rs @@ -586,8 +586,13 @@ impl, I: 'static> Pallet { account.balance = account.balance.saturating_sub(actual); if account.balance < details.min_balance { debug_assert!(account.balance.is_zero(), "checked in prep; qed"); - target_died = - Some(Self::dead_account(id, target, details, &account.reason, false)?); + target_died = Some(Self::dead_account( + id.clone(), + target, + details, + &account.reason, + false, + )?); if let Some(Remove) = target_died { return Ok(()) } @@ -708,8 +713,13 @@ impl, I: 'static> Pallet { // Remove source account if it's now dead. if source_account.balance < details.min_balance { debug_assert!(source_account.balance.is_zero(), "checked in prep; qed"); - source_died = - Some(Self::dead_account(id, source, details, &source_account.reason, false)?); + source_died = Some(Self::dead_account( + id.clone(), + source, + details, + &source_account.reason, + false, + )?); if let Some(Remove) = source_died { Account::::remove(&id, &source); return Ok(())