From 487c231e533ff9159dfc9457f8920cb01c1617e1 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 17:55:03 +0200 Subject: [PATCH 01/13] added log crate to crowdloan --- Cargo.lock | 1 + pallets/crowdloan/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 65578627f9..3dbcfe75a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6120,6 +6120,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-balances", "pallet-preimage", "parity-scale-codec", diff --git a/pallets/crowdloan/Cargo.toml b/pallets/crowdloan/Cargo.toml index 1739a85b7c..3098db490e 100644 --- a/pallets/crowdloan/Cargo.toml +++ b/pallets/crowdloan/Cargo.toml @@ -21,6 +21,7 @@ frame-support.workspace = true frame-system.workspace = true sp-runtime.workspace = true sp-std.workspace = true +log = { workspace = true } [dev-dependencies] pallet-balances = { default-features = true, workspace = true } From 39cea2a22b584c0eaf4d589ad769d7654c43fded Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 17:57:22 +0200 Subject: [PATCH 02/13] keep track of contributors count and allow a maximum of contributors, added migration --- pallets/crowdloan/src/lib.rs | 78 +++++++++++++++++-- .../migrate_add_contributors_count.rs | 46 +++++++++++ pallets/crowdloan/src/migrations/mod.rs | 2 + 3 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs create mode 100644 pallets/crowdloan/src/migrations/mod.rs diff --git a/pallets/crowdloan/src/lib.rs b/pallets/crowdloan/src/lib.rs index 9b49d5e778..392413feba 100644 --- a/pallets/crowdloan/src/lib.rs +++ b/pallets/crowdloan/src/lib.rs @@ -7,7 +7,7 @@ extern crate alloc; -use alloc::{boxed::Box, vec, vec::Vec}; +use alloc::{boxed::Box, vec}; use codec::{Decode, Encode}; use frame_support::{ PalletId, @@ -25,6 +25,7 @@ use frame_support::{ use frame_system::pallet_prelude::*; use scale_info::TypeInfo; use sp_runtime::traits::CheckedSub; +use sp_std::vec::Vec; use weights::WeightInfo; pub use pallet::*; @@ -33,6 +34,7 @@ use subtensor_macros::freeze_struct; pub type CrowdloanId = u32; mod benchmarking; +mod migrations; mod mock; mod tests; pub mod weights; @@ -42,6 +44,9 @@ pub type CurrencyOf = ::Currency; pub type BalanceOf = as fungible::Inspect<::AccountId>>::Balance; +// Define a maximum length for the migration key +type MigrationKeyMaxLen = ConstU32<128>; + pub type BoundedCallOf = Bounded<::RuntimeCall, ::Hashing>; @@ -134,6 +139,10 @@ pub mod pallet { /// The maximum number of contributors that can be refunded in a single refund. #[pallet::constant] type RefundContributorsLimit: Get; + + // The maximum number of contributors that can contribute to a crowdloan. + #[pallet::constant] + type MaxContributors: Get; } /// A map of crowdloan ids to their information. @@ -157,11 +166,21 @@ pub mod pallet { OptionQuery, >; + /// A map of crowdloan ids to their contributors count. + #[pallet::storage] + pub type ContributorsCount = + StorageMap<_, Twox64Concat, CrowdloanId, u32, OptionQuery>; + /// The current crowdloan id that will be set during the finalize call, making it /// temporarily accessible to the dispatched call. #[pallet::storage] pub type CurrentCrowdloanId = StorageValue<_, CrowdloanId, OptionQuery>; + /// Storage for the migration run status. + #[pallet::storage] + pub type HasMigrationRun = + StorageMap<_, Identity, BoundedVec, bool, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -253,6 +272,21 @@ pub mod pallet { NotReadyToDissolve, /// The deposit cannot be withdrawn from the crowdloan. DepositCannotBeWithdrawn, + /// The maximum number of contributors has been reached. + MaxContributorsReached, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let mut weight = frame_support::weights::Weight::from_parts(0, 0); + + weight = weight + // Add the contributors count for each crowdloan + .saturating_add(migrations::migrate_add_contributors_count::()); + + weight + } } #[pallet::call] @@ -354,6 +388,7 @@ pub mod pallet { )?; Contributions::::insert(crowdloan_id, &creator, deposit); + ContributorsCount::::insert(crowdloan_id, 1); Self::deposit_event(Event::::Created { crowdloan_id, @@ -398,6 +433,14 @@ pub mod pallet { Error::::ContributionTooLow ); + // Ensure the crowdloan has not reached the maximum number of contributors + let contributors_count = + ContributorsCount::::get(crowdloan_id).ok_or(Error::::InvalidCrowdloanId)?; + ensure!( + contributors_count < T::MaxContributors::get(), + Error::::MaxContributorsReached + ); + // Ensure contribution does not overflow the actual raised amount // and it does not exceed the cap let left_to_raise = crowdloan @@ -415,11 +458,18 @@ pub mod pallet { .checked_add(amount) .ok_or(Error::::Overflow)?; - // Compute the new total contribution and ensure it does not overflow. - let contribution = Contributions::::get(crowdloan_id, &contributor) - .unwrap_or(Zero::zero()) - .checked_add(amount) - .ok_or(Error::::Overflow)?; + // Compute the new total contribution and ensure it does not overflow, we + // also increment the contributor count if the contribution is new. + let contribution = + if let Some(contribution) = Contributions::::get(crowdloan_id, &contributor) { + contribution + .checked_add(amount) + .ok_or(Error::::Overflow)? + } else { + // We have a new contribution + Self::increment_contributor_count(crowdloan_id); + amount + }; // Ensure contributor has enough balance to pay ensure!( @@ -476,6 +526,7 @@ pub mod pallet { Contributions::::insert(crowdloan_id, &who, crowdloan.deposit); } else { Contributions::::remove(crowdloan_id, &who); + Self::decrement_contributor_count(crowdloan_id); } CurrencyOf::::transfer( @@ -630,6 +681,7 @@ pub mod pallet { // Clear refunded contributors for contributor in refunded_contributors { Contributions::::remove(crowdloan_id, &contributor); + Self::decrement_contributor_count(crowdloan_id); } if all_refunded { @@ -682,6 +734,7 @@ pub mod pallet { creator_contribution, Preservation::Expendable, )?; + Contributions::::remove(crowdloan_id, &crowdloan.creator); // Clear the call from the preimage storage if let Some(call) = crowdloan.call { @@ -691,6 +744,7 @@ pub mod pallet { // Remove the crowdloan let _ = frame_system::Pallet::::dec_providers(&crowdloan.funds_account).defensive(); Crowdloans::::remove(crowdloan_id); + ContributorsCount::::remove(crowdloan_id); Self::deposit_event(Event::::Dissolved { crowdloan_id }); Ok(()) @@ -831,4 +885,16 @@ impl Pallet { ); Ok(()) } + + fn increment_contributor_count(crowdloan_id: CrowdloanId) { + ContributorsCount::::mutate(crowdloan_id, |count| { + *count = count.map(|v| v.saturating_add(1)) + }); + } + + fn decrement_contributor_count(crowdloan_id: CrowdloanId) { + ContributorsCount::::mutate(crowdloan_id, |count| { + *count = count.map(|v| v.saturating_sub(1)) + }); + } } diff --git a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs new file mode 100644 index 0000000000..684c4b1cff --- /dev/null +++ b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs @@ -0,0 +1,46 @@ +use alloc::string::String; +use frame_support::{BoundedVec, traits::Get, weights::Weight}; + +use crate::*; + +pub fn migrate_add_contributors_count() -> Weight { + let migration_name = + BoundedVec::truncate_from(b"migrate_crowdloan_contributors_count".to_vec()); + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + migration_name + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // Get all crowdloans, there is not so many at the moment so we are safe. + let crowdloan_ids = Crowdloans::::iter_keys().collect::>(); + weight = weight.saturating_add(T::DbWeight::get().reads(crowdloan_ids.len() as u64)); + + for crowdloan_id in crowdloan_ids { + let contributions = Contributions::::iter_key_prefix(crowdloan_id) + .collect::>() + .len(); + weight = weight.saturating_add(T::DbWeight::get().reads(contributions as u64)); + + ContributorsCount::::insert(crowdloan_id, contributions as u32); + } + + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/crowdloan/src/migrations/mod.rs b/pallets/crowdloan/src/migrations/mod.rs new file mode 100644 index 0000000000..f6701fb83a --- /dev/null +++ b/pallets/crowdloan/src/migrations/mod.rs @@ -0,0 +1,2 @@ +mod migrate_add_contributors_count; +pub use migrate_add_contributors_count::*; From 203a8a3561c70982cbcd5e56a1fd41cee07540a2 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 17:57:32 +0200 Subject: [PATCH 03/13] fix tests --- pallets/crowdloan/src/mock.rs | 2 + pallets/crowdloan/src/tests.rs | 148 +++++++++++++++++++++++++++++++-- 2 files changed, 142 insertions(+), 8 deletions(-) diff --git a/pallets/crowdloan/src/mock.rs b/pallets/crowdloan/src/mock.rs index 980b9fa26b..78cf15717c 100644 --- a/pallets/crowdloan/src/mock.rs +++ b/pallets/crowdloan/src/mock.rs @@ -111,6 +111,7 @@ parameter_types! { pub const MinimumBlockDuration: u64 = 20; pub const MaximumBlockDuration: u64 = 100; pub const RefundContributorsLimit: u32 = 5; + pub const MaxContributors: u32 = 10; } impl pallet_crowdloan::Config for Test { @@ -125,6 +126,7 @@ impl pallet_crowdloan::Config for Test { type MinimumBlockDuration = MinimumBlockDuration; type MaximumBlockDuration = MaximumBlockDuration; type RefundContributorsLimit = RefundContributorsLimit; + type MaxContributors = MaxContributors; } // A test pallet used to test some behavior of the crowdloan pallet diff --git a/pallets/crowdloan/src/tests.rs b/pallets/crowdloan/src/tests.rs index 45bc61e40a..b978dbadf0 100644 --- a/pallets/crowdloan/src/tests.rs +++ b/pallets/crowdloan/src/tests.rs @@ -1,7 +1,7 @@ #![cfg(test)] #![allow(clippy::arithmetic_side_effects, clippy::unwrap_used)] -use frame_support::{assert_err, assert_ok, traits::StorePreimage}; +use frame_support::{StorageDoubleMap, assert_err, assert_ok, traits::StorePreimage}; use frame_system::pallet_prelude::BlockNumberFor; use sp_core::U256; use sp_runtime::DispatchError; @@ -58,6 +58,11 @@ fn test_create_succeeds() { .collect::>(), vec![(creator, deposit)] ); + // ensure the contributor count is updated + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); // ensure the raised amount is updated correctly assert!( pallet_crowdloan::Crowdloans::::get(crowdloan_id) @@ -330,8 +335,15 @@ fn test_contribute_succeeds() { // run some blocks run_to_block(10); - // first contribution to the crowdloan from creator let crowdloan_id: CrowdloanId = 0; + + // only the creator has contributed so far + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); + + // first contribution to the crowdloan from creator let amount: BalanceOf = 50; assert_ok!(Crowdloan::contribute( RuntimeOrigin::signed(creator), @@ -351,6 +363,10 @@ fn test_contribute_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, creator), Some(100) ); + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); assert_eq!( Balances::free_balance(creator), 200 - amount - initial_deposit @@ -377,6 +393,10 @@ fn test_contribute_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor1), Some(100) ); + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(2) + ); assert_eq!(Balances::free_balance(contributor1), 500 - amount); // third contribution to the crowdloan @@ -400,6 +420,10 @@ fn test_contribute_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor2), Some(50) ); + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(3) + ); assert_eq!(Balances::free_balance(contributor2), 200 - amount); // ensure the contributions are present in the funds account @@ -656,6 +680,62 @@ fn test_contribute_fails_if_contribution_is_below_minimum_contribution() { }); } +#[test] +fn test_contribute_fails_if_max_contributors_has_been_reached() { + TestState::default() + .with_balance(U256::from(1), 100) + .with_balance(U256::from(2), 100) + .with_balance(U256::from(3), 100) + .with_balance(U256::from(4), 100) + .with_balance(U256::from(5), 100) + .with_balance(U256::from(6), 100) + .with_balance(U256::from(7), 100) + .with_balance(U256::from(8), 100) + .with_balance(U256::from(9), 100) + .with_balance(U256::from(10), 100) + .with_balance(U256::from(11), 100) + .build_and_execute(|| { + // create a crowdloan + let creator: AccountOf = U256::from(1); + let initial_deposit: BalanceOf = 50; + let min_contribution: BalanceOf = 10; + let cap: BalanceOf = 1000; + let end: BlockNumberFor = 50; + + assert_ok!(Crowdloan::create( + RuntimeOrigin::signed(creator), + initial_deposit, + min_contribution, + cap, + end, + Some(noop_call()), + None + )); + + // run some blocks + run_to_block(10); + + // contribute to the crowdloan + let crowdloan_id: CrowdloanId = 0; + let amount: BalanceOf = 20; + for i in 2..=10 { + let contributor: AccountOf = U256::from(i); + assert_ok!(Crowdloan::contribute( + RuntimeOrigin::signed(contributor), + crowdloan_id, + amount + )); + } + + // try to contribute + let contributor: AccountOf = U256::from(10); + assert_err!( + Crowdloan::contribute(RuntimeOrigin::signed(contributor), crowdloan_id, amount), + pallet_crowdloan::Error::::MaxContributorsReached + ); + }); +} + #[test] fn test_contribute_fails_if_contributor_has_insufficient_balance() { TestState::default() @@ -743,6 +823,12 @@ fn test_withdraw_from_contributor_succeeds() { // run some more blocks past the end of the contribution period run_to_block(60); + // ensure the contributor count is correct + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(3) + ); + // withdraw from contributor1 assert_ok!(Crowdloan::withdraw( RuntimeOrigin::signed(contributor1), @@ -753,6 +839,10 @@ fn test_withdraw_from_contributor_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor1), None, ); + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(2) + ); // ensure the contributor1 has the correct amount assert_eq!( pallet_balances::Pallet::::free_balance(contributor1), @@ -769,6 +859,10 @@ fn test_withdraw_from_contributor_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor2), None, ); + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); // ensure the contributor2 has the correct amount assert_eq!( pallet_balances::Pallet::::free_balance(contributor2), @@ -818,6 +912,12 @@ fn test_withdraw_from_creator_with_contribution_over_deposit_succeeds() { amount )); + // ensure the contributor count is correct + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); + // withdraw let crowdloan_id: CrowdloanId = 0; assert_ok!(Crowdloan::withdraw( @@ -835,6 +935,11 @@ fn test_withdraw_from_creator_with_contribution_over_deposit_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, creator), Some(initial_deposit), ); + // ensure the contributor count hasn't changed because deposit is kept + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); // ensure the crowdloan account has the correct amount let funds_account = pallet_crowdloan::Pallet::::funds_account(crowdloan_id); @@ -1478,6 +1583,12 @@ fn test_refund_succeeds() { )); } + // ensure the contributor count is correct + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(7) + ); + // run some more blocks past the end of the contribution period run_to_block(60); @@ -1487,6 +1598,12 @@ fn test_refund_succeeds() { crowdloan_id )); + // ensure the contributor count is correct, we processed 5 refunds + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(2) + ); + // ensure the crowdloan account has the correct amount let funds_account = pallet_crowdloan::Pallet::::funds_account(crowdloan_id); assert_eq!(Balances::free_balance(funds_account), 350 - 5 * amount); @@ -1510,6 +1627,13 @@ fn test_refund_succeeds() { crowdloan_id )); + // ensure the contributor count is correct, we processed 1 more refund + // keeping deposit + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); + // ensure the crowdloan account has the correct amount assert_eq!( pallet_balances::Pallet::::free_balance(funds_account), @@ -1638,15 +1762,15 @@ fn test_dissolve_succeeds() { // run some blocks past end run_to_block(60); - // refund the contributions let crowdloan_id: CrowdloanId = 0; - assert_ok!(Crowdloan::refund( - RuntimeOrigin::signed(creator), - crowdloan_id - )); + + // ensure the contributor count is correct + assert_eq!( + pallet_crowdloan::ContributorsCount::::get(crowdloan_id), + Some(1) + ); // dissolve the crowdloan - let crowdloan_id: CrowdloanId = 0; assert_ok!(Crowdloan::dissolve( RuntimeOrigin::signed(creator), crowdloan_id @@ -1655,6 +1779,14 @@ fn test_dissolve_succeeds() { // ensure the crowdloan is removed from the crowdloans map assert!(pallet_crowdloan::Crowdloans::::get(crowdloan_id).is_none()); + // ensure the contributions are removed + assert!(!pallet_crowdloan::Contributions::::contains_prefix( + crowdloan_id + )); + + // ensure the contributor count is removed + assert!(pallet_crowdloan::ContributorsCount::::get(crowdloan_id).is_none()); + // ensure the event is emitted assert_eq!( last_event(), From ea8700e95ccd13c805015e9b189d4420b5cf23e1 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 17:57:51 +0200 Subject: [PATCH 04/13] added MaxContributors to runtime --- runtime/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 88dabca33c..cba54cc3d0 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1447,6 +1447,7 @@ parameter_types! { 432000 // 60 days maximum (60 * 24 * 60 * 60 / 12) }; pub const RefundContributorsLimit: u32 = 50; + pub const MaxContributors: u32 = 500; } impl pallet_crowdloan::Config for Runtime { @@ -1461,6 +1462,7 @@ impl pallet_crowdloan::Config for Runtime { type MinimumBlockDuration = MinimumBlockDuration; type MaximumBlockDuration = MaximumBlockDuration; type RefundContributorsLimit = RefundContributorsLimit; + type MaxContributors = MaxContributors; } // Create the runtime by composing the FRAME pallets that were previously configured. From 6ec5d630a397209ecbc145be9ce2a9fe079900c2 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 18:01:36 +0200 Subject: [PATCH 05/13] fix migration name --- .../crowdloan/src/migrations/migrate_add_contributors_count.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs index 684c4b1cff..fc5de151cb 100644 --- a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs +++ b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs @@ -4,8 +4,7 @@ use frame_support::{BoundedVec, traits::Get, weights::Weight}; use crate::*; pub fn migrate_add_contributors_count() -> Weight { - let migration_name = - BoundedVec::truncate_from(b"migrate_crowdloan_contributors_count".to_vec()); + let migration_name = BoundedVec::truncate_from(b"migrate_add_contributors_count".to_vec()); let mut weight = T::DbWeight::get().reads(1); if HasMigrationRun::::get(&migration_name) { From d49c04eb16ee59bf86cfac416a919680e1fd8baa Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 18:18:41 +0200 Subject: [PATCH 06/13] added migration test --- .../migrate_add_contributors_count.rs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs index fc5de151cb..cb399634eb 100644 --- a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs +++ b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs @@ -43,3 +43,54 @@ pub fn migrate_add_contributors_count() -> Weight { weight } + +#[cfg(test)] +mod tests { + use crate::mock::{Test, TestState}; + + use super::*; + use sp_core::U256; + + #[test] + fn test_migrate_add_contributors_count_works() { + TestState::default().build_and_execute(|| { + Crowdloans::::insert( + 0, + CrowdloanInfo { + creator: U256::from(1), + deposit: 100, + min_contribution: 10, + cap: 1000, + end: 100, + call: None, + finalized: false, + raised: 0, + funds_account: U256::from(2), + target_address: None, + }, + ); + + Contributions::::insert(0, U256::from(1), 100); + Contributions::::insert(0, U256::from(2), 100); + Contributions::::insert(0, U256::from(3), 100); + + assert_eq!(ContributorsCount::::get(0), None); + assert_eq!( + HasMigrationRun::::get(BoundedVec::truncate_from( + b"migrate_add_contributors_count".to_vec() + )), + false + ); + + migrate_add_contributors_count::(); + + assert_eq!(ContributorsCount::::get(0), Some(3)); + assert_eq!( + HasMigrationRun::::get(BoundedVec::truncate_from( + b"migrate_add_contributors_count".to_vec() + )), + true + ); + }); + } +} From 6b37119bae0f88266215aa63601f56ef71da37ee Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 18:21:39 +0200 Subject: [PATCH 07/13] cargo clippy --- .../src/migrations/migrate_add_contributors_count.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs index cb399634eb..9d610fd94e 100644 --- a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs +++ b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs @@ -75,21 +75,19 @@ mod tests { Contributions::::insert(0, U256::from(3), 100); assert_eq!(ContributorsCount::::get(0), None); - assert_eq!( - HasMigrationRun::::get(BoundedVec::truncate_from( + assert!( + !HasMigrationRun::::get(BoundedVec::truncate_from( b"migrate_add_contributors_count".to_vec() - )), - false + )) ); migrate_add_contributors_count::(); assert_eq!(ContributorsCount::::get(0), Some(3)); - assert_eq!( + assert!( HasMigrationRun::::get(BoundedVec::truncate_from( b"migrate_add_contributors_count".to_vec() - )), - true + )) ); }); } From 96cdf6486d82cd1e8eaccfa47f9124a1c97708c0 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Mon, 5 May 2025 18:22:17 +0200 Subject: [PATCH 08/13] cargo fmt --- .../migrations/migrate_add_contributors_count.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs index 9d610fd94e..378e8bcc3d 100644 --- a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs +++ b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs @@ -75,20 +75,16 @@ mod tests { Contributions::::insert(0, U256::from(3), 100); assert_eq!(ContributorsCount::::get(0), None); - assert!( - !HasMigrationRun::::get(BoundedVec::truncate_from( - b"migrate_add_contributors_count".to_vec() - )) - ); + assert!(!HasMigrationRun::::get(BoundedVec::truncate_from( + b"migrate_add_contributors_count".to_vec() + ))); migrate_add_contributors_count::(); assert_eq!(ContributorsCount::::get(0), Some(3)); - assert!( - HasMigrationRun::::get(BoundedVec::truncate_from( - b"migrate_add_contributors_count".to_vec() - )) - ); + assert!(HasMigrationRun::::get(BoundedVec::truncate_from( + b"migrate_add_contributors_count".to_vec() + ))); }); } } From 016e7f3dbdce0eac2faa69349a7d72d8b3658d2e Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 6 May 2025 11:00:02 +0200 Subject: [PATCH 09/13] propagate log/std in Cargo.toml --- pallets/crowdloan/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/crowdloan/Cargo.toml b/pallets/crowdloan/Cargo.toml index 3098db490e..e8d582fa44 100644 --- a/pallets/crowdloan/Cargo.toml +++ b/pallets/crowdloan/Cargo.toml @@ -40,6 +40,7 @@ std = [ "sp-runtime/std", "sp-std/std", "sp-io/std", + "log/std", "sp-core/std", "pallet-balances/std", "pallet-preimage/std", From 08dd35e086c33b2b345842e17ea49ac8cf1ca19a Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 6 May 2025 18:07:12 +0200 Subject: [PATCH 10/13] move contributors count to Crowdloan struct and update migration --- pallets/crowdloan/Cargo.toml | 4 +- pallets/crowdloan/src/lib.rs | 34 +--- .../migrate_add_contributors_count.rs | 166 ++++++++++++++---- pallets/crowdloan/src/tests.rs | 87 +++++---- 4 files changed, 181 insertions(+), 110 deletions(-) diff --git a/pallets/crowdloan/Cargo.toml b/pallets/crowdloan/Cargo.toml index e8d582fa44..b662d7269f 100644 --- a/pallets/crowdloan/Cargo.toml +++ b/pallets/crowdloan/Cargo.toml @@ -22,12 +22,12 @@ frame-system.workspace = true sp-runtime.workspace = true sp-std.workspace = true log = { workspace = true } +sp-core = { default-features = true, workspace = true } +sp-io = { default-features = true, workspace = true } [dev-dependencies] pallet-balances = { default-features = true, workspace = true } pallet-preimage = { default-features = true, workspace = true } -sp-core = { default-features = true, workspace = true } -sp-io = { default-features = true, workspace = true } [features] default = ["std"] diff --git a/pallets/crowdloan/src/lib.rs b/pallets/crowdloan/src/lib.rs index 392413feba..b25807151a 100644 --- a/pallets/crowdloan/src/lib.rs +++ b/pallets/crowdloan/src/lib.rs @@ -51,7 +51,7 @@ pub type BoundedCallOf = Bounded<::RuntimeCall, ::Hashing>; /// A struct containing the information about a crowdloan. -#[freeze_struct("6b86ccf70fc1b8f1")] +#[freeze_struct("5db9538284491545")] #[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct CrowdloanInfo { /// The creator of the crowdloan. @@ -76,6 +76,8 @@ pub struct CrowdloanInfo { pub call: Option, /// Whether the crowdloan has been finalized. pub finalized: bool, + /// The number of contributors to the crowdloan. + pub contributors_count: u32, } pub type CrowdloanInfoOf = CrowdloanInfo< @@ -166,11 +168,6 @@ pub mod pallet { OptionQuery, >; - /// A map of crowdloan ids to their contributors count. - #[pallet::storage] - pub type ContributorsCount = - StorageMap<_, Twox64Concat, CrowdloanId, u32, OptionQuery>; - /// The current crowdloan id that will be set during the finalize call, making it /// temporarily accessible to the dispatched call. #[pallet::storage] @@ -376,6 +373,7 @@ pub mod pallet { target_address, call, finalized: false, + contributors_count: 1, }; Crowdloans::::insert(crowdloan_id, &crowdloan); @@ -388,7 +386,6 @@ pub mod pallet { )?; Contributions::::insert(crowdloan_id, &creator, deposit); - ContributorsCount::::insert(crowdloan_id, 1); Self::deposit_event(Event::::Created { crowdloan_id, @@ -434,10 +431,8 @@ pub mod pallet { ); // Ensure the crowdloan has not reached the maximum number of contributors - let contributors_count = - ContributorsCount::::get(crowdloan_id).ok_or(Error::::InvalidCrowdloanId)?; ensure!( - contributors_count < T::MaxContributors::get(), + crowdloan.contributors_count < T::MaxContributors::get(), Error::::MaxContributorsReached ); @@ -467,7 +462,7 @@ pub mod pallet { .ok_or(Error::::Overflow)? } else { // We have a new contribution - Self::increment_contributor_count(crowdloan_id); + crowdloan.contributors_count += 1; amount }; @@ -526,7 +521,7 @@ pub mod pallet { Contributions::::insert(crowdloan_id, &who, crowdloan.deposit); } else { Contributions::::remove(crowdloan_id, &who); - Self::decrement_contributor_count(crowdloan_id); + crowdloan.contributors_count -= 1; } CurrencyOf::::transfer( @@ -676,12 +671,12 @@ pub mod pallet { refund_count = refund_count.checked_add(1).ok_or(Error::::Overflow)?; } + crowdloan.contributors_count -= refund_count; Crowdloans::::insert(crowdloan_id, &crowdloan); // Clear refunded contributors for contributor in refunded_contributors { Contributions::::remove(crowdloan_id, &contributor); - Self::decrement_contributor_count(crowdloan_id); } if all_refunded { @@ -744,7 +739,6 @@ pub mod pallet { // Remove the crowdloan let _ = frame_system::Pallet::::dec_providers(&crowdloan.funds_account).defensive(); Crowdloans::::remove(crowdloan_id); - ContributorsCount::::remove(crowdloan_id); Self::deposit_event(Event::::Dissolved { crowdloan_id }); Ok(()) @@ -885,16 +879,4 @@ impl Pallet { ); Ok(()) } - - fn increment_contributor_count(crowdloan_id: CrowdloanId) { - ContributorsCount::::mutate(crowdloan_id, |count| { - *count = count.map(|v| v.saturating_add(1)) - }); - } - - fn decrement_contributor_count(crowdloan_id: CrowdloanId) { - ContributorsCount::::mutate(crowdloan_id, |count| { - *count = count.map(|v| v.saturating_sub(1)) - }); - } } diff --git a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs index 378e8bcc3d..d5fbc4ec97 100644 --- a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs +++ b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs @@ -1,8 +1,26 @@ use alloc::string::String; -use frame_support::{BoundedVec, traits::Get, weights::Weight}; +use frame_support::{BoundedVec, migration::storage_key_iter, traits::Get, weights::Weight}; use crate::*; +mod old_storage { + use super::*; + + #[derive(Encode, Decode, Debug)] + pub struct OldCrowdloanInfo { + pub creator: AccountId, + pub deposit: Balance, + pub min_contribution: Balance, + pub end: BlockNumber, + pub cap: Balance, + pub funds_account: AccountId, + pub raised: Balance, + pub target_address: Option, + pub call: Option, + pub finalized: bool, + } +} + pub fn migrate_add_contributors_count() -> Weight { let migration_name = BoundedVec::truncate_from(b"migrate_add_contributors_count".to_vec()); let mut weight = T::DbWeight::get().reads(1); @@ -20,17 +38,44 @@ pub fn migrate_add_contributors_count() -> Weight { String::from_utf8_lossy(&migration_name) ); - // Get all crowdloans, there is not so many at the moment so we are safe. - let crowdloan_ids = Crowdloans::::iter_keys().collect::>(); - weight = weight.saturating_add(T::DbWeight::get().reads(crowdloan_ids.len() as u64)); - - for crowdloan_id in crowdloan_ids { - let contributions = Contributions::::iter_key_prefix(crowdloan_id) + let pallet_name = b"Crowdloan"; + let item_name = b"Crowdloans"; + let crowdloans = storage_key_iter::< + CrowdloanId, + old_storage::OldCrowdloanInfo< + T::AccountId, + BalanceOf, + BlockNumberFor, + BoundedCallOf, + >, + Twox64Concat, + >(pallet_name, item_name) + .collect::>(); + weight = weight.saturating_add(T::DbWeight::get().reads(crowdloans.len() as u64)); + + for (id, crowdloan) in crowdloans { + let contributions = Contributions::::iter_key_prefix(id) .collect::>() .len(); weight = weight.saturating_add(T::DbWeight::get().reads(contributions as u64)); - ContributorsCount::::insert(crowdloan_id, contributions as u32); + Crowdloans::::insert( + id, + CrowdloanInfo { + creator: crowdloan.creator, + deposit: crowdloan.deposit, + min_contribution: crowdloan.min_contribution, + end: crowdloan.end, + cap: crowdloan.cap, + funds_account: crowdloan.funds_account, + raised: crowdloan.raised, + target_address: crowdloan.target_address, + call: crowdloan.call, + finalized: crowdloan.finalized, + contributors_count: contributions as u32, + }, + ); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); } HasMigrationRun::::insert(&migration_name, true); @@ -46,42 +91,93 @@ pub fn migrate_add_contributors_count() -> Weight { #[cfg(test)] mod tests { - use crate::mock::{Test, TestState}; + use frame_support::{Hashable, storage::unhashed::put_raw}; + use sp_core::U256; + use sp_io::hashing::twox_128; use super::*; - use sp_core::U256; + use crate::mock::{Test, TestState}; #[test] fn test_migrate_add_contributors_count_works() { TestState::default().build_and_execute(|| { - Crowdloans::::insert( - 0, - CrowdloanInfo { - creator: U256::from(1), - deposit: 100, - min_contribution: 10, - cap: 1000, - end: 100, - call: None, - finalized: false, - raised: 0, - funds_account: U256::from(2), - target_address: None, - }, - ); - - Contributions::::insert(0, U256::from(1), 100); - Contributions::::insert(0, U256::from(2), 100); - Contributions::::insert(0, U256::from(3), 100); - - assert_eq!(ContributorsCount::::get(0), None); - assert!(!HasMigrationRun::::get(BoundedVec::truncate_from( - b"migrate_add_contributors_count".to_vec() - ))); + let pallet_name = twox_128(b"Crowdloan"); + let storage_name = twox_128(b"Crowdloans"); + let prefix = [pallet_name, storage_name].concat(); + + let items = vec![ + ( + old_storage::OldCrowdloanInfo { + creator: U256::from(1), + deposit: 100u64, + min_contribution: 10u64, + end: 100u64, + cap: 1000u64, + funds_account: U256::from(2), + raised: 0u64, + target_address: None, + call: None::>, + finalized: false, + }, + vec![(U256::from(1), 100)], + ), + ( + old_storage::OldCrowdloanInfo { + creator: U256::from(1), + deposit: 100u64, + min_contribution: 10u64, + end: 100u64, + cap: 1000u64, + funds_account: U256::from(2), + raised: 0u64, + target_address: None, + call: None::>, + finalized: false, + }, + vec![ + (U256::from(1), 100), + (U256::from(2), 100), + (U256::from(3), 100), + ], + ), + ( + old_storage::OldCrowdloanInfo { + creator: U256::from(1), + deposit: 100u64, + min_contribution: 10u64, + end: 100u64, + cap: 1000u64, + funds_account: U256::from(2), + raised: 0u64, + target_address: None, + call: None::>, + finalized: false, + }, + vec![ + (U256::from(1), 100), + (U256::from(2), 100), + (U256::from(3), 100), + (U256::from(4), 100), + (U256::from(5), 100), + ], + ), + ]; + + for (id, (crowdloan, contributions)) in items.into_iter().enumerate() { + let key = [prefix.clone(), (id as u32).twox_64_concat()].concat(); + put_raw(&key, &crowdloan.encode()); + + for (contributor, amount) in contributions { + Contributions::::insert(id as u32, contributor, amount); + } + } migrate_add_contributors_count::(); - assert_eq!(ContributorsCount::::get(0), Some(3)); + assert!(Crowdloans::::get(0).is_some_and(|c| c.contributors_count == 1)); + assert!(Crowdloans::::get(1).is_some_and(|c| c.contributors_count == 3)); + assert!(Crowdloans::::get(2).is_some_and(|c| c.contributors_count == 5)); + assert!(HasMigrationRun::::get(BoundedVec::truncate_from( b"migrate_add_contributors_count".to_vec() ))); diff --git a/pallets/crowdloan/src/tests.rs b/pallets/crowdloan/src/tests.rs index b978dbadf0..1e03854b1f 100644 --- a/pallets/crowdloan/src/tests.rs +++ b/pallets/crowdloan/src/tests.rs @@ -46,6 +46,7 @@ fn test_create_succeeds() { target_address: None, call: Some(call), finalized: false, + contributors_count: 1, }) ); // ensure the crowdloan account has the deposit @@ -58,11 +59,6 @@ fn test_create_succeeds() { .collect::>(), vec![(creator, deposit)] ); - // ensure the contributor count is updated - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) - ); // ensure the raised amount is updated correctly assert!( pallet_crowdloan::Crowdloans::::get(crowdloan_id) @@ -338,9 +334,9 @@ fn test_contribute_succeeds() { let crowdloan_id: CrowdloanId = 0; // only the creator has contributed so far - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 1) ); // first contribution to the crowdloan from creator @@ -363,9 +359,9 @@ fn test_contribute_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, creator), Some(100) ); - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 1) ); assert_eq!( Balances::free_balance(creator), @@ -393,9 +389,9 @@ fn test_contribute_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor1), Some(100) ); - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(2) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 2) ); assert_eq!(Balances::free_balance(contributor1), 500 - amount); @@ -420,9 +416,9 @@ fn test_contribute_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor2), Some(50) ); - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(3) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 3) ); assert_eq!(Balances::free_balance(contributor2), 200 - amount); @@ -824,9 +820,9 @@ fn test_withdraw_from_contributor_succeeds() { run_to_block(60); // ensure the contributor count is correct - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(3) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 3) ); // withdraw from contributor1 @@ -839,9 +835,9 @@ fn test_withdraw_from_contributor_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor1), None, ); - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(2) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 2) ); // ensure the contributor1 has the correct amount assert_eq!( @@ -859,9 +855,9 @@ fn test_withdraw_from_contributor_succeeds() { pallet_crowdloan::Contributions::::get(crowdloan_id, contributor2), None, ); - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 1) ); // ensure the contributor2 has the correct amount assert_eq!( @@ -913,9 +909,9 @@ fn test_withdraw_from_creator_with_contribution_over_deposit_succeeds() { )); // ensure the contributor count is correct - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 1) ); // withdraw @@ -936,9 +932,9 @@ fn test_withdraw_from_creator_with_contribution_over_deposit_succeeds() { Some(initial_deposit), ); // ensure the contributor count hasn't changed because deposit is kept - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 1) ); // ensure the crowdloan account has the correct amount @@ -1584,9 +1580,9 @@ fn test_refund_succeeds() { } // ensure the contributor count is correct - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(7) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 7) ); // run some more blocks past the end of the contribution period @@ -1599,9 +1595,9 @@ fn test_refund_succeeds() { )); // ensure the contributor count is correct, we processed 5 refunds - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(2) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 2) ); // ensure the crowdloan account has the correct amount @@ -1629,9 +1625,9 @@ fn test_refund_succeeds() { // ensure the contributor count is correct, we processed 1 more refund // keeping deposit - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 1) ); // ensure the crowdloan account has the correct amount @@ -1765,9 +1761,9 @@ fn test_dissolve_succeeds() { let crowdloan_id: CrowdloanId = 0; // ensure the contributor count is correct - assert_eq!( - pallet_crowdloan::ContributorsCount::::get(crowdloan_id), - Some(1) + assert!( + pallet_crowdloan::Crowdloans::::get(crowdloan_id) + .is_some_and(|c| c.contributors_count == 1) ); // dissolve the crowdloan @@ -1784,9 +1780,6 @@ fn test_dissolve_succeeds() { crowdloan_id )); - // ensure the contributor count is removed - assert!(pallet_crowdloan::ContributorsCount::::get(crowdloan_id).is_none()); - // ensure the event is emitted assert_eq!( last_event(), From e79e783765fddb1f606993f6078c74ad9604c956 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 6 May 2025 18:26:48 +0200 Subject: [PATCH 11/13] fix benchmark --- pallets/crowdloan/Cargo.toml | 4 ++-- pallets/crowdloan/src/benchmarking.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/crowdloan/Cargo.toml b/pallets/crowdloan/Cargo.toml index b662d7269f..e8d582fa44 100644 --- a/pallets/crowdloan/Cargo.toml +++ b/pallets/crowdloan/Cargo.toml @@ -22,12 +22,12 @@ frame-system.workspace = true sp-runtime.workspace = true sp-std.workspace = true log = { workspace = true } -sp-core = { default-features = true, workspace = true } -sp-io = { default-features = true, workspace = true } [dev-dependencies] pallet-balances = { default-features = true, workspace = true } pallet-preimage = { default-features = true, workspace = true } +sp-core = { default-features = true, workspace = true } +sp-io = { default-features = true, workspace = true } [features] default = ["std"] diff --git a/pallets/crowdloan/src/benchmarking.rs b/pallets/crowdloan/src/benchmarking.rs index e36f792261..0891baf5af 100644 --- a/pallets/crowdloan/src/benchmarking.rs +++ b/pallets/crowdloan/src/benchmarking.rs @@ -68,6 +68,7 @@ mod benchmarks { target_address: Some(target_address.clone()), call: Some(T::Preimages::bound(*call).unwrap()), finalized: false, + contributors_count: 1, }) ); // ensure the creator has been deducted the deposit From 3f565fb3c4d91bfd262e94d5fbce5a4c0fc0e405 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Tue, 6 May 2025 18:28:28 +0200 Subject: [PATCH 12/13] fix arithmetic --- pallets/crowdloan/src/lib.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pallets/crowdloan/src/lib.rs b/pallets/crowdloan/src/lib.rs index b25807151a..1d4ed4e263 100644 --- a/pallets/crowdloan/src/lib.rs +++ b/pallets/crowdloan/src/lib.rs @@ -462,7 +462,10 @@ pub mod pallet { .ok_or(Error::::Overflow)? } else { // We have a new contribution - crowdloan.contributors_count += 1; + crowdloan.contributors_count = crowdloan + .contributors_count + .checked_add(1) + .ok_or(Error::::Overflow)?; amount }; @@ -521,7 +524,10 @@ pub mod pallet { Contributions::::insert(crowdloan_id, &who, crowdloan.deposit); } else { Contributions::::remove(crowdloan_id, &who); - crowdloan.contributors_count -= 1; + crowdloan.contributors_count = crowdloan + .contributors_count + .checked_sub(1) + .ok_or(Error::::Underflow)?; } CurrencyOf::::transfer( @@ -671,7 +677,10 @@ pub mod pallet { refund_count = refund_count.checked_add(1).ok_or(Error::::Overflow)?; } - crowdloan.contributors_count -= refund_count; + crowdloan.contributors_count = crowdloan + .contributors_count + .checked_sub(refund_count) + .ok_or(Error::::Underflow)?; Crowdloans::::insert(crowdloan_id, &crowdloan); // Clear refunded contributors From 79490d334e99b9308cdb71604c38ae8e5e6aaa1c Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 7 May 2025 09:22:46 +0200 Subject: [PATCH 13/13] added freeze_struct to OldCrowdloanInfo --- .../crowdloan/src/migrations/migrate_add_contributors_count.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs index d5fbc4ec97..3b094843ce 100644 --- a/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs +++ b/pallets/crowdloan/src/migrations/migrate_add_contributors_count.rs @@ -1,11 +1,13 @@ use alloc::string::String; use frame_support::{BoundedVec, migration::storage_key_iter, traits::Get, weights::Weight}; +use subtensor_macros::freeze_struct; use crate::*; mod old_storage { use super::*; + #[freeze_struct("84bcbf9b8d3f0ddf")] #[derive(Encode, Decode, Debug)] pub struct OldCrowdloanInfo { pub creator: AccountId,