From 3cd49b3e6453f5c9510da412158f8c5537b15cda Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 24 Dec 2024 18:21:17 +0900 Subject: [PATCH 01/62] new start --- Cargo.toml | 2 + substrate/bin/node/runtime/src/lib.rs | 49 +++++++ substrate/frame/opf/Cargo.toml | 90 ++++++++++++ substrate/frame/opf/src/lib.rs | 146 +++++++++++++++++++ substrate/frame/opf/src/types.rs | 201 ++++++++++++++++++++++++++ umbrella/Cargo.toml | 11 +- 6 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 substrate/frame/opf/Cargo.toml create mode 100644 substrate/frame/opf/src/lib.rs create mode 100644 substrate/frame/opf/src/types.rs diff --git a/Cargo.toml b/Cargo.toml index 64a11a340d10..de3e59b5d8f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -390,6 +390,7 @@ members = [ "substrate/frame/nomination-pools/test-transfer-stake", "substrate/frame/offences", "substrate/frame/offences/benchmarking", + "substrate/frame/opf", "substrate/frame/paged-list", "substrate/frame/paged-list/fuzzer", "substrate/frame/parameters", @@ -963,6 +964,7 @@ pallet-nomination-pools-benchmarking = { path = "substrate/frame/nomination-pool pallet-nomination-pools-runtime-api = { path = "substrate/frame/nomination-pools/runtime-api", default-features = false } pallet-offences = { path = "substrate/frame/offences", default-features = false } pallet-offences-benchmarking = { path = "substrate/frame/offences/benchmarking", default-features = false } +pallet-opf = { path = "substrate/frame/opf", default-features = false } pallet-paged-list = { path = "substrate/frame/paged-list", default-features = false } pallet-parachain-template = { path = "templates/parachain/pallets/template", default-features = false } pallet-parameters = { path = "substrate/frame/parameters", default-features = false } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index faffcd23fbcf..0eaa636f8efd 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1588,6 +1588,51 @@ impl pallet_offences::Config for Runtime { type OnOffenceHandler = Staking; } + +parameter_types! { + // Id of the treasury + pub const PotId: PalletId = PalletId(*b"py/potid"); + pub const VotingPeriod:BlockNumber = 30 * DAYS; + pub const ClaimingPeriod: BlockNumber = 7 * DAYS; + pub const VoteValidityPeriod: BlockNumber = 7 * DAYS; + pub const MaxProjects:u32 = 50; + + +} +impl pallet_opf::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + + type NativeBalance = Balances; + + /// Pot PalletId + type PotId = PotId; + + /// Maximum number of whitelisted projects + type MaxProjects = MaxProjects; + + /// Time period in which people can vote. + /// After the period has ended, the votes are counted (STOP THE COUNT) + /// and then the funds are distributed into Spends. + type VotingPeriod = VotingPeriod; + + /// Time for claiming a Spend. + /// After the period has passed, a spend is thrown away + /// and the funds are available again for distribution in the pot. + type ClaimingPeriod = ClaimingPeriod; + + /// Period after which all the votes are resetted. + type VoteValidityPeriod = VoteValidityPeriod; + + type BlockNumberProvider = System; + + type Preimages = Preimage; + + type Scheduler = Scheduler; + + type WeightInfo = (); //pallet_opf::weights::SubstrateWeight; +} + impl pallet_authority_discovery::Config for Runtime { type MaxAuthorities = MaxAuthorities; } @@ -2633,6 +2678,9 @@ mod runtime { #[runtime::pallet_index(81)] pub type VerifySignature = pallet_verify_signature::Pallet; + + #[runtime::pallet_index(82)] + pub type Opf = pallet_opf::Pallet; } impl TryFrom for pallet_revive::Call { @@ -2893,6 +2941,7 @@ mod benches { [pallet_example_mbm, PalletExampleMbms] [pallet_asset_conversion_ops, AssetConversionMigration] [pallet_verify_signature, VerifySignature] + [pallet_opf, Opf] ); } diff --git a/substrate/frame/opf/Cargo.toml b/substrate/frame/opf/Cargo.toml new file mode 100644 index 000000000000..08bfa9c4543c --- /dev/null +++ b/substrate/frame/opf/Cargo.toml @@ -0,0 +1,90 @@ +[package] +authors.workspace = true +description = "Optimist Project Funding - pallet allowing users to nominate projects to be funded, by locking their DOTS." +edition.workspace = true +homepage = "https://substrate.io" +license = "Apache-2.0" +name = "pallet-opf" +readme = "README.md" +repository.workspace = true +version = "0.1.0" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { workspace = true, default-features = false } +frame-benchmarking = { optional = true, workspace = true, default-features = false } +frame-support = { workspace = true, default-features = false } +frame-system = { workspace = true, default-features = false } +log = { workspace = true } +pallet-conviction-voting = { workspace = true, default-features = false } +scale-info = { features = [ + "derive", +], workspace = true, default-features = false } +sp-core = { workspace = true, default-features = false } +sp-io = { workspace = true, default-features = false } +sp-runtime = { workspace = true, default-features = false } + +[dev-dependencies] +pallet-assets = { workspace = true, default-features = true } +pallet-balances = { workspace = true, default-features = true } +pallet-preimage = { workspace = true, default-features = true } +pallet-scheduler = { workspace = true, default-features = true } +pallet-sudo = { workspace = true, default-features = true } +pallet-timestamp = { workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = 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", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-preimage/runtime-benchmarks", + "pallet-scheduler/runtime-benchmarks", + "pallet-sudo/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-transaction-payment/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", + "pallet-conviction-voting/std", + "pallet-preimage/std", + "pallet-scheduler/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/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", + "pallet-conviction-voting/try-runtime", + "pallet-preimage/try-runtime", + "pallet-scheduler/try-runtime", + "pallet-sudo/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs new file mode 100644 index 000000000000..86ad8ab08d6e --- /dev/null +++ b/substrate/frame/opf/src/lib.rs @@ -0,0 +1,146 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +mod functions; +mod types; +pub use pallet_scheduler as Schedule; +pub use types::*; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; +pub mod weights; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeCall: Parameter + + Dispatchable + + From> + + IsType<::RuntimeCall> + + From>; + + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Type to access the Balances Pallet. + type NativeBalance: fungible::Inspect + + fungible::Mutate + + fungible::hold::Inspect + + fungible::hold::Mutate + + fungible::freeze::Inspect + + fungible::freeze::Mutate; + + /// Provider for the block number. + type BlockNumberProvider: BlockNumberProvider>; + + /// Treasury account Id + #[pallet::constant] + type PotId: Get; + + /// The preimage provider. + type Preimages: QueryPreimage + StorePreimage; + + /// The Scheduler. + type Scheduler: ScheduleAnon< + BlockNumberFor, + CallOf, + PalletsOriginOf, + Hasher = Self::Hashing, + > + ScheduleNamed< + BlockNumberFor, + CallOf, + PalletsOriginOf, + Hasher = Self::Hashing, + >; + + /// Time period in which people can vote. + /// After the period has ended, the votes are counted (STOP THE COUNT) + /// and then the funds are distributed into Spends. + #[pallet::constant] + type VotingPeriod: Get>; + + /// Maximum number projects that can be accepted by this pallet + #[pallet::constant] + type MaxProjects: Get; + + /// Time for claiming a Spend. + /// After the period has passed, a spend is thrown away + /// and the funds are available again for distribution in the pot. + #[pallet::constant] + type ClaimingPeriod: Get>; + + /// Period after which all the votes are resetted. + #[pallet::constant] + type VoteValidityPeriod: Get>; + + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + /// Spends that still have to be claimed. + #[pallet::storage] + pub(super) type Spends = + CountedStorageMap<_, Twox64Concat, ProjectId, SpendInfo, OptionQuery>; + + /// List of whitelisted projects to be rewarded + #[pallet::storage] + pub type Projects = + StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Reward successfully claimed + RewardClaimed { when: BlockNumberFor, amount: BalanceOf, project_id: ProjectId }, + + /// A Spend was created + SpendCreated { when: BlockNumberFor, amount: BalanceOf, project_id: ProjectId }, + + /// Not yet in the claiming period + NotClaimingPeriod { project_id: ProjectId, claiming_period: BlockNumberFor }, + + /// Payment will be enacted for corresponding project + WillBeEnacted { project_id: ProjectId }, + } + + #[pallet::error] + pub enum Error { + /// Not enough Funds in the Pot + InsufficientPotReserves, + /// The funds transfer operation failed + TransferFailed, + /// Spend or Spend index does not exists + InexistentSpend, + /// No valid Account_id found + NoValidAccount, + /// No project available for funding + NoProjectAvailable, + /// The Funds transfer failed + FailedSpendOperation, + /// Still not in claiming period + NotClaimingPeriod, + /// Funds locking failed + FundsReserveFailed, + /// An invalid result was returned + InvalidResult, + } + + /*#[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(n: BlockNumberFor) -> Weight { + Self::begin_block(n) + } + }*/ +} \ No newline at end of file diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs new file mode 100644 index 000000000000..912440417c7a --- /dev/null +++ b/substrate/frame/opf/src/types.rs @@ -0,0 +1,201 @@ +// 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. + +//! Types & Imports for Distribution pallet. + +pub use super::*; + +pub use frame_support::{ + pallet_prelude::*, + traits::{ + fungible, + fungible::{Inspect, Mutate, MutateHold}, + fungibles, + schedule::{ + v3::{Anon as ScheduleAnon, Named as ScheduleNamed}, + DispatchTime, MaybeHashed, + }, + tokens::{Precision, Preservation}, + Bounded, DefensiveOption, EnsureOrigin, LockIdentifier, OriginTrait, QueryPreimage, + StorePreimage, + }, + transactional, PalletId, Serialize, +}; +pub use frame_system::{pallet_prelude::*, RawOrigin}; +pub use scale_info::prelude::vec::Vec; +pub use sp_runtime::traits::{ + AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, Zero, +}; +pub use sp_std::boxed::Box; +pub use weights::WeightInfo; + +pub type BalanceOf = <::NativeBalance as fungible::Inspect< + ::AccountId, +>>::Balance; +pub type AccountIdOf = ::AccountId; +/// A reward index. +pub type SpendIndex = u32; +pub type CallOf = ::RuntimeCall; +pub type BoundedCallOf = Bounded, ::Hashing>; +pub type ProjectId = AccountIdOf; +pub type PalletsOriginOf = + <::RuntimeOrigin as OriginTrait>::PalletsOrigin; +pub const DISTRIBUTION_ID: LockIdentifier = *b"distribu"; +ub type RoundIndex = u32; +pub type VoterId = AccountIdOf; + +/// The state of the payment claim. +#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo, Default)] +pub enum SpendState { + /// Unclaimed + #[default] + Unclaimed, + /// Claimed & Paid. + Completed, + /// Claimed but Failed. + Failed, +} + +//Processed Reward status +#[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct SpendInfo { + /// The asset amount of the spend. + pub amount: BalanceOf, + /// The block number from which the spend can be claimed(24h after SpendStatus Creation). + pub valid_from: BlockNumberFor, + /// Corresponding project id + pub whitelisted_project: AccountIdOf, + /// Has it been claimed? + pub claimed: bool, + /// Claim Expiration block + pub expire: BlockNumberFor, +} + +impl SpendInfo { + pub fn new(whitelisted: &ProjectInfo) -> Self { + let amount = whitelisted.amount; + let whitelisted_project = whitelisted.project_id.clone(); + let claimed = false; + let valid_from = + >::block_number(); + let expire = valid_from.clone().saturating_add(T::ClaimingPeriod::get()) + + let spend = SpendInfo { amount, valid_from, whitelisted_project, claimed, expire }; + + //Add it to the Spends storage + Spends::::insert(whitelisted.project_id.clone(), spend.clone()); + + spend + } +} + +#[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct ProjectInfo { + /// AcountId that will receive the payment. + pub project_id: ProjectId, + + /// Block at which the project was submitted for reward distribution + pub submission_block: BlockNumberFor, + + /// Amount to be lock & pay for this project + pub amount: BalanceOf, +} + +#[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct VoteInfo { + /// The amount of stake/slash placed on this vote. + pub amount: BalanceOf, + + /// Round at which the vote was casted + pub round: VotingRoundInfo, + + /// Whether the vote is "fund" / "not fund" + pub is_fund: bool, + + pub conviction: Conviction, + + pub funds_unlock_block: BlockNumberFor, +} + +// If no conviction, user's funds are released at the end of the voting round +impl VoteInfo { + pub fn funds_unlock(&mut self) { + let conviction_coeff = >::from(self.conviction); + let funds_unlock_block = self + .round + .round_ending_block + .saturating_add(T::VoteLockingPeriod::get().saturating_mul(conviction_coeff.into())); + self.funds_unlock_block = funds_unlock_block; + } +} + +impl Default for VoteInfo { + // Dummy vote infos used to handle errors + fn default() -> Self { + // get round number + let round = VotingRounds::::get(0).expect("Round 0 exists"); + let amount = Zero::zero(); + let is_fund = false; + let conviction = Conviction::None; + let funds_unlock_block = round.round_ending_block; + VoteInfo { amount, round, is_fund, conviction, funds_unlock_block } + } +} + +/// Voting rounds are periodically created inside a hook on_initialize (use poll in the future) +#[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct VotingRoundInfo { + pub round_number: u32, + pub round_starting_block: BlockNumberFor, + pub round_ending_block: BlockNumberFor, + pub total_positive_votes_amount: BalanceOf, + pub total_negative_votes_amount: BalanceOf, +} + +impl VotingRoundInfo { + pub fn new() -> Self { + let round_starting_block = T::BlockNumberProvider::current_block_number(); + let round_ending_block = round_starting_block + .clone() + .checked_add(&T::VotingPeriod::get()) + .expect("Invalid Result"); + let round_number = VotingRoundNumber::::get(); + let new_number = round_number.checked_add(1).expect("Invalid Result"); + VotingRoundNumber::::put(new_number); + let total_positive_votes_amount = BalanceOf::::zero(); + let total_negative_votes_amount = BalanceOf::::zero(); + + Pallet::::deposit_event(Event::::VotingRoundStarted { + when: round_starting_block, + round_number, + }); + + let round_infos = VotingRoundInfo { + round_number, + round_starting_block, + round_ending_block, + total_positive_votes_amount, + total_negative_votes_amount, + }; + VotingRounds::::insert(round_number, round_infos.clone()); + round_infos + } +} \ No newline at end of file diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index f36d39d63f6a..221bd2bd00e2 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -112,6 +112,7 @@ std = [ "pallet-nomination-pools?/std", "pallet-offences-benchmarking?/std", "pallet-offences?/std", + "pallet-opf?/std", "pallet-paged-list?/std", "pallet-parameters?/std", "pallet-preimage?/std", @@ -300,6 +301,7 @@ runtime-benchmarks = [ "pallet-nomination-pools?/runtime-benchmarks", "pallet-offences-benchmarking?/runtime-benchmarks", "pallet-offences?/runtime-benchmarks", + "pallet-opf?/runtime-benchmarks", "pallet-paged-list?/runtime-benchmarks", "pallet-parameters?/runtime-benchmarks", "pallet-preimage?/runtime-benchmarks", @@ -434,6 +436,7 @@ try-runtime = [ "pallet-node-authorization?/try-runtime", "pallet-nomination-pools?/try-runtime", "pallet-offences?/try-runtime", + "pallet-opf?/try-runtime", "pallet-paged-list?/try-runtime", "pallet-parameters?/try-runtime", "pallet-preimage?/try-runtime", @@ -495,6 +498,7 @@ serde = [ "pallet-democracy?/serde", "pallet-message-queue?/serde", "pallet-offences?/serde", + "pallet-opf?/serde", "pallet-parameters?/serde", "pallet-referenda?/serde", "pallet-remark?/serde", @@ -541,7 +545,7 @@ 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-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-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-verify-signature", "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-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-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-opf", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "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-verify-signature", "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", @@ -1148,6 +1152,11 @@ default-features = false optional = true path = "../substrate/frame/offences/benchmarking" +[dependencies.pallet-opf] +default-features = false +optional = true +path = "../substrate/frame/opf" + [dependencies.pallet-paged-list] default-features = false optional = true From 859e6b3ebfd4b54959fcd2185336c157252c07f5 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 24 Dec 2024 21:22:28 +0900 Subject: [PATCH 02/62] New OPF pallet in work... --- Cargo.lock | 27 +++++++++++++++ substrate/frame/opf/Cargo.toml | 5 +++ substrate/frame/opf/src/lib.rs | 57 ++++++++++++++++++++++++-------- substrate/frame/opf/src/types.rs | 9 +++-- umbrella/src/lib.rs | 5 +++ 5 files changed, 84 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6151ed33c5b6..126e18d6a742 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14530,6 +14530,32 @@ dependencies = [ "sp-staking 36.0.0", ] +[[package]] +name = "pallet-opf" +version = "0.1.0" +dependencies = [ + "frame-benchmarking 28.0.0", + "frame-support 28.0.0", + "frame-system 28.0.0", + "log", + "pallet-assets 29.1.0", + "pallet-balances 28.0.0", + "pallet-conviction-voting 28.0.0", + "pallet-preimage 28.0.0", + "pallet-scheduler 29.0.0", + "pallet-sudo 28.0.0", + "pallet-timestamp 27.0.0", + "pallet-transaction-payment 28.0.0", + "pallet-transaction-payment-rpc-runtime-api 28.0.0", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", + "sp-std 14.0.0", +] + [[package]] name = "pallet-paged-list" version = "0.6.0" @@ -18749,6 +18775,7 @@ dependencies = [ "pallet-nomination-pools-runtime-api 23.0.0", "pallet-offences 27.0.0", "pallet-offences-benchmarking 28.0.0", + "pallet-opf", "pallet-paged-list 0.6.0", "pallet-parameters 0.1.0", "pallet-preimage 28.0.0", diff --git a/substrate/frame/opf/Cargo.toml b/substrate/frame/opf/Cargo.toml index 08bfa9c4543c..db8d32613361 100644 --- a/substrate/frame/opf/Cargo.toml +++ b/substrate/frame/opf/Cargo.toml @@ -22,12 +22,15 @@ frame-support = { workspace = true, default-features = false } frame-system = { workspace = true, default-features = false } log = { workspace = true } pallet-conviction-voting = { workspace = true, default-features = false } +pallet-scheduler = { workspace = true, default-features = false } scale-info = { features = [ "derive", ], workspace = true, default-features = false } +serde = { optional = true, workspace = true, default-features = true } sp-core = { workspace = true, default-features = false } sp-io = { workspace = true, default-features = false } sp-runtime = { workspace = true, default-features = false } +sp-std = { workspace = true, default-features = true } [dev-dependencies] pallet-assets = { workspace = true, default-features = true } @@ -53,6 +56,8 @@ runtime-benchmarks = [ "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-transaction-payment/runtime-benchmarks", + "scale-info/std", + "serde", "sp-runtime/runtime-benchmarks", ] std = [ diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 86ad8ab08d6e..e81ee5514feb 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -1,24 +1,16 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; -mod functions; mod types; pub use pallet_scheduler as Schedule; pub use types::*; -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; -pub mod weights; #[frame_support::pallet] pub mod pallet { use super::*; + use frame_system::WeightInfo; #[pallet::pallet] pub struct Pallet(_); @@ -35,11 +27,7 @@ pub mod pallet { /// Type to access the Balances Pallet. type NativeBalance: fungible::Inspect - + fungible::Mutate - + fungible::hold::Inspect - + fungible::hold::Mutate - + fungible::freeze::Inspect - + fungible::freeze::Mutate; + + fungible::Mutate; /// Provider for the block number. type BlockNumberProvider: BlockNumberProvider>; @@ -89,6 +77,15 @@ pub mod pallet { type WeightInfo: WeightInfo; } + /// Number of Voting Rounds executed so far + #[pallet::storage] + pub type VotingRoundNumber = StorageValue<_, u32, ValueQuery>; + + /// Returns Infos about a Voting Round agains the Voting Round index + #[pallet::storage] + pub type VotingRounds = + StorageMap<_, Twox64Concat, RoundIndex, VotingRoundInfo, OptionQuery>; + /// Spends that still have to be claimed. #[pallet::storage] pub(super) type Spends = @@ -113,6 +110,38 @@ pub mod pallet { /// Payment will be enacted for corresponding project WillBeEnacted { project_id: ProjectId }, + + /// Reward successfully assigned + RewardsAssigned { when: BlockNumberFor }, + + /// User's vote successfully submitted + VoteCasted { who: VoterId, when: BlockNumberFor, project_id: ProjectId }, + + /// User's vote successfully removed + VoteRemoved { who: VoterId, when: BlockNumberFor, project_id: ProjectId }, + + /// Project removed from whitelisted projects list + ProjectUnlisted { when: BlockNumberFor, project_id: ProjectId }, + + /// Project Funding Accepted by voters + ProjectFundingAccepted { + project_id: ProjectId, + when: BlockNumberFor, + round_number: u32, + amount: BalanceOf, + }, + + /// Project Funding rejected by voters + ProjectFundingRejected { when: BlockNumberFor, project_id: ProjectId }, + + /// A new voting round started + VotingRoundStarted { when: BlockNumberFor, round_number: u32 }, + + /// The users voting period ended. Reward calculation will start. + VoteActionLocked { when: BlockNumberFor, round_number: u32 }, + + /// The voting round ended + VotingRoundEnded { when: BlockNumberFor, round_number: u32 }, } #[pallet::error] diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 912440417c7a..2594d902861d 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -35,13 +35,13 @@ pub use frame_support::{ }, transactional, PalletId, Serialize, }; +pub use pallet_conviction_voting::Conviction; pub use frame_system::{pallet_prelude::*, RawOrigin}; pub use scale_info::prelude::vec::Vec; pub use sp_runtime::traits::{ AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, Zero, }; pub use sp_std::boxed::Box; -pub use weights::WeightInfo; pub type BalanceOf = <::NativeBalance as fungible::Inspect< ::AccountId, @@ -55,7 +55,7 @@ pub type ProjectId = AccountIdOf; pub type PalletsOriginOf = <::RuntimeOrigin as OriginTrait>::PalletsOrigin; pub const DISTRIBUTION_ID: LockIdentifier = *b"distribu"; -ub type RoundIndex = u32; +pub type RoundIndex = u32; pub type VoterId = AccountIdOf; /// The state of the payment claim. @@ -93,7 +93,7 @@ impl SpendInfo { let claimed = false; let valid_from = >::block_number(); - let expire = valid_from.clone().saturating_add(T::ClaimingPeriod::get()) + let expire = valid_from.clone().saturating_add(T::ClaimingPeriod::get()); let spend = SpendInfo { amount, valid_from, whitelisted_project, claimed, expire }; @@ -140,8 +140,7 @@ impl VoteInfo { let conviction_coeff = >::from(self.conviction); let funds_unlock_block = self .round - .round_ending_block - .saturating_add(T::VoteLockingPeriod::get().saturating_mul(conviction_coeff.into())); + .round_ending_block; self.funds_unlock_block = funds_unlock_block; } } diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 7b3c869588f0..7bb7cf3211c3 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -543,6 +543,11 @@ pub use pallet_offences; #[cfg(feature = "pallet-offences-benchmarking")] pub use pallet_offences_benchmarking; +/// Optimist Project Funding - pallet allowing users to nominate projects to be funded, by +/// locking their DOTS. +#[cfg(feature = "pallet-opf")] +pub use pallet_opf; + /// FRAME pallet that provides a paged list data structure. #[cfg(feature = "pallet-paged-list")] pub use pallet_paged_list; From 788b33f7ecffde9ff7e9f159553ca977fa80781d Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 26 Dec 2024 11:49:19 +0900 Subject: [PATCH 03/62] Added main extrinsics --- substrate/frame/opf/src/function.rs | 0 substrate/frame/opf/src/lib.rs | 45 +++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 substrate/frame/opf/src/function.rs diff --git a/substrate/frame/opf/src/function.rs b/substrate/frame/opf/src/function.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index e81ee5514feb..bd0ff45832f6 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -7,7 +7,7 @@ pub use types::*; -#[frame_support::pallet] +#[frame_support::pallet(dev_mode)] pub mod pallet { use super::*; use frame_system::WeightInfo; @@ -68,7 +68,7 @@ pub mod pallet { #[pallet::constant] type ClaimingPeriod: Get>; - /// Period after which all the votes are resetted. + /// Period after which all the votes are reset. #[pallet::constant] type VoteValidityPeriod: Get>; @@ -172,4 +172,45 @@ pub mod pallet { Self::begin_block(n) } }*/ + + #[pallet::call] + impl Pallet { + + #[pallet::call_index(0)] + #[transactional] + pub fn register_project(origin: OriginFor) -> DispatchResult{ + Ok(()) + } + + #[pallet::call_index(1)] + #[transactional] + pub fn unregister_project(origin: OriginFor) -> DispatchResult { + Ok(()) + } + + #[pallet::call_index(2)] + #[transactional] + pub fn vote(origin: OriginFor) -> DispatchResult { + Ok(()) + } + + #[pallet::call_index(3)] + #[transactional] + pub fn remove_vote(origin: OriginFor) -> DispatchResult { + Ok(()) + } + + #[pallet::call_index(4)] + #[transactional] + pub fn claim_reward_for(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + Ok(()) + } + + #[pallet::call_index(5)] + #[transactional] + pub fn execute_claim(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + Ok(()) + } + + } } \ No newline at end of file From 0f6bd3d0a91d4ce2b34a8c51633d95174a814ff9 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 11:20:45 +0900 Subject: [PATCH 04/62] extrinsic #0 added --- substrate/frame/opf/src/function.rs | 0 substrate/frame/opf/src/functions.rs | 32 +++++++++++++ substrate/frame/opf/src/lib.rs | 70 +++++++++++++++++++--------- substrate/frame/opf/src/types.rs | 15 +++--- 4 files changed, 88 insertions(+), 29 deletions(-) delete mode 100644 substrate/frame/opf/src/function.rs create mode 100644 substrate/frame/opf/src/functions.rs diff --git a/substrate/frame/opf/src/function.rs b/substrate/frame/opf/src/function.rs deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs new file mode 100644 index 000000000000..542d73242bd1 --- /dev/null +++ b/substrate/frame/opf/src/functions.rs @@ -0,0 +1,32 @@ +// 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. +//! Helper functions for OPF pallet. + + +pub use super::*; +impl Pallet { + + // Helper function for project registration + pub fn register_new(project_id: ProjectId, amount: BalanceOf) -> DispatchResult{ + let submission_block = T::BlockNumberProvider::current_block_number(); + let project_infos: ProjectInfo = ProjectInfo { project_id, submission_block, amount}; + let mut bounded = Projects::get(); + let _ = bounded.try_push(project_infos); + Projects::put(bounded); + Ok(()) + } +} \ No newline at end of file diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index bd0ff45832f6..96e3e4014402 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -1,12 +1,28 @@ +// 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. + #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; mod types; +mod functions; pub use pallet_scheduler as Schedule; pub use types::*; - - #[frame_support::pallet(dev_mode)] pub mod pallet { use super::*; @@ -30,7 +46,7 @@ pub mod pallet { + fungible::Mutate; /// Provider for the block number. - type BlockNumberProvider: BlockNumberProvider>; + type BlockNumberProvider: BlockNumberProvider; /// Treasury account Id #[pallet::constant] @@ -41,12 +57,12 @@ pub mod pallet { /// The Scheduler. type Scheduler: ScheduleAnon< - BlockNumberFor, + ProvidedBlockNumberFor, CallOf, PalletsOriginOf, Hasher = Self::Hashing, > + ScheduleNamed< - BlockNumberFor, + ProvidedBlockNumberFor, CallOf, PalletsOriginOf, Hasher = Self::Hashing, @@ -56,7 +72,7 @@ pub mod pallet { /// After the period has ended, the votes are counted (STOP THE COUNT) /// and then the funds are distributed into Spends. #[pallet::constant] - type VotingPeriod: Get>; + type VotingPeriod: Get>; /// Maximum number projects that can be accepted by this pallet #[pallet::constant] @@ -66,11 +82,11 @@ pub mod pallet { /// After the period has passed, a spend is thrown away /// and the funds are available again for distribution in the pot. #[pallet::constant] - type ClaimingPeriod: Get>; + type ClaimingPeriod: Get>; /// Period after which all the votes are reset. #[pallet::constant] - type VoteValidityPeriod: Get>; + type VoteValidityPeriod: Get>; /// Weight information for extrinsics in this pallet. @@ -100,48 +116,51 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Reward successfully claimed - RewardClaimed { when: BlockNumberFor, amount: BalanceOf, project_id: ProjectId }, + RewardClaimed { when: ProvidedBlockNumberFor, amount: BalanceOf, project_id: ProjectId }, /// A Spend was created - SpendCreated { when: BlockNumberFor, amount: BalanceOf, project_id: ProjectId }, + SpendCreated { when: ProvidedBlockNumberFor, amount: BalanceOf, project_id: ProjectId }, /// Not yet in the claiming period - NotClaimingPeriod { project_id: ProjectId, claiming_period: BlockNumberFor }, + NotClaimingPeriod { project_id: ProjectId, claiming_period: ProvidedBlockNumberFor }, /// Payment will be enacted for corresponding project WillBeEnacted { project_id: ProjectId }, /// Reward successfully assigned - RewardsAssigned { when: BlockNumberFor }, + RewardsAssigned { when: ProvidedBlockNumberFor }, /// User's vote successfully submitted - VoteCasted { who: VoterId, when: BlockNumberFor, project_id: ProjectId }, + VoteCasted { who: VoterId, when: ProvidedBlockNumberFor, project_id: ProjectId }, /// User's vote successfully removed - VoteRemoved { who: VoterId, when: BlockNumberFor, project_id: ProjectId }, + VoteRemoved { who: VoterId, when: ProvidedBlockNumberFor, project_id: ProjectId }, + + /// Project added to whitelisted projects list + Projectlisted { when:ProvidedBlockNumberFor, project_id: ProjectId }, /// Project removed from whitelisted projects list - ProjectUnlisted { when: BlockNumberFor, project_id: ProjectId }, + ProjectUnlisted { when:ProvidedBlockNumberFor, project_id: ProjectId }, /// Project Funding Accepted by voters ProjectFundingAccepted { project_id: ProjectId, - when: BlockNumberFor, + when:ProvidedBlockNumberFor, round_number: u32, amount: BalanceOf, }, /// Project Funding rejected by voters - ProjectFundingRejected { when: BlockNumberFor, project_id: ProjectId }, + ProjectFundingRejected { when:ProvidedBlockNumberFor, project_id: ProjectId }, /// A new voting round started - VotingRoundStarted { when: BlockNumberFor, round_number: u32 }, + VotingRoundStarted { when:ProvidedBlockNumberFor, round_number: u32 }, /// The users voting period ended. Reward calculation will start. - VoteActionLocked { when: BlockNumberFor, round_number: u32 }, + VoteActionLocked { when:ProvidedBlockNumberFor, round_number: u32 }, /// The voting round ended - VotingRoundEnded { when: BlockNumberFor, round_number: u32 }, + VotingRoundEnded { when:ProvidedBlockNumberFor, round_number: u32 }, } #[pallet::error] @@ -168,7 +187,7 @@ pub mod pallet { /*#[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(n: BlockNumberFor) -> Weight { + fn on_initialize(n:ProvidedBlockNumberFor) -> Weight { Self::begin_block(n) } }*/ @@ -178,7 +197,14 @@ pub mod pallet { #[pallet::call_index(0)] #[transactional] - pub fn register_project(origin: OriginFor) -> DispatchResult{ + pub fn register_project(origin: OriginFor, project_id: ProjectId, amount: BalanceOf) -> DispatchResult{ + let _caller = ensure_signed(origin)?; + let when = T::BlockNumberProvider::current_block_number(); + Self::register_new(project_id.clone(), amount)?; + Self::deposit_event(Event::Projectlisted { + when, + project_id, + }); Ok(()) } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 2594d902861d..dc3b929d87a6 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -57,6 +57,7 @@ pub type PalletsOriginOf = pub const DISTRIBUTION_ID: LockIdentifier = *b"distribu"; pub type RoundIndex = u32; pub type VoterId = AccountIdOf; +pub type ProvidedBlockNumberFor = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; /// The state of the payment claim. #[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo, Default)] @@ -77,13 +78,13 @@ pub struct SpendInfo { /// The asset amount of the spend. pub amount: BalanceOf, /// The block number from which the spend can be claimed(24h after SpendStatus Creation). - pub valid_from: BlockNumberFor, + pub valid_from:ProvidedBlockNumberFor, /// Corresponding project id pub whitelisted_project: AccountIdOf, /// Has it been claimed? pub claimed: bool, /// Claim Expiration block - pub expire: BlockNumberFor, + pub expire:ProvidedBlockNumberFor, } impl SpendInfo { @@ -92,7 +93,7 @@ impl SpendInfo { let whitelisted_project = whitelisted.project_id.clone(); let claimed = false; let valid_from = - >::block_number(); + T::BlockNumberProvider::current_block_number(); let expire = valid_from.clone().saturating_add(T::ClaimingPeriod::get()); let spend = SpendInfo { amount, valid_from, whitelisted_project, claimed, expire }; @@ -111,7 +112,7 @@ pub struct ProjectInfo { pub project_id: ProjectId, /// Block at which the project was submitted for reward distribution - pub submission_block: BlockNumberFor, + pub submission_block:ProvidedBlockNumberFor, /// Amount to be lock & pay for this project pub amount: BalanceOf, @@ -131,7 +132,7 @@ pub struct VoteInfo { pub conviction: Conviction, - pub funds_unlock_block: BlockNumberFor, + pub funds_unlock_block:ProvidedBlockNumberFor, } // If no conviction, user's funds are released at the end of the voting round @@ -163,8 +164,8 @@ impl Default for VoteInfo { #[scale_info(skip_type_params(T))] pub struct VotingRoundInfo { pub round_number: u32, - pub round_starting_block: BlockNumberFor, - pub round_ending_block: BlockNumberFor, + pub round_starting_block:ProvidedBlockNumberFor, + pub round_ending_block:ProvidedBlockNumberFor, pub total_positive_votes_amount: BalanceOf, pub total_negative_votes_amount: BalanceOf, } From 54701b7da09e71d6e97165d035f2003d19b66e22 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 12:43:03 +0900 Subject: [PATCH 05/62] Added some helpers and extrinsic_#1 --- substrate/frame/opf/src/functions.rs | 122 +++++++++++++++++++++++++-- substrate/frame/opf/src/lib.rs | 34 +++++++- substrate/frame/opf/src/types.rs | 4 +- 3 files changed, 150 insertions(+), 10 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 542d73242bd1..1c409e224daf 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -20,13 +20,125 @@ pub use super::*; impl Pallet { + pub fn pot_account() -> AccountIdOf { + // Get Pot account + T::PotId::get().into_account_truncating() + } + + /// Funds transfer from the Pot to a project account + pub fn spend(amount: BalanceOf, beneficiary: AccountIdOf) -> DispatchResult { + // Get Pot account + let pot_account: AccountIdOf = Self::pot_account(); + + //Operate the transfer + T::NativeBalance::transfer(&pot_account, &beneficiary, amount, Preservation::Preserve)?; + + Ok(()) + } + + /// Series of checks on the Pot, to ensure that we have enough funds + /// before executing a Spend --> used in tests. + pub fn pot_check(spend: BalanceOf) -> DispatchResult { + // Get Pot account + let pot_account = Self::pot_account(); + + // Check that the Pot as enough funds for the transfer + let balance = T::NativeBalance::balance(&pot_account); + let minimum_balance = T::NativeBalance::minimum_balance(); + let remaining_balance = balance.saturating_sub(spend); + + ensure!(remaining_balance > minimum_balance, Error::::InsufficientPotReserves); + ensure!(balance > spend, Error::::InsufficientPotReserves); + Ok(()) + } + // Helper function for project registration - pub fn register_new(project_id: ProjectId, amount: BalanceOf) -> DispatchResult{ + pub fn register_new(project_id: ProjectId) -> DispatchResult{ let submission_block = T::BlockNumberProvider::current_block_number(); - let project_infos: ProjectInfo = ProjectInfo { project_id, submission_block, amount}; - let mut bounded = Projects::get(); - let _ = bounded.try_push(project_infos); - Projects::put(bounded); + let mut bounded: BoundedVec, T::MaxProjects> = WhiteListedProjectAccounts::::get(); + let _ = bounded.try_push(project_id); + WhiteListedProjectAccounts::::put(bounded); Ok(()) } + + // Voting Period checks + pub fn period_check() -> DispatchResult { + // Get current voting round & check if we are in voting period or not + let current_round_index = VotingRoundNumber::::get().saturating_sub(1); + let round = VotingRounds::::get(current_round_index).ok_or(Error::::NoRoundFound)?; + let now = T::BlockNumberProvider::current_block_number(); + ensure!(now < round.round_ending_block, Error::::VotingRoundOver); + Ok(()) + } + + pub fn unlist_project(project_id: ProjectId) -> DispatchResult { + WhiteListedProjectAccounts::::mutate(|value| { + let mut val = value.clone(); + val.retain(|x| *x != project_id); + *value = val; + }); + + Ok(()) + } + + // The total reward to be distributed is a portion or inflation, determined in another pallet + // Reward calculation is executed within the Voting period + pub fn calculate_rewards(total_reward: BalanceOf) -> DispatchResult { + let projects = WhiteListedProjectAccounts::::get(); + let round_number = VotingRoundNumber::::get().saturating_sub(1); + let round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; + if projects.clone().len() > 0 as usize { + let total_positive_votes_amount = round.total_positive_votes_amount; + let total_negative_votes_amount = round.total_negative_votes_amount; + + let total_votes_amount = + total_positive_votes_amount.saturating_sub(total_negative_votes_amount); + + // for each project, calculate the percentage of votes, the amount to be distributed, + // and then populate the storage Projects + for project in projects { + if ProjectFunds::::contains_key(&project) { + let funds = ProjectFunds::::get(&project); + let project_positive_reward = funds[0]; + let project_negative_reward = funds[1]; + + let project_reward = + project_positive_reward.saturating_sub(project_negative_reward); + + let project_percentage = + Percent::from_rational(project_reward, total_votes_amount); + let final_amount = project_percentage * total_reward; + + // Send calculated reward for reward distribution + let now = T::BlockNumberProvider::current_block_number(); + let project_info = ProjectInfo { + project_id: project.clone(), + submission_block: now, + amount: final_amount, + }; + + let mut rewarded = Projects::::get(); + rewarded + .try_push(project_info.clone()) + .map_err(|_| Error::::MaximumProjectsNumber)?; + + Projects::::mutate(|value| { + *value = rewarded; + }); + + let when = T::BlockNumberProvider::current_block_number(); + Self::deposit_event(Event::::ProjectFundingAccepted { + project_id: project, + when, + round_number, + amount: project_info.amount, + }) + + } + } + } + + Ok(()) + } + } \ No newline at end of file diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 96e3e4014402..80259c92923a 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -111,6 +111,21 @@ pub mod pallet { #[pallet::storage] pub type Projects = StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + + /// List of Whitelisted Project registered + #[pallet::storage] + pub type WhiteListedProjectAccounts = + StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + + /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts + #[pallet::storage] + pub type ProjectFunds = StorageMap< + _, + Twox64Concat, + ProjectId, + BoundedVec, ConstU32<2>>, + ValueQuery, + >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -183,6 +198,14 @@ pub mod pallet { FundsReserveFailed, /// An invalid result was returned InvalidResult, + /// The reward calculation failed due to an internal error + FailedRewardCalculation, + /// Voting round is over + VotingRoundOver, + /// This voting round does not exists + NoRoundFound, + /// Maximum number of projects submission for reward distribution as been reached + MaximumProjectsNumber, } /*#[pallet::hooks] @@ -197,10 +220,10 @@ pub mod pallet { #[pallet::call_index(0)] #[transactional] - pub fn register_project(origin: OriginFor, project_id: ProjectId, amount: BalanceOf) -> DispatchResult{ + pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult{ let _caller = ensure_signed(origin)?; let when = T::BlockNumberProvider::current_block_number(); - Self::register_new(project_id.clone(), amount)?; + Self::register_new(project_id.clone())?; Self::deposit_event(Event::Projectlisted { when, project_id, @@ -210,7 +233,12 @@ pub mod pallet { #[pallet::call_index(1)] #[transactional] - pub fn unregister_project(origin: OriginFor) -> DispatchResult { + pub fn unregister_project(origin: OriginFor, project_id: ProjectId ) -> DispatchResult { + ensure_root(origin)?; + let when = T::BlockNumberProvider::current_block_number(); + Self::unlist_project(project_id.clone())?; + Self::deposit_event(Event::::ProjectUnlisted { when, project_id }); + Ok(()) } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index dc3b929d87a6..0a93d3594ed5 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -38,9 +38,9 @@ pub use frame_support::{ pub use pallet_conviction_voting::Conviction; pub use frame_system::{pallet_prelude::*, RawOrigin}; pub use scale_info::prelude::vec::Vec; -pub use sp_runtime::traits::{ +pub use sp_runtime::{traits::{ AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, Zero, -}; +}, Percent}; pub use sp_std::boxed::Box; pub type BalanceOf = <::NativeBalance as fungible::Inspect< From e09ff1178afb4347f658d240b0061d5c72204063 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 17:57:06 +0900 Subject: [PATCH 06/62] Remove unneedded storage --- substrate/bin/node/runtime/src/lib.rs | 7 ++- substrate/frame/opf/src/functions.rs | 75 ++++++++++++++++++++++----- substrate/frame/opf/src/lib.rs | 8 ++- substrate/frame/opf/src/types.rs | 6 +-- 4 files changed, 74 insertions(+), 22 deletions(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index b6d61636b4ef..f05048fa9a32 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1596,13 +1596,15 @@ parameter_types! { pub const ClaimingPeriod: BlockNumber = 7 * DAYS; pub const VoteValidityPeriod: BlockNumber = 7 * DAYS; pub const MaxProjects:u32 = 50; + + /// This should be calculated as a percentage of inflation. + pub const TemporaryRewards: Balance = 100000 * DOLLARS; } impl pallet_opf::Config for Runtime { type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; - type NativeBalance = Balances; /// Pot PalletId @@ -1626,6 +1628,9 @@ impl pallet_opf::Config for Runtime { type BlockNumberProvider = System; + /// This should be calculated as a percentage of inflation. + type TemporaryRewards = TemporaryRewards; + type Preimages = Preimage; type Scheduler = Scheduler; diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 1c409e224daf..4ae2b4a899ba 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -90,7 +90,7 @@ impl Pallet { if projects.clone().len() > 0 as usize { let total_positive_votes_amount = round.total_positive_votes_amount; let total_negative_votes_amount = round.total_negative_votes_amount; - + let when = T::BlockNumberProvider::current_block_number(); let total_votes_amount = total_positive_votes_amount.saturating_sub(total_negative_votes_amount); @@ -102,7 +102,8 @@ impl Pallet { let project_positive_reward = funds[0]; let project_negative_reward = funds[1]; - let project_reward = + if project_positive_reward > project_negative_reward{ + let project_reward = project_positive_reward.saturating_sub(project_negative_reward); let project_percentage = @@ -110,29 +111,28 @@ impl Pallet { let final_amount = project_percentage * total_reward; // Send calculated reward for reward distribution - let now = T::BlockNumberProvider::current_block_number(); let project_info = ProjectInfo { project_id: project.clone(), - submission_block: now, + submission_block: when, amount: final_amount, }; - let mut rewarded = Projects::::get(); - rewarded - .try_push(project_info.clone()) - .map_err(|_| Error::::MaximumProjectsNumber)?; - - Projects::::mutate(|value| { - *value = rewarded; - }); + // create a spend for project to be rewarded + let new_spend = SpendInfo::::new(&project_info); - let when = T::BlockNumberProvider::current_block_number(); + Self::deposit_event(Event::::ProjectFundingAccepted { project_id: project, when, round_number, amount: project_info.amount, }) + } else{ + Self::deposit_event(Event::::ProjectFundingRejected { + when, + project_id: project, + }) + } } } @@ -141,4 +141,53 @@ impl Pallet { Ok(()) } + // To be executed in a hook, on_initialize + pub fn on_idle_function(now: ProvidedBlockNumberFor, limit: Weight) -> Weight { + let mut meter = WeightMeter::with_limit(limit); + let max_block_weight = T::DbWeight::get().reads_writes(14, 8); + + if meter.try_consume(max_block_weight).is_err() { + return meter.consumed(); + } + let mut round_index = VotingRoundNumber::::get(); + + // No active round? + if round_index == 0 { + // Start the first voting round + let _round0 = VotingRoundInfo::::new(); + round_index = VotingRoundNumber::::get(); + } + + let current_round_index = round_index.saturating_sub(1); + + let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); + let round_ending_block = round_infos.round_ending_block; + + // Conditions for reward distribution preparations are: + // - We are at the end of voting_round period + + if now == round_ending_block { + + // prepare reward distribution + // for now we are using the temporary-constant reward. + let _ = Self::calculate_rewards(T::TemporaryRewards::get()) + .map_err(|_| Error::::FailedRewardCalculation); + + // Create a new round. + let _new_round = VotingRoundInfo::::new(); + // Clear WhiteListedProjectAccounts storage + WhiteListedProjectAccounts::::kill(); + // Clear ProjectFunds storage + ProjectFunds::::drain(); + // Emmit events + Self::deposit_event(Event::::VotingRoundEnded { + when: now, + round_number: round_infos.round_number, + }); + } + + meter.consumed() + } + + } \ No newline at end of file diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 80259c92923a..089443341608 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -88,6 +88,9 @@ pub mod pallet { #[pallet::constant] type VoteValidityPeriod: Get>; + /// Used for Pallet testing only. Represents the Total Reward distributed + type TemporaryRewards: Get>; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -106,11 +109,6 @@ pub mod pallet { #[pallet::storage] pub(super) type Spends = CountedStorageMap<_, Twox64Concat, ProjectId, SpendInfo, OptionQuery>; - - /// List of whitelisted projects to be rewarded - #[pallet::storage] - pub type Projects = - StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; /// List of Whitelisted Project registered #[pallet::storage] diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 0a93d3594ed5..e6b7a934cb36 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -33,7 +33,7 @@ pub use frame_support::{ Bounded, DefensiveOption, EnsureOrigin, LockIdentifier, OriginTrait, QueryPreimage, StorePreimage, }, - transactional, PalletId, Serialize, + transactional, PalletId, Serialize, weights::WeightMeter, }; pub use pallet_conviction_voting::Conviction; pub use frame_system::{pallet_prelude::*, RawOrigin}; @@ -80,7 +80,7 @@ pub struct SpendInfo { /// The block number from which the spend can be claimed(24h after SpendStatus Creation). pub valid_from:ProvidedBlockNumberFor, /// Corresponding project id - pub whitelisted_project: AccountIdOf, + pub whitelisted_project: ProjectInfo, /// Has it been claimed? pub claimed: bool, /// Claim Expiration block @@ -90,7 +90,7 @@ pub struct SpendInfo { impl SpendInfo { pub fn new(whitelisted: &ProjectInfo) -> Self { let amount = whitelisted.amount; - let whitelisted_project = whitelisted.project_id.clone(); + let whitelisted_project = whitelisted.clone(); let claimed = false; let valid_from = T::BlockNumberProvider::current_block_number(); From a52879a2619800fe81bab49a1c779ff1d02cf519 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 18:15:10 +0900 Subject: [PATCH 07/62] prdoc --- prdoc/pr_6994.prdoc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 prdoc/pr_6994.prdoc diff --git a/prdoc/pr_6994.prdoc b/prdoc/pr_6994.prdoc new file mode 100644 index 000000000000..32805f2c145e --- /dev/null +++ b/prdoc/pr_6994.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Optimistic Project Funding" + +doc: + - audience: Runtime Dev + description: | + the PF pallet handles the Optimistic Project Funding. + It allows users to nominate projects (whitelisted in OpenGov) with their DOT. + This mechanism will be funded with a constant stream of DOT taken directly from inflation and distributed to projects based on the proportion of DOT that has nominated them. + +crates: + - name: pallet-opf + bump: patch + - name: frame-support + bump: none + - name: polkadot-sdk + bump: patch + - name: polkadot + bump: none \ No newline at end of file From e023fefabb75db98f5998744e91635f1c6d94fc9 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 18:19:51 +0900 Subject: [PATCH 08/62] cargo fmt --- substrate/frame/opf/src/functions.rs | 62 ++++++++++++------------- substrate/frame/opf/src/lib.rs | 68 +++++++++++++++------------- substrate/frame/opf/src/types.rs | 42 +++++++++-------- 3 files changed, 88 insertions(+), 84 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 4ae2b4a899ba..c2b046b2224e 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -16,11 +16,9 @@ // limitations under the License. //! Helper functions for OPF pallet. - pub use super::*; impl Pallet { - - pub fn pot_account() -> AccountIdOf { + pub fn pot_account() -> AccountIdOf { // Get Pot account T::PotId::get().into_account_truncating() } @@ -36,7 +34,7 @@ impl Pallet { Ok(()) } - /// Series of checks on the Pot, to ensure that we have enough funds + /// Series of checks on the Pot, to ensure that we have enough funds /// before executing a Spend --> used in tests. pub fn pot_check(spend: BalanceOf) -> DispatchResult { // Get Pot account @@ -52,16 +50,17 @@ impl Pallet { Ok(()) } - // Helper function for project registration - pub fn register_new(project_id: ProjectId) -> DispatchResult{ - let submission_block = T::BlockNumberProvider::current_block_number(); - let mut bounded: BoundedVec, T::MaxProjects> = WhiteListedProjectAccounts::::get(); - let _ = bounded.try_push(project_id); - WhiteListedProjectAccounts::::put(bounded); - Ok(()) - } + // Helper function for project registration + pub fn register_new(project_id: ProjectId) -> DispatchResult { + let submission_block = T::BlockNumberProvider::current_block_number(); + let mut bounded: BoundedVec, T::MaxProjects> = + WhiteListedProjectAccounts::::get(); + let _ = bounded.try_push(project_id); + WhiteListedProjectAccounts::::put(bounded); + Ok(()) + } - // Voting Period checks + // Voting Period checks pub fn period_check() -> DispatchResult { // Get current voting round & check if we are in voting period or not let current_round_index = VotingRoundNumber::::get().saturating_sub(1); @@ -71,18 +70,18 @@ impl Pallet { Ok(()) } - pub fn unlist_project(project_id: ProjectId) -> DispatchResult { + pub fn unlist_project(project_id: ProjectId) -> DispatchResult { WhiteListedProjectAccounts::::mutate(|value| { let mut val = value.clone(); val.retain(|x| *x != project_id); *value = val; }); - + Ok(()) } - // The total reward to be distributed is a portion or inflation, determined in another pallet - // Reward calculation is executed within the Voting period + // The total reward to be distributed is a portion or inflation, determined in another pallet + // Reward calculation is executed within the Voting period pub fn calculate_rewards(total_reward: BalanceOf) -> DispatchResult { let projects = WhiteListedProjectAccounts::::get(); let round_number = VotingRoundNumber::::get().saturating_sub(1); @@ -90,7 +89,7 @@ impl Pallet { if projects.clone().len() > 0 as usize { let total_positive_votes_amount = round.total_positive_votes_amount; let total_negative_votes_amount = round.total_negative_votes_amount; - let when = T::BlockNumberProvider::current_block_number(); + let when = T::BlockNumberProvider::current_block_number(); let total_votes_amount = total_positive_votes_amount.saturating_sub(total_negative_votes_amount); @@ -102,9 +101,9 @@ impl Pallet { let project_positive_reward = funds[0]; let project_negative_reward = funds[1]; - if project_positive_reward > project_negative_reward{ - let project_reward = - project_positive_reward.saturating_sub(project_negative_reward); + if project_positive_reward > project_negative_reward { + let project_reward = + project_positive_reward.saturating_sub(project_negative_reward); let project_percentage = Percent::from_rational(project_reward, total_votes_amount); @@ -117,23 +116,21 @@ impl Pallet { amount: final_amount, }; - // create a spend for project to be rewarded + // create a spend for project to be rewarded let new_spend = SpendInfo::::new(&project_info); - Self::deposit_event(Event::::ProjectFundingAccepted { project_id: project, when, round_number, amount: project_info.amount, }) - } else{ + } else { Self::deposit_event(Event::::ProjectFundingRejected { when, - project_id: project, + project_id: project, }) - } - + } } } } @@ -141,7 +138,7 @@ impl Pallet { Ok(()) } - // To be executed in a hook, on_initialize + // To be executed in a hook, on_initialize pub fn on_idle_function(now: ProvidedBlockNumberFor, limit: Weight) -> Weight { let mut meter = WeightMeter::with_limit(limit); let max_block_weight = T::DbWeight::get().reads_writes(14, 8); @@ -158,6 +155,8 @@ impl Pallet { round_index = VotingRoundNumber::::get(); } + // ToDo: Check Spends and remove those which are not valid anymore + let current_round_index = round_index.saturating_sub(1); let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); @@ -167,13 +166,12 @@ impl Pallet { // - We are at the end of voting_round period if now == round_ending_block { - // prepare reward distribution // for now we are using the temporary-constant reward. let _ = Self::calculate_rewards(T::TemporaryRewards::get()) .map_err(|_| Error::::FailedRewardCalculation); - // Create a new round. + // Create a new round. let _new_round = VotingRoundInfo::::new(); // Clear WhiteListedProjectAccounts storage WhiteListedProjectAccounts::::kill(); @@ -188,6 +186,4 @@ impl Pallet { meter.consumed() } - - -} \ No newline at end of file +} diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 089443341608..f5edb25ff0af 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -18,8 +18,8 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; -mod types; mod functions; +mod types; pub use pallet_scheduler as Schedule; pub use types::*; @@ -42,8 +42,7 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Type to access the Balances Pallet. - type NativeBalance: fungible::Inspect - + fungible::Mutate; + type NativeBalance: fungible::Inspect + fungible::Mutate; /// Provider for the block number. type BlockNumberProvider: BlockNumberProvider; @@ -57,20 +56,20 @@ pub mod pallet { /// The Scheduler. type Scheduler: ScheduleAnon< - ProvidedBlockNumberFor, + ProvidedBlockNumberFor, CallOf, PalletsOriginOf, Hasher = Self::Hashing, > + ScheduleNamed< - ProvidedBlockNumberFor, + ProvidedBlockNumberFor, CallOf, PalletsOriginOf, Hasher = Self::Hashing, >; - /// Time period in which people can vote. - /// After the period has ended, the votes are counted (STOP THE COUNT) - /// and then the funds are distributed into Spends. + /// Time period in which people can vote. + /// After the period has ended, the votes are counted (STOP THE COUNT) + /// and then the funds are distributed into Spends. #[pallet::constant] type VotingPeriod: Get>; @@ -78,9 +77,9 @@ pub mod pallet { #[pallet::constant] type MaxProjects: Get; - /// Time for claiming a Spend. - /// After the period has passed, a spend is thrown away - /// and the funds are available again for distribution in the pot. + /// Time for claiming a Spend. + /// After the period has passed, a spend is thrown away + /// and the funds are available again for distribution in the pot. #[pallet::constant] type ClaimingPeriod: Get>; @@ -91,7 +90,6 @@ pub mod pallet { /// Used for Pallet testing only. Represents the Total Reward distributed type TemporaryRewards: Get>; - /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -109,12 +107,12 @@ pub mod pallet { #[pallet::storage] pub(super) type Spends = CountedStorageMap<_, Twox64Concat, ProjectId, SpendInfo, OptionQuery>; - + /// List of Whitelisted Project registered #[pallet::storage] pub type WhiteListedProjectAccounts = StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; - + /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts #[pallet::storage] pub type ProjectFunds = StorageMap< @@ -129,10 +127,18 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Reward successfully claimed - RewardClaimed { when: ProvidedBlockNumberFor, amount: BalanceOf, project_id: ProjectId }, + RewardClaimed { + when: ProvidedBlockNumberFor, + amount: BalanceOf, + project_id: ProjectId, + }, /// A Spend was created - SpendCreated { when: ProvidedBlockNumberFor, amount: BalanceOf, project_id: ProjectId }, + SpendCreated { + when: ProvidedBlockNumberFor, + amount: BalanceOf, + project_id: ProjectId, + }, /// Not yet in the claiming period NotClaimingPeriod { project_id: ProjectId, claiming_period: ProvidedBlockNumberFor }, @@ -150,30 +156,30 @@ pub mod pallet { VoteRemoved { who: VoterId, when: ProvidedBlockNumberFor, project_id: ProjectId }, /// Project added to whitelisted projects list - Projectlisted { when:ProvidedBlockNumberFor, project_id: ProjectId }, + Projectlisted { when: ProvidedBlockNumberFor, project_id: ProjectId }, /// Project removed from whitelisted projects list - ProjectUnlisted { when:ProvidedBlockNumberFor, project_id: ProjectId }, + ProjectUnlisted { when: ProvidedBlockNumberFor, project_id: ProjectId }, /// Project Funding Accepted by voters ProjectFundingAccepted { project_id: ProjectId, - when:ProvidedBlockNumberFor, + when: ProvidedBlockNumberFor, round_number: u32, amount: BalanceOf, }, /// Project Funding rejected by voters - ProjectFundingRejected { when:ProvidedBlockNumberFor, project_id: ProjectId }, + ProjectFundingRejected { when: ProvidedBlockNumberFor, project_id: ProjectId }, /// A new voting round started - VotingRoundStarted { when:ProvidedBlockNumberFor, round_number: u32 }, + VotingRoundStarted { when: ProvidedBlockNumberFor, round_number: u32 }, /// The users voting period ended. Reward calculation will start. - VoteActionLocked { when:ProvidedBlockNumberFor, round_number: u32 }, + VoteActionLocked { when: ProvidedBlockNumberFor, round_number: u32 }, /// The voting round ended - VotingRoundEnded { when:ProvidedBlockNumberFor, round_number: u32 }, + VotingRoundEnded { when: ProvidedBlockNumberFor, round_number: u32 }, } #[pallet::error] @@ -215,23 +221,22 @@ pub mod pallet { #[pallet::call] impl Pallet { - #[pallet::call_index(0)] #[transactional] - pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult{ + pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult { let _caller = ensure_signed(origin)?; let when = T::BlockNumberProvider::current_block_number(); Self::register_new(project_id.clone())?; - Self::deposit_event(Event::Projectlisted { - when, - project_id, - }); + Self::deposit_event(Event::Projectlisted { when, project_id }); Ok(()) } #[pallet::call_index(1)] #[transactional] - pub fn unregister_project(origin: OriginFor, project_id: ProjectId ) -> DispatchResult { + pub fn unregister_project( + origin: OriginFor, + project_id: ProjectId, + ) -> DispatchResult { ensure_root(origin)?; let when = T::BlockNumberProvider::current_block_number(); Self::unlist_project(project_id.clone())?; @@ -263,6 +268,5 @@ pub mod pallet { pub fn execute_claim(origin: OriginFor, project_id: ProjectId) -> DispatchResult { Ok(()) } - } -} \ No newline at end of file +} diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index e6b7a934cb36..4e0c518c8b83 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -33,14 +33,20 @@ pub use frame_support::{ Bounded, DefensiveOption, EnsureOrigin, LockIdentifier, OriginTrait, QueryPreimage, StorePreimage, }, - transactional, PalletId, Serialize, weights::WeightMeter, + transactional, + weights::WeightMeter, + PalletId, Serialize, }; -pub use pallet_conviction_voting::Conviction; pub use frame_system::{pallet_prelude::*, RawOrigin}; +pub use pallet_conviction_voting::Conviction; pub use scale_info::prelude::vec::Vec; -pub use sp_runtime::{traits::{ - AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, Zero, -}, Percent}; +pub use sp_runtime::{ + traits::{ + AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, + Zero, + }, + Percent, +}; pub use sp_std::boxed::Box; pub type BalanceOf = <::NativeBalance as fungible::Inspect< @@ -57,7 +63,8 @@ pub type PalletsOriginOf = pub const DISTRIBUTION_ID: LockIdentifier = *b"distribu"; pub type RoundIndex = u32; pub type VoterId = AccountIdOf; -pub type ProvidedBlockNumberFor = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; +pub type ProvidedBlockNumberFor = + <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; /// The state of the payment claim. #[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo, Default)] @@ -78,13 +85,13 @@ pub struct SpendInfo { /// The asset amount of the spend. pub amount: BalanceOf, /// The block number from which the spend can be claimed(24h after SpendStatus Creation). - pub valid_from:ProvidedBlockNumberFor, + pub valid_from: ProvidedBlockNumberFor, /// Corresponding project id pub whitelisted_project: ProjectInfo, /// Has it been claimed? pub claimed: bool, /// Claim Expiration block - pub expire:ProvidedBlockNumberFor, + pub expire: ProvidedBlockNumberFor, } impl SpendInfo { @@ -92,9 +99,8 @@ impl SpendInfo { let amount = whitelisted.amount; let whitelisted_project = whitelisted.clone(); let claimed = false; - let valid_from = - T::BlockNumberProvider::current_block_number(); - let expire = valid_from.clone().saturating_add(T::ClaimingPeriod::get()); + let valid_from = T::BlockNumberProvider::current_block_number(); + let expire = valid_from.saturating_add(T::ClaimingPeriod::get()); let spend = SpendInfo { amount, valid_from, whitelisted_project, claimed, expire }; @@ -112,7 +118,7 @@ pub struct ProjectInfo { pub project_id: ProjectId, /// Block at which the project was submitted for reward distribution - pub submission_block:ProvidedBlockNumberFor, + pub submission_block: ProvidedBlockNumberFor, /// Amount to be lock & pay for this project pub amount: BalanceOf, @@ -132,16 +138,14 @@ pub struct VoteInfo { pub conviction: Conviction, - pub funds_unlock_block:ProvidedBlockNumberFor, + pub funds_unlock_block: ProvidedBlockNumberFor, } // If no conviction, user's funds are released at the end of the voting round impl VoteInfo { pub fn funds_unlock(&mut self) { let conviction_coeff = >::from(self.conviction); - let funds_unlock_block = self - .round - .round_ending_block; + let funds_unlock_block = self.round.round_ending_block; self.funds_unlock_block = funds_unlock_block; } } @@ -164,8 +168,8 @@ impl Default for VoteInfo { #[scale_info(skip_type_params(T))] pub struct VotingRoundInfo { pub round_number: u32, - pub round_starting_block:ProvidedBlockNumberFor, - pub round_ending_block:ProvidedBlockNumberFor, + pub round_starting_block: ProvidedBlockNumberFor, + pub round_ending_block: ProvidedBlockNumberFor, pub total_positive_votes_amount: BalanceOf, pub total_negative_votes_amount: BalanceOf, } @@ -198,4 +202,4 @@ impl VotingRoundInfo { VotingRounds::::insert(round_number, round_infos.clone()); round_infos } -} \ No newline at end of file +} From c5104d7b2f30a863bc5fa36e49d680a438455272 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 20:14:42 +0900 Subject: [PATCH 09/62] Claim_reward conditions --- substrate/frame/opf/src/functions.rs | 5 +-- substrate/frame/opf/src/lib.rs | 54 +++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index c2b046b2224e..1abff4fc4410 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -52,7 +52,6 @@ impl Pallet { // Helper function for project registration pub fn register_new(project_id: ProjectId) -> DispatchResult { - let submission_block = T::BlockNumberProvider::current_block_number(); let mut bounded: BoundedVec, T::MaxProjects> = WhiteListedProjectAccounts::::get(); let _ = bounded.try_push(project_id); @@ -117,7 +116,7 @@ impl Pallet { }; // create a spend for project to be rewarded - let new_spend = SpendInfo::::new(&project_info); + let _ = SpendInfo::::new(&project_info); Self::deposit_event(Event::::ProjectFundingAccepted { project_id: project, @@ -155,8 +154,6 @@ impl Pallet { round_index = VotingRoundNumber::::get(); } - // ToDo: Check Spends and remove those which are not valid anymore - let current_round_index = round_index.saturating_sub(1); let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index f5edb25ff0af..14d0dd875989 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -169,6 +169,9 @@ pub mod pallet { amount: BalanceOf, }, + /// Reward claim has expired + ExpiredClaim { expired_when: ProvidedBlockNumberFor, project_id: ProjectId }, + /// Project Funding rejected by voters ProjectFundingRejected { when: ProvidedBlockNumberFor, project_id: ProjectId }, @@ -257,16 +260,57 @@ pub mod pallet { Ok(()) } + /// OPF Reward Claim logic + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// From this extrinsic any user can claim a reward for a nominated/whitelisted project. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// + /// ### Errors + /// - [`Error::::InexistentSpend`]:Spend or Spend index does not exists + /// - [`Error::::NoValidAccount`]: No valid Account_id found + /// - [`Error::::NotClaimingPeriod`]: Still not in claiming period + /// + /// ## Events + /// Emits [`Event::::RewardClaimed`] if successful for a positive approval. #[pallet::call_index(4)] #[transactional] pub fn claim_reward_for(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let _caller = ensure_signed(origin)?; + let now = T::BlockNumberProvider::current_block_number(); + let info = Spends::::get(&project_id).ok_or(Error::::InexistentSpend)?; + match now { + _ if now >= info.expire => { + Spends::::remove(&project_id); + Self::deposit_event(Event::ExpiredClaim { + expired_when: info.expire, + project_id, + }); + Ok(()) + }, + _ if now >= info.expire => { + // transfer the funds + Self::spend(info.amount, project_id.clone())?; + + Self::deposit_event(Event::RewardClaimed { + when: now, + amount: info.amount, + project_id, + }); Ok(()) - } + } + _ => Err(DispatchError::Other("Not Claiming Period")) + } - #[pallet::call_index(5)] - #[transactional] - pub fn execute_claim(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - Ok(()) } + + } } From f440a8ee78f0024bedb79a68e1a326120c1fce83 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 30 Dec 2024 20:16:47 +0900 Subject: [PATCH 10/62] cargo fmt --- substrate/frame/opf/src/lib.rs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 14d0dd875989..147259bd9135 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -289,28 +289,25 @@ pub mod pallet { match now { _ if now >= info.expire => { Spends::::remove(&project_id); - Self::deposit_event(Event::ExpiredClaim { - expired_when: info.expire, - project_id, - }); - Ok(()) + Self::deposit_event(Event::ExpiredClaim { + expired_when: info.expire, + project_id, + }); + Ok(()) }, _ if now >= info.expire => { // transfer the funds - Self::spend(info.amount, project_id.clone())?; - - Self::deposit_event(Event::RewardClaimed { - when: now, - amount: info.amount, - project_id, - }); - Ok(()) - } - _ => Err(DispatchError::Other("Not Claiming Period")) + Self::spend(info.amount, project_id.clone())?; + + Self::deposit_event(Event::RewardClaimed { + when: now, + amount: info.amount, + project_id, + }); + Ok(()) + }, + _ => Err(DispatchError::Other("Not Claiming Period")), } - } - - } } From 27f910771fad245548b945f435eacfa996b7c25e Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 31 Dec 2024 10:43:29 +0900 Subject: [PATCH 11/62] Hook on_idle added --- substrate/frame/opf/src/functions.rs | 3 ++- substrate/frame/opf/src/lib.rs | 10 +++++----- substrate/frame/opf/src/types.rs | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 1abff4fc4410..0d11d9c124a8 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -138,7 +138,8 @@ impl Pallet { } // To be executed in a hook, on_initialize - pub fn on_idle_function(now: ProvidedBlockNumberFor, limit: Weight) -> Weight { + pub fn on_idle_function(limit: Weight) -> Weight { + let now = T::BlockNumberProvider::current_block_number(); let mut meter = WeightMeter::with_limit(limit); let max_block_weight = T::DbWeight::get().reads_writes(14, 8); diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 147259bd9135..c835162c7ab9 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -215,12 +215,12 @@ pub mod pallet { MaximumProjectsNumber, } - /*#[pallet::hooks] - impl Hooks> for Pallet { - fn on_initialize(n:ProvidedBlockNumberFor) -> Weight { - Self::begin_block(n) + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_idle(_n: SystemBlockNumberFor, remaining_weight: Weight) -> Weight { + Self::on_idle_function(remaining_weight) } - }*/ + } #[pallet::call] impl Pallet { diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 4e0c518c8b83..99ea557ed0ac 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -65,6 +65,7 @@ pub type RoundIndex = u32; pub type VoterId = AccountIdOf; pub type ProvidedBlockNumberFor = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; +pub use frame_system::pallet_prelude::BlockNumberFor as SystemBlockNumberFor; /// The state of the payment claim. #[derive(Encode, Decode, Clone, PartialEq, Eq, MaxEncodedLen, RuntimeDebug, TypeInfo, Default)] From 57f751dd4b6e3c55d24e642932fdbfb1a909feeb Mon Sep 17 00:00:00 2001 From: ndkazu Date: Wed, 1 Jan 2025 12:39:14 +0900 Subject: [PATCH 12/62] Adding some errors --- substrate/frame/opf/src/functions.rs | 8 +++++- substrate/frame/opf/src/lib.rs | 40 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 0d11d9c124a8..ca01204907cd 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -54,7 +54,9 @@ impl Pallet { pub fn register_new(project_id: ProjectId) -> DispatchResult { let mut bounded: BoundedVec, T::MaxProjects> = WhiteListedProjectAccounts::::get(); - let _ = bounded.try_push(project_id); + let vec = bounded.clone().to_vec(); + ensure!(!vec.contains(&project_id), Error::::SubmittedProjectId); + let _ = bounded.try_push(project_id).map_err(|_| Error::::MaximumProjectsNumber); WhiteListedProjectAccounts::::put(bounded); Ok(()) } @@ -70,6 +72,10 @@ impl Pallet { } pub fn unlist_project(project_id: ProjectId) -> DispatchResult { + let mut bounded: BoundedVec, T::MaxProjects> = + WhiteListedProjectAccounts::::get(); + let vec = bounded.clone().to_vec(); + ensure!(vec.contains(&project_id), Error::::NoProjectAvailable); WhiteListedProjectAccounts::::mutate(|value| { let mut val = value.clone(); val.retain(|x| *x != project_id); diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index c835162c7ab9..2edb1f04245a 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -213,6 +213,8 @@ pub mod pallet { NoRoundFound, /// Maximum number of projects submission for reward distribution as been reached MaximumProjectsNumber, + /// Another project has already been submitted under the same project_id + SubmittedProjectId, } #[pallet::hooks] @@ -224,6 +226,26 @@ pub mod pallet { #[pallet::call] impl Pallet { + + /// OPF Projects registration + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// From this extrinsic any user can register project. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// + /// ### Errors + /// - [`Error::::SubmittedProjectId`]: Project already submitted under this project_id + /// - [`Error::::MaximumProjectsNumber`]: Maximum number of project subscriptions reached + /// + /// ## Events + /// Emits [`Event::::Projectlisted`]. #[pallet::call_index(0)] #[transactional] pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult { @@ -234,6 +256,24 @@ pub mod pallet { Ok(()) } + /// OPF Projects de-listing + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// From this extrinsic only Root can de-list a project. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// + /// ### Errors + /// - [`Error::::NoProjectAvailable`]: No project found under this project_id + /// + /// ## Events + /// Emits [`Event::::ProjectUnlisted`]. #[pallet::call_index(1)] #[transactional] pub fn unregister_project( From b639990632a09c3c1e1ffcc1f84cccf35f0063de Mon Sep 17 00:00:00 2001 From: ndkazu Date: Wed, 1 Jan 2025 22:26:27 +0900 Subject: [PATCH 13/62] Re-factored the code --- substrate/frame/opf/src/functions.rs | 30 ++---- substrate/frame/opf/src/lib.rs | 11 ++- substrate/frame/opf/src/mock.rs | 137 +++++++++++++++++++++++++++ substrate/frame/opf/src/tests.rs | 55 +++++++++++ substrate/frame/opf/src/types.rs | 13 +++ 5 files changed, 219 insertions(+), 27 deletions(-) create mode 100644 substrate/frame/opf/src/mock.rs create mode 100644 substrate/frame/opf/src/tests.rs diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index ca01204907cd..8a3b26cb9fce 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -50,17 +50,6 @@ impl Pallet { Ok(()) } - // Helper function for project registration - pub fn register_new(project_id: ProjectId) -> DispatchResult { - let mut bounded: BoundedVec, T::MaxProjects> = - WhiteListedProjectAccounts::::get(); - let vec = bounded.clone().to_vec(); - ensure!(!vec.contains(&project_id), Error::::SubmittedProjectId); - let _ = bounded.try_push(project_id).map_err(|_| Error::::MaximumProjectsNumber); - WhiteListedProjectAccounts::::put(bounded); - Ok(()) - } - // Voting Period checks pub fn period_check() -> DispatchResult { // Get current voting round & check if we are in voting period or not @@ -72,13 +61,9 @@ impl Pallet { } pub fn unlist_project(project_id: ProjectId) -> DispatchResult { - let mut bounded: BoundedVec, T::MaxProjects> = - WhiteListedProjectAccounts::::get(); - let vec = bounded.clone().to_vec(); - ensure!(vec.contains(&project_id), Error::::NoProjectAvailable); WhiteListedProjectAccounts::::mutate(|value| { let mut val = value.clone(); - val.retain(|x| *x != project_id); + val.retain(|x| x.project_id != project_id); *value = val; }); @@ -101,8 +86,9 @@ impl Pallet { // for each project, calculate the percentage of votes, the amount to be distributed, // and then populate the storage Projects for project in projects { - if ProjectFunds::::contains_key(&project) { - let funds = ProjectFunds::::get(&project); + let project_id = &project.project_id; + if ProjectFunds::::contains_key(project_id) { + let funds = ProjectFunds::::get(project_id); let project_positive_reward = funds[0]; let project_negative_reward = funds[1]; @@ -116,7 +102,7 @@ impl Pallet { // Send calculated reward for reward distribution let project_info = ProjectInfo { - project_id: project.clone(), + project_id: project.project_id.clone(), submission_block: when, amount: final_amount, }; @@ -125,7 +111,7 @@ impl Pallet { let _ = SpendInfo::::new(&project_info); Self::deposit_event(Event::::ProjectFundingAccepted { - project_id: project, + project_id: project.project_id, when, round_number, amount: project_info.amount, @@ -133,7 +119,7 @@ impl Pallet { } else { Self::deposit_event(Event::::ProjectFundingRejected { when, - project_id: project, + project_id: project.project_id, }) } } @@ -177,8 +163,6 @@ impl Pallet { // Create a new round. let _new_round = VotingRoundInfo::::new(); - // Clear WhiteListedProjectAccounts storage - WhiteListedProjectAccounts::::kill(); // Clear ProjectFunds storage ProjectFunds::::drain(); // Emmit events diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 2edb1f04245a..7d20b8f2ed1d 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -111,7 +111,11 @@ pub mod pallet { /// List of Whitelisted Project registered #[pallet::storage] pub type WhiteListedProjectAccounts = - StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + + /// Whitelisted Projects registration counter + #[pallet::storage] + pub type WhiteListedProjectCounter = StorageValue<_, u32, ValueQuery>; /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts #[pallet::storage] @@ -226,7 +230,6 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// OPF Projects registration /// /// ## Dispatch Origin @@ -251,7 +254,7 @@ pub mod pallet { pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult { let _caller = ensure_signed(origin)?; let when = T::BlockNumberProvider::current_block_number(); - Self::register_new(project_id.clone())?; + ProjectInfo::::new(project_id.clone()); Self::deposit_event(Event::Projectlisted { when, project_id }); Ok(()) } @@ -316,7 +319,7 @@ pub mod pallet { /// ### Errors /// - [`Error::::InexistentSpend`]:Spend or Spend index does not exists /// - [`Error::::NoValidAccount`]: No valid Account_id found - /// - [`Error::::NotClaimingPeriod`]: Still not in claiming period + /// - [`Not Claiming Period`]: Still not in claiming period /// /// ## Events /// Emits [`Event::::RewardClaimed`] if successful for a positive approval. diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs new file mode 100644 index 000000000000..c041669d5e19 --- /dev/null +++ b/substrate/frame/opf/src/mock.rs @@ -0,0 +1,137 @@ +// 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. + +//! Test environment for OPF pallet. +use crate as pallet_opf; +pub use frame_support::{ + derive_impl, parameter_types, + traits::{ConstU32, EqualPrivilegeOnly, OnFinalize, OnInitialize}, + weights::Weight, + PalletId, +}; +pub use sp_runtime::{ + traits::{AccountIdConversion, IdentityLookup}, + BuildStorage, +}; + +pub use frame_system::EnsureRoot; +pub type Block = frame_system::mocking::MockBlock; +pub type Balance = u64; +pub type AccountId = u64; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub struct Test { + System: frame_system, + Balances: pallet_balances, + Preimage: pallet_preimage, + Scheduler: pallet_scheduler, + Opf: pallet_opf, + } +); + +parameter_types! { + pub MaxWeight: Weight = Weight::from_parts(2_000_000_000_000, u64::MAX); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type AccountId = AccountId; + type AccountData = pallet_balances::AccountData; + type Block = Block; + type Lookup = IdentityLookup; +} + +impl pallet_preimage::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; + type ManagerOrigin = EnsureRoot; + type Consideration = (); +} +impl pallet_scheduler::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = MaxWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<100>; + type WeightInfo = (); + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = Preimage; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type AccountStore = System; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"py/potid"); + pub const MaxProjects:u32 = 50; + pub const TemporaryRewards: Balance = 100_000; + pub const VoteLockingPeriod:u32 = 10; + pub const VotingPeriod:u32 = 30; +} +impl pallet_opf::Config for Test { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type NativeBalance = Balances; + type PotId = PotId; + type MaxProjects = MaxProjects; + type VotingPeriod = VotingPeriod; + type ClaimingPeriod = VotingPeriod; + type VoteValidityPeriod = VotingPeriod; + type BlockNumberProvider = System; + type TemporaryRewards = TemporaryRewards; + type Preimages = Preimage; + type Scheduler = Scheduler; + type WeightInfo = (); +} + +//Define some accounts and use them +pub const ALICE: AccountId = 10; +pub const BOB: AccountId = 11; +pub const DAVE: AccountId = 12; +pub const EVE: AccountId = 13; +pub const BSX: Balance = 100_000_000_000; + +pub fn expect_events(e: Vec) { + e.into_iter().for_each(frame_system::Pallet::::assert_has_event); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + let pot_account = PotId::get().into_account_truncating(); + + pallet_balances::GenesisConfig:: { + balances: vec![ + (ALICE, 200_000 * BSX), + (BOB, 200_000 * BSX), + (DAVE, 150_000 * BSX), + (EVE, 150_000 * BSX), + (pot_account, 150_000_000 * BSX), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} \ No newline at end of file diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs new file mode 100644 index 000000000000..03b20b10ab83 --- /dev/null +++ b/substrate/frame/opf/src/tests.rs @@ -0,0 +1,55 @@ +// 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 OPF pallet. + +pub use super::*; +use crate::mock::*; +use frame_support::{assert_noop, assert_ok, traits::OnIdle}; + +pub fn next_block() { + System::set_block_number( + ::BlockNumberProvider::current_block_number() + 1, + ); + AllPalletsWithSystem::on_initialize( + ::BlockNumberProvider::current_block_number(), + ); + AllPalletsWithSystem::on_idle( + ::BlockNumberProvider::current_block_number(), + Weight::MAX, + ); +} + +pub fn run_to_block(n: BlockNumberFor) { + while ::BlockNumberProvider::current_block_number() < n { + if ::BlockNumberProvider::current_block_number() > 1 { + AllPalletsWithSystem::on_finalize( + ::BlockNumberProvider::current_block_number(), + ); + } + next_block(); + } +} + +#[test] +fn project_registration_works(){ + new_test_ext().execute_with(|| { + + + + }) +} \ No newline at end of file diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 99ea557ed0ac..3f96af2939d7 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -125,6 +125,19 @@ pub struct ProjectInfo { pub amount: BalanceOf, } +impl ProjectInfo { + pub fn new(project_id: ProjectId) { + let submission_block = T::BlockNumberProvider::current_block_number(); + let amount = Zero::zero(); + let project_info = ProjectInfo { project_id, submission_block, amount }; + WhiteListedProjectAccounts::::mutate(|project| { + let _ = project + .try_push(project_info.clone()) + .map_err(|_| Error::::MaximumProjectsNumber); + }); + } +} + #[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct VoteInfo { From 757d7a44332061fbad3e668411e8f8e81330ca37 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Wed, 1 Jan 2025 23:13:37 +0900 Subject: [PATCH 14/62] Listing/unlisting reserved to Root --- substrate/frame/opf/src/lib.rs | 15 ++++++++------- substrate/frame/opf/src/tests.rs | 6 ++++-- substrate/frame/opf/src/types.rs | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 7d20b8f2ed1d..7b6c9ce8aac7 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -23,6 +23,12 @@ mod types; pub use pallet_scheduler as Schedule; pub use types::*; +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + #[frame_support::pallet(dev_mode)] pub mod pallet { use super::*; @@ -113,10 +119,6 @@ pub mod pallet { pub type WhiteListedProjectAccounts = StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; - /// Whitelisted Projects registration counter - #[pallet::storage] - pub type WhiteListedProjectCounter = StorageValue<_, u32, ValueQuery>; - /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts #[pallet::storage] pub type ProjectFunds = StorageMap< @@ -238,13 +240,12 @@ pub mod pallet { /// /// ## Details /// - /// From this extrinsic any user can register project. + /// From this extrinsic only Root can register project. /// /// ### Parameters /// - `project_id`: The account that will receive the reward. /// /// ### Errors - /// - [`Error::::SubmittedProjectId`]: Project already submitted under this project_id /// - [`Error::::MaximumProjectsNumber`]: Maximum number of project subscriptions reached /// /// ## Events @@ -252,7 +253,7 @@ pub mod pallet { #[pallet::call_index(0)] #[transactional] pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - let _caller = ensure_signed(origin)?; + let _caller = ensure_root(origin)?; let when = T::BlockNumberProvider::current_block_number(); ProjectInfo::::new(project_id.clone()); Self::deposit_event(Event::Projectlisted { when, project_id }); diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 03b20b10ab83..74b2472a978d 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -49,7 +49,9 @@ pub fn run_to_block(n: BlockNumberFor) { fn project_registration_works(){ new_test_ext().execute_with(|| { - - + assert_ok!(Opf::register_project(RawOrigin::Root.into(), BOB)); + let project_list = WhiteListedProjectAccounts::::get().to_vec(); + assert_eq!(project_list.len(),1); + }) } \ No newline at end of file diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 3f96af2939d7..f3667600a622 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -158,7 +158,7 @@ pub struct VoteInfo { // If no conviction, user's funds are released at the end of the voting round impl VoteInfo { pub fn funds_unlock(&mut self) { - let conviction_coeff = >::from(self.conviction); + //let conviction_coeff = >::from(self.conviction); let funds_unlock_block = self.round.round_ending_block; self.funds_unlock_block = funds_unlock_block; } From bf0390271ab5be56c472393655cc0c1ee7c74d19 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 2 Jan 2025 20:35:57 +0900 Subject: [PATCH 15/62] Add pallet-referenda --- Cargo.lock | 1 + substrate/bin/node/runtime/src/lib.rs | 7 +- substrate/frame/opf/Cargo.toml | 4 + substrate/frame/opf/src/lib.rs | 26 +--- substrate/frame/opf/src/mock.rs | 173 ++++++++++++++++++++++++-- substrate/frame/opf/src/tests.rs | 20 ++- 6 files changed, 179 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 126e18d6a742..98b3cda37104 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14542,6 +14542,7 @@ dependencies = [ "pallet-balances 28.0.0", "pallet-conviction-voting 28.0.0", "pallet-preimage 28.0.0", + "pallet-referenda 28.0.0", "pallet-scheduler 29.0.0", "pallet-sudo 28.0.0", "pallet-timestamp 27.0.0", diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index f05048fa9a32..847746d3fb5d 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1603,7 +1603,6 @@ parameter_types! { } impl pallet_opf::Config for Runtime { - type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type NativeBalance = Balances; @@ -1630,11 +1629,7 @@ impl pallet_opf::Config for Runtime { /// This should be calculated as a percentage of inflation. type TemporaryRewards = TemporaryRewards; - - type Preimages = Preimage; - - type Scheduler = Scheduler; - + type WeightInfo = (); //pallet_opf::weights::SubstrateWeight; } diff --git a/substrate/frame/opf/Cargo.toml b/substrate/frame/opf/Cargo.toml index db8d32613361..406abdb7b703 100644 --- a/substrate/frame/opf/Cargo.toml +++ b/substrate/frame/opf/Cargo.toml @@ -22,6 +22,7 @@ frame-support = { workspace = true, default-features = false } frame-system = { workspace = true, default-features = false } log = { workspace = true } pallet-conviction-voting = { workspace = true, default-features = false } +pallet-referenda = { workspace = true, default-features = true } pallet-scheduler = { workspace = true, default-features = false } scale-info = { features = [ "derive", @@ -52,6 +53,7 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-conviction-voting/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", @@ -70,6 +72,7 @@ std = [ "pallet-balances/std", "pallet-conviction-voting/std", "pallet-preimage/std", + "pallet-referenda/std", "pallet-scheduler/std", "pallet-sudo/std", "pallet-timestamp/std", @@ -87,6 +90,7 @@ try-runtime = [ "pallet-balances/try-runtime", "pallet-conviction-voting/try-runtime", "pallet-preimage/try-runtime", + "pallet-referenda/try-runtime", "pallet-scheduler/try-runtime", "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 7b6c9ce8aac7..e0c8a5c3f091 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -20,7 +20,7 @@ pub use pallet::*; mod functions; mod types; -pub use pallet_scheduler as Schedule; +pub use pallet_referenda as Referenda; pub use types::*; #[cfg(test)] @@ -38,13 +38,7 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: frame_system::Config { - type RuntimeCall: Parameter - + Dispatchable - + From> - + IsType<::RuntimeCall> - + From>; - + pub trait Config: frame_system::Config + Referenda::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Type to access the Balances Pallet. @@ -57,22 +51,6 @@ pub mod pallet { #[pallet::constant] type PotId: Get; - /// The preimage provider. - type Preimages: QueryPreimage + StorePreimage; - - /// The Scheduler. - type Scheduler: ScheduleAnon< - ProvidedBlockNumberFor, - CallOf, - PalletsOriginOf, - Hasher = Self::Hashing, - > + ScheduleNamed< - ProvidedBlockNumberFor, - CallOf, - PalletsOriginOf, - Hasher = Self::Hashing, - >; - /// Time period in which people can vote. /// After the period has ended, the votes are counted (STOP THE COUNT) /// and then the funds are distributed into Spends. diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index c041669d5e19..8a3070584596 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -17,22 +17,55 @@ //! Test environment for OPF pallet. use crate as pallet_opf; +use codec::{Decode, Encode, MaxEncodedLen}; pub use frame_support::{ - derive_impl, parameter_types, - traits::{ConstU32, EqualPrivilegeOnly, OnFinalize, OnInitialize}, + derive_impl, ord_parameter_types, + pallet_prelude::TypeInfo, + parameter_types, + traits::{ + ConstU32, ConstU64, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, VoteTally, + }, weights::Weight, PalletId, }; +pub use frame_system::{EnsureRoot, EnsureSignedBy}; +use pallet_referenda::{impl_tracksinfo_get, Curve, TrackInfo, TracksInfo}; pub use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup}, - BuildStorage, + BuildStorage, Perbill, }; - -pub use frame_system::EnsureRoot; pub type Block = frame_system::mocking::MockBlock; pub type Balance = u64; pub type AccountId = u64; +#[derive(Encode, Debug, Decode, TypeInfo, Eq, PartialEq, Clone, MaxEncodedLen)] +pub struct Tally { + pub ayes: u32, + pub nays: u32, +} + +impl VoteTally for Tally { + fn new(_: Class) -> Self { + Self { ayes: 0, nays: 0 } + } + + fn ayes(&self, _: Class) -> u32 { + self.ayes + } + + fn support(&self, _: Class) -> Perbill { + Perbill::from_percent(self.ayes) + } + + fn approval(&self, _: Class) -> Perbill { + if self.ayes + self.nays > 0 { + Perbill::from_rational(self.ayes, self.ayes + self.nays) + } else { + Perbill::zero() + } + } +} + // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub struct Test { @@ -41,6 +74,7 @@ frame_support::construct_runtime!( Preimage: pallet_preimage, Scheduler: pallet_scheduler, Opf: pallet_opf, + Referenda: pallet_referenda, } ); @@ -82,14 +116,135 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub const PotId: PalletId = PalletId(*b"py/potid"); + pub static AlarmInterval: u64 = 1; +} +ord_parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; + pub const Three: u64 = 3; + pub const Four: u64 = 4; + pub const Five: u64 = 5; + pub const Six: u64 = 6; +} + +pub struct TestTracksInfo; +impl TracksInfo for TestTracksInfo { + type Id = u8; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, TrackInfo)] { + static DATA: [(u8, TrackInfo); 3] = [ + ( + 0u8, + TrackInfo { + name: "root", + max_deciding: 1, + decision_deposit: 10, + prepare_period: 4, + decision_period: 4, + confirm_period: 2, + min_enactment_period: 4, + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(50), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(0), + ceil: Perbill::from_percent(100), + }, + }, + ), + ( + 1u8, + TrackInfo { + name: "none", + max_deciding: 3, + decision_deposit: 1, + prepare_period: 2, + decision_period: 2, + confirm_period: 1, + min_enactment_period: 2, + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(95), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(90), + ceil: Perbill::from_percent(100), + }, + }, + ), + ( + 2u8, + TrackInfo { + name: "none", + max_deciding: 3, + decision_deposit: 1, + prepare_period: 2, + decision_period: 2, + confirm_period: 1, + min_enactment_period: 0, + min_approval: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(95), + ceil: Perbill::from_percent(100), + }, + min_support: Curve::LinearDecreasing { + length: Perbill::from_percent(100), + floor: Perbill::from_percent(90), + ceil: Perbill::from_percent(100), + }, + }, + ), + ]; + &DATA[..] + } + fn track_for(id: &Self::RuntimeOrigin) -> Result { + if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => Ok(0), + frame_system::RawOrigin::None => Ok(1), + frame_system::RawOrigin::Signed(1) => Ok(2), + _ => Err(()), + } + } else { + Err(()) + } + } +} +impl_tracksinfo_get!(TestTracksInfo, u64, u64); + +impl pallet_referenda::Config for Test { + type WeightInfo = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = pallet_balances::Pallet; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EnsureSignedBy; + type KillOrigin = EnsureRoot; + type Slash = (); + type Votes = u32; + type Tally = Tally; + type SubmissionDeposit = ConstU64<2>; + type MaxQueued = ConstU32<3>; + type UndecidingTimeout = ConstU64<20>; + type AlarmInterval = AlarmInterval; + type Tracks = TestTracksInfo; + type Preimages = Preimage; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"py/potid"); pub const MaxProjects:u32 = 50; pub const TemporaryRewards: Balance = 100_000; pub const VoteLockingPeriod:u32 = 10; pub const VotingPeriod:u32 = 30; } impl pallet_opf::Config for Test { - type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type NativeBalance = Balances; type PotId = PotId; @@ -99,8 +254,6 @@ impl pallet_opf::Config for Test { type VoteValidityPeriod = VotingPeriod; type BlockNumberProvider = System; type TemporaryRewards = TemporaryRewards; - type Preimages = Preimage; - type Scheduler = Scheduler; type WeightInfo = (); } @@ -134,4 +287,4 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); ext -} \ No newline at end of file +} diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 74b2472a978d..f49dab30e1fd 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -22,9 +22,7 @@ use crate::mock::*; use frame_support::{assert_noop, assert_ok, traits::OnIdle}; pub fn next_block() { - System::set_block_number( - ::BlockNumberProvider::current_block_number() + 1, - ); + System::set_block_number(::BlockNumberProvider::current_block_number() + 1); AllPalletsWithSystem::on_initialize( ::BlockNumberProvider::current_block_number(), ); @@ -46,12 +44,10 @@ pub fn run_to_block(n: BlockNumberFor) { } #[test] -fn project_registration_works(){ - new_test_ext().execute_with(|| { - - assert_ok!(Opf::register_project(RawOrigin::Root.into(), BOB)); - let project_list = WhiteListedProjectAccounts::::get().to_vec(); - assert_eq!(project_list.len(),1); - - }) -} \ No newline at end of file +fn project_registration_works() { + new_test_ext().execute_with(|| { + assert_ok!(Opf::register_project(RawOrigin::Root.into(), BOB)); + let project_list = WhiteListedProjectAccounts::::get().to_vec(); + assert_eq!(project_list.len(), 1); + }) +} From 9ad6f9834d71729964d2897682a662ba2c93a03f Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 4 Jan 2025 22:32:08 +0900 Subject: [PATCH 16/62] Applied some corrections --- substrate/bin/node/runtime/src/lib.rs | 3 +++ substrate/frame/opf/src/functions.rs | 33 ++++++++++++--------------- substrate/frame/opf/src/lib.rs | 33 ++++++++++++++++++--------- substrate/frame/opf/src/mock.rs | 1 + substrate/frame/opf/src/tests.rs | 4 ++-- substrate/frame/opf/src/types.rs | 23 ++++++++----------- 6 files changed, 52 insertions(+), 45 deletions(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 847746d3fb5d..61e11a5a2eb8 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1609,6 +1609,9 @@ impl pallet_opf::Config for Runtime { /// Pot PalletId type PotId = PotId; + /// A reason for placing a hold on funds. + type RuntimeHoldReason = RuntimeHoldReason; + /// Maximum number of whitelisted projects type MaxProjects = MaxProjects; diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 8a3b26cb9fce..2834208fb052 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -53,7 +53,7 @@ impl Pallet { // Voting Period checks pub fn period_check() -> DispatchResult { // Get current voting round & check if we are in voting period or not - let current_round_index = VotingRoundNumber::::get().saturating_sub(1); + let current_round_index = NextVotingRoundNumber::::get().saturating_sub(1); let round = VotingRounds::::get(current_round_index).ok_or(Error::::NoRoundFound)?; let now = T::BlockNumberProvider::current_block_number(); ensure!(now < round.round_ending_block, Error::::VotingRoundOver); @@ -61,11 +61,7 @@ impl Pallet { } pub fn unlist_project(project_id: ProjectId) -> DispatchResult { - WhiteListedProjectAccounts::::mutate(|value| { - let mut val = value.clone(); - val.retain(|x| x.project_id != project_id); - *value = val; - }); + WhiteListedProjectAccounts::::remove(&project_id); Ok(()) } @@ -73,8 +69,9 @@ impl Pallet { // The total reward to be distributed is a portion or inflation, determined in another pallet // Reward calculation is executed within the Voting period pub fn calculate_rewards(total_reward: BalanceOf) -> DispatchResult { - let projects = WhiteListedProjectAccounts::::get(); - let round_number = VotingRoundNumber::::get().saturating_sub(1); + let projects: Vec> = WhiteListedProjectAccounts::::iter_keys().collect(); + //if projects.is_empty() { return Ok(()) } + let round_number = NextVotingRoundNumber::::get().saturating_sub(1); let round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; if projects.clone().len() > 0 as usize { let total_positive_votes_amount = round.total_positive_votes_amount; @@ -85,10 +82,9 @@ impl Pallet { // for each project, calculate the percentage of votes, the amount to be distributed, // and then populate the storage Projects - for project in projects { - let project_id = &project.project_id; - if ProjectFunds::::contains_key(project_id) { - let funds = ProjectFunds::::get(project_id); + for project_id in projects { + if ProjectFunds::::contains_key(&project_id) { + let funds = ProjectFunds::::get(&project_id); let project_positive_reward = funds[0]; let project_negative_reward = funds[1]; @@ -102,7 +98,7 @@ impl Pallet { // Send calculated reward for reward distribution let project_info = ProjectInfo { - project_id: project.project_id.clone(), + project_id: project_id.clone(), submission_block: when, amount: final_amount, }; @@ -111,7 +107,7 @@ impl Pallet { let _ = SpendInfo::::new(&project_info); Self::deposit_event(Event::::ProjectFundingAccepted { - project_id: project.project_id, + project_id: project_id.clone(), when, round_number, amount: project_info.amount, @@ -119,7 +115,7 @@ impl Pallet { } else { Self::deposit_event(Event::::ProjectFundingRejected { when, - project_id: project.project_id, + project_id: project_id.clone(), }) } } @@ -138,13 +134,13 @@ impl Pallet { if meter.try_consume(max_block_weight).is_err() { return meter.consumed(); } - let mut round_index = VotingRoundNumber::::get(); + let mut round_index = NextVotingRoundNumber::::get(); // No active round? if round_index == 0 { // Start the first voting round let _round0 = VotingRoundInfo::::new(); - round_index = VotingRoundNumber::::get(); + round_index = NextVotingRoundNumber::::get(); } let current_round_index = round_index.saturating_sub(1); @@ -154,8 +150,7 @@ impl Pallet { // Conditions for reward distribution preparations are: // - We are at the end of voting_round period - - if now == round_ending_block { + if now >= round_ending_block { // prepare reward distribution // for now we are using the temporary-constant reward. let _ = Self::calculate_rewards(T::TemporaryRewards::get()) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index e0c8a5c3f091..2a7c6cc21c63 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -42,8 +42,14 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Type to access the Balances Pallet. - type NativeBalance: fungible::Inspect + fungible::Mutate; - + type NativeBalance: fungible::Inspect + + fungible::Mutate + + fungible::hold::Inspect + + fungible::hold::Mutate + + fungible::freeze::Inspect + + fungible::freeze::Mutate; + + type RuntimeHoldReason: From; /// Provider for the block number. type BlockNumberProvider: BlockNumberProvider; @@ -78,9 +84,17 @@ pub mod pallet { type WeightInfo: WeightInfo; } + /// A reason for placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// Funds are held for a given buffer time before payment + #[codec(index = 0)] + FundsReserved, + } + /// Number of Voting Rounds executed so far #[pallet::storage] - pub type VotingRoundNumber = StorageValue<_, u32, ValueQuery>; + pub type NextVotingRoundNumber = StorageValue<_, u32, ValueQuery>; /// Returns Infos about a Voting Round agains the Voting Round index #[pallet::storage] @@ -95,7 +109,7 @@ pub mod pallet { /// List of Whitelisted Project registered #[pallet::storage] pub type WhiteListedProjectAccounts = - StorageValue<_, BoundedVec, T::MaxProjects>, ValueQuery>; + CountedStorageMap<_, Twox64Concat, ProjectId, ProjectInfo, OptionQuery>; /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts #[pallet::storage] @@ -229,7 +243,6 @@ pub mod pallet { /// ## Events /// Emits [`Event::::Projectlisted`]. #[pallet::call_index(0)] - #[transactional] pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult { let _caller = ensure_root(origin)?; let when = T::BlockNumberProvider::current_block_number(); @@ -308,16 +321,14 @@ pub mod pallet { let _caller = ensure_signed(origin)?; let now = T::BlockNumberProvider::current_block_number(); let info = Spends::::get(&project_id).ok_or(Error::::InexistentSpend)?; - match now { - _ if now >= info.expire => { + if now >= info.expire { Spends::::remove(&project_id); Self::deposit_event(Event::ExpiredClaim { expired_when: info.expire, project_id, }); Ok(()) - }, - _ if now >= info.expire => { + } else if now < info.expire { // transfer the funds Self::spend(info.amount, project_id.clone())?; @@ -327,8 +338,8 @@ pub mod pallet { project_id, }); Ok(()) - }, - _ => Err(DispatchError::Other("Not Claiming Period")), + } else { + Err(DispatchError::Other("Not Claiming Period")) } } } diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 8a3070584596..7f68aaa5c6f8 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -248,6 +248,7 @@ impl pallet_opf::Config for Test { type RuntimeEvent = RuntimeEvent; type NativeBalance = Balances; type PotId = PotId; + type RuntimeHoldReason = RuntimeHoldReason; type MaxProjects = MaxProjects; type VotingPeriod = VotingPeriod; type ClaimingPeriod = VotingPeriod; diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index f49dab30e1fd..ce7975346407 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -47,7 +47,7 @@ pub fn run_to_block(n: BlockNumberFor) { fn project_registration_works() { new_test_ext().execute_with(|| { assert_ok!(Opf::register_project(RawOrigin::Root.into(), BOB)); - let project_list = WhiteListedProjectAccounts::::get().to_vec(); - assert_eq!(project_list.len(), 1); + let project_list = WhiteListedProjectAccounts::::get(BOB); + assert!(project_list.is_some()); }) } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index f3667600a622..59c394dd589a 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -129,13 +129,10 @@ impl ProjectInfo { pub fn new(project_id: ProjectId) { let submission_block = T::BlockNumberProvider::current_block_number(); let amount = Zero::zero(); - let project_info = ProjectInfo { project_id, submission_block, amount }; - WhiteListedProjectAccounts::::mutate(|project| { - let _ = project - .try_push(project_info.clone()) - .map_err(|_| Error::::MaximumProjectsNumber); - }); + let project_info = ProjectInfo { project_id: project_id.clone(), submission_block, amount }; + WhiteListedProjectAccounts::::insert(project_id, project_info); } + } #[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] @@ -158,8 +155,11 @@ pub struct VoteInfo { // If no conviction, user's funds are released at the end of the voting round impl VoteInfo { pub fn funds_unlock(&mut self) { - //let conviction_coeff = >::from(self.conviction); - let funds_unlock_block = self.round.round_ending_block; + let conviction_coeff = >::from(self.conviction); + let funds_unlock_block = self + .round + .round_ending_block + .saturating_add(T::VoteValidityPeriod::get().saturating_mul(conviction_coeff.into())); self.funds_unlock_block = funds_unlock_block; } } @@ -193,11 +193,8 @@ impl VotingRoundInfo { let round_starting_block = T::BlockNumberProvider::current_block_number(); let round_ending_block = round_starting_block .clone() - .checked_add(&T::VotingPeriod::get()) - .expect("Invalid Result"); - let round_number = VotingRoundNumber::::get(); - let new_number = round_number.checked_add(1).expect("Invalid Result"); - VotingRoundNumber::::put(new_number); + .saturating_add(T::VotingPeriod::get()); + let round_number = NextVotingRoundNumber::::mutate(|n| { let res = *n; *n = n.saturating_add(1); res }); let total_positive_votes_amount = BalanceOf::::zero(); let total_negative_votes_amount = BalanceOf::::zero(); From 2d39b0cb3245a31052b4310125348c633b7389e5 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 4 Jan 2025 22:47:02 +0900 Subject: [PATCH 17/62] Custom Origin --- substrate/bin/node/runtime/src/lib.rs | 2 +- substrate/frame/opf/src/lib.rs | 7 +++++-- substrate/frame/opf/src/mock.rs | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 61e11a5a2eb8..c9919bde9b76 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1605,7 +1605,7 @@ parameter_types! { impl pallet_opf::Config for Runtime { type RuntimeEvent = RuntimeEvent; type NativeBalance = Balances; - + type AdminOrigin = EnsureRoot; /// Pot PalletId type PotId = PotId; diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 2a7c6cc21c63..bedf28366473 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -41,6 +41,9 @@ pub mod pallet { pub trait Config: frame_system::Config + Referenda::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The admin origin that can list and un-list whitelisted projects. + type AdminOrigin: EnsureOrigin; + /// Type to access the Balances Pallet. type NativeBalance: fungible::Inspect + fungible::Mutate @@ -244,7 +247,7 @@ pub mod pallet { /// Emits [`Event::::Projectlisted`]. #[pallet::call_index(0)] pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - let _caller = ensure_root(origin)?; + T::AdminOrigin::ensure_origin_or_root(origin)?; let when = T::BlockNumberProvider::current_block_number(); ProjectInfo::::new(project_id.clone()); Self::deposit_event(Event::Projectlisted { when, project_id }); @@ -275,7 +278,7 @@ pub mod pallet { origin: OriginFor, project_id: ProjectId, ) -> DispatchResult { - ensure_root(origin)?; + T::AdminOrigin::ensure_origin_or_root(origin)?; let when = T::BlockNumberProvider::current_block_number(); Self::unlist_project(project_id.clone())?; Self::deposit_event(Event::::ProjectUnlisted { when, project_id }); diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 7f68aaa5c6f8..403e41215e29 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -246,6 +246,7 @@ parameter_types! { } impl pallet_opf::Config for Test { type RuntimeEvent = RuntimeEvent; + type AdminOrigin = frame_system::EnsureRoot; type NativeBalance = Balances; type PotId = PotId; type RuntimeHoldReason = RuntimeHoldReason; From 21a3169cac22d2bef3319fe1e9be8ee8f531cd5c Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sun, 5 Jan 2025 16:44:18 +0900 Subject: [PATCH 18/62] Batch registration --- substrate/frame/opf/src/lib.rs | 53 ++++++++++++++++++++++++++------ substrate/frame/opf/src/types.rs | 2 +- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index bedf28366473..13d497d45235 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -159,6 +159,9 @@ pub mod pallet { /// Project added to whitelisted projects list Projectlisted { when: ProvidedBlockNumberFor, project_id: ProjectId }, + /// Several projects added to whitelisted projects list + Projectslisted { when: ProvidedBlockNumberFor, projects_id: Vec> }, + /// Project removed from whitelisted projects list ProjectUnlisted { when: ProvidedBlockNumberFor, project_id: ProjectId }, @@ -231,14 +234,14 @@ pub mod pallet { /// /// ## Dispatch Origin /// - /// Must be signed + /// Must be AdminOrigin /// /// ## Details /// - /// From this extrinsic only Root can register project. + /// From this extrinsic only AdminOrigin can register project. /// /// ### Parameters - /// - `project_id`: The account that will receive the reward. + /// - `project_id`: The account that might be funded. /// /// ### Errors /// - [`Error::::MaximumProjectsNumber`]: Maximum number of project subscriptions reached @@ -254,6 +257,36 @@ pub mod pallet { Ok(()) } + /// OPF Projects registration + /// + /// ## Dispatch Origin + /// + /// Must be AdminOrigin + /// + /// ## Details + /// + /// From this extrinsic only AdminOrigin can register project. + /// + /// ### Parameters + /// - `projects_id`: The accounts that might be funded. + /// + /// ### Errors + /// - [`Error::::MaximumProjectsNumber`]: Maximum number of project subscriptions reached + /// + /// ## Events + /// Emits [`Event::::Projectslisted`]. + #[pallet::call_index(1)] + pub fn register_projects_batch(origin: OriginFor, projects_id: Vec>) -> DispatchResult { + T::AdminOrigin::ensure_origin_or_root(origin)?; + let when = T::BlockNumberProvider::current_block_number(); + for project_id in &projects_id{ + ProjectInfo::::new(project_id.clone()); + } + + Self::deposit_event(Event::Projectslisted { when, projects_id }); + Ok(()) + } + /// OPF Projects de-listing /// /// ## Dispatch Origin @@ -272,7 +305,7 @@ pub mod pallet { /// /// ## Events /// Emits [`Event::::ProjectUnlisted`]. - #[pallet::call_index(1)] + #[pallet::call_index(2)] #[transactional] pub fn unregister_project( origin: OriginFor, @@ -286,13 +319,13 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(2)] + #[pallet::call_index(3)] #[transactional] pub fn vote(origin: OriginFor) -> DispatchResult { Ok(()) } - #[pallet::call_index(3)] + #[pallet::call_index(4)] #[transactional] pub fn remove_vote(origin: OriginFor) -> DispatchResult { Ok(()) @@ -318,7 +351,7 @@ pub mod pallet { /// /// ## Events /// Emits [`Event::::RewardClaimed`] if successful for a positive approval. - #[pallet::call_index(4)] + #[pallet::call_index(5)] #[transactional] pub fn claim_reward_for(origin: OriginFor, project_id: ProjectId) -> DispatchResult { let _caller = ensure_signed(origin)?; @@ -333,13 +366,13 @@ pub mod pallet { Ok(()) } else if now < info.expire { // transfer the funds - Self::spend(info.amount, project_id.clone())?; - + Self::spend(info.amount, project_id.clone())?; Self::deposit_event(Event::RewardClaimed { when: now, amount: info.amount, - project_id, + project_id: project_id.clone(), }); + Self::unlist_project(project_id)?; Ok(()) } else { Err(DispatchError::Other("Not Claiming Period")) diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 59c394dd589a..4a10d0756d8a 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -121,7 +121,7 @@ pub struct ProjectInfo { /// Block at which the project was submitted for reward distribution pub submission_block: ProvidedBlockNumberFor, - /// Amount to be lock & pay for this project + /// Amount to be locked & payed for this project pub amount: BalanceOf, } From 78454bc75ea32bf91e52c752ec80b36ae9e455fa Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 7 Jan 2025 13:22:25 +0900 Subject: [PATCH 19/62] Correction --- substrate/frame/opf/src/functions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 2834208fb052..36e1c811a2ad 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -70,7 +70,7 @@ impl Pallet { // Reward calculation is executed within the Voting period pub fn calculate_rewards(total_reward: BalanceOf) -> DispatchResult { let projects: Vec> = WhiteListedProjectAccounts::::iter_keys().collect(); - //if projects.is_empty() { return Ok(()) } + if projects.is_empty() { return Ok(()) } let round_number = NextVotingRoundNumber::::get().saturating_sub(1); let round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; if projects.clone().len() > 0 as usize { From 367d7bb226e321528df10fd1bb322e68e00c9250 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 7 Jan 2025 16:57:09 +0900 Subject: [PATCH 20/62] Using pallet democracy instead of pallet referenda --- Cargo.lock | 2 +- substrate/bin/node/runtime/src/lib.rs | 2 +- substrate/frame/opf/Cargo.toml | 8 +- substrate/frame/opf/src/functions.rs | 10 +- substrate/frame/opf/src/lib.rs | 78 ++++++----- substrate/frame/opf/src/mock.rs | 184 +++++++------------------- substrate/frame/opf/src/tests.rs | 8 +- substrate/frame/opf/src/types.rs | 7 +- 8 files changed, 120 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dedd56da42d8..516d06e5d051 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14555,8 +14555,8 @@ dependencies = [ "pallet-assets 29.1.0", "pallet-balances 28.0.0", "pallet-conviction-voting 28.0.0", + "pallet-democracy 28.0.0", "pallet-preimage 28.0.0", - "pallet-referenda 28.0.0", "pallet-scheduler 29.0.0", "pallet-sudo 28.0.0", "pallet-timestamp 27.0.0", diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index cdf4f139ce04..80fc5d88047a 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1593,7 +1593,6 @@ impl pallet_offences::Config for Runtime { parameter_types! { // Id of the treasury pub const PotId: PalletId = PalletId(*b"py/potid"); - pub const VotingPeriod:BlockNumber = 30 * DAYS; pub const ClaimingPeriod: BlockNumber = 7 * DAYS; pub const VoteValidityPeriod: BlockNumber = 7 * DAYS; pub const MaxProjects:u32 = 50; @@ -1605,6 +1604,7 @@ parameter_types! { } impl pallet_opf::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type NativeBalance = Balances; type AdminOrigin = EnsureRoot; /// Pot PalletId diff --git a/substrate/frame/opf/Cargo.toml b/substrate/frame/opf/Cargo.toml index 406abdb7b703..d2c7c662d39b 100644 --- a/substrate/frame/opf/Cargo.toml +++ b/substrate/frame/opf/Cargo.toml @@ -22,7 +22,7 @@ frame-support = { workspace = true, default-features = false } frame-system = { workspace = true, default-features = false } log = { workspace = true } pallet-conviction-voting = { workspace = true, default-features = false } -pallet-referenda = { workspace = true, default-features = true } +pallet-democracy = { workspace = true, default-features = true } pallet-scheduler = { workspace = true, default-features = false } scale-info = { features = [ "derive", @@ -52,8 +52,8 @@ runtime-benchmarks = [ "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-conviction-voting/runtime-benchmarks", + "pallet-democracy/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", - "pallet-referenda/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", @@ -71,8 +71,8 @@ std = [ "pallet-assets/std", "pallet-balances/std", "pallet-conviction-voting/std", + "pallet-democracy/std", "pallet-preimage/std", - "pallet-referenda/std", "pallet-scheduler/std", "pallet-sudo/std", "pallet-timestamp/std", @@ -89,8 +89,8 @@ try-runtime = [ "pallet-assets/try-runtime", "pallet-balances/try-runtime", "pallet-conviction-voting/try-runtime", + "pallet-democracy/try-runtime", "pallet-preimage/try-runtime", - "pallet-referenda/try-runtime", "pallet-scheduler/try-runtime", "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 36e1c811a2ad..863cddad927f 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -150,14 +150,8 @@ impl Pallet { // Conditions for reward distribution preparations are: // - We are at the end of voting_round period - if now >= round_ending_block { - // prepare reward distribution - // for now we are using the temporary-constant reward. - let _ = Self::calculate_rewards(T::TemporaryRewards::get()) - .map_err(|_| Error::::FailedRewardCalculation); - - // Create a new round. - let _new_round = VotingRoundInfo::::new(); + if now > round_ending_block { + // Clear ProjectFunds storage ProjectFunds::::drain(); // Emmit events diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 13d497d45235..5342d66b486d 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -20,7 +20,7 @@ pub use pallet::*; mod functions; mod types; -pub use pallet_referenda as Referenda; +pub use pallet_democracy as Democracy; pub use types::*; #[cfg(test)] @@ -38,9 +38,13 @@ pub mod pallet { pub struct Pallet(_); #[pallet::config] - pub trait Config: frame_system::Config + Referenda::Config { + pub trait Config: frame_system::Config + Democracy::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - + type RuntimeCall: Convert<::RuntimeCall, ::RuntimeCall> + + Parameter + + UnfilteredDispatchable::RuntimeOrigin> + + From> + + GetDispatchInfo; /// The admin origin that can list and un-list whitelisted projects. type AdminOrigin: EnsureOrigin; @@ -219,6 +223,8 @@ pub mod pallet { MaximumProjectsNumber, /// Another project has already been submitted under the same project_id SubmittedProjectId, + /// Project batch already submitted + BatchAlreadySubmitted } #[pallet::hooks] @@ -230,32 +236,6 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// OPF Projects registration - /// - /// ## Dispatch Origin - /// - /// Must be AdminOrigin - /// - /// ## Details - /// - /// From this extrinsic only AdminOrigin can register project. - /// - /// ### Parameters - /// - `project_id`: The account that might be funded. - /// - /// ### Errors - /// - [`Error::::MaximumProjectsNumber`]: Maximum number of project subscriptions reached - /// - /// ## Events - /// Emits [`Event::::Projectlisted`]. - #[pallet::call_index(0)] - pub fn register_project(origin: OriginFor, project_id: ProjectId) -> DispatchResult { - T::AdminOrigin::ensure_origin_or_root(origin)?; - let when = T::BlockNumberProvider::current_block_number(); - ProjectInfo::::new(project_id.clone()); - Self::deposit_event(Event::Projectlisted { when, project_id }); - Ok(()) - } /// OPF Projects registration /// @@ -277,10 +257,39 @@ pub mod pallet { /// Emits [`Event::::Projectslisted`]. #[pallet::call_index(1)] pub fn register_projects_batch(origin: OriginFor, projects_id: Vec>) -> DispatchResult { - T::AdminOrigin::ensure_origin_or_root(origin)?; + //T::AdminOrigin::ensure_origin_or_root(origin.clone())?; + let who = T::SubmitOrigin::ensure_origin(origin.clone())?; + // Only 1 batch submission per round + let mut round_index = NextVotingRoundNumber::::get(); + + // No active round? + if round_index == 0 { + // Start the first voting round + let _round0 = VotingRoundInfo::::new(); + round_index = NextVotingRoundNumber::::get(); + } + + let current_round_index = round_index.saturating_sub(1); + + let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); + // Check no Project batch has been submitted yet + ensure!(round_infos.batch_submitted == false, Error::::BatchAlreadySubmitted); + let round_ending_block = round_infos.round_ending_block; + + // If current voting round is over, start a new one let when = T::BlockNumberProvider::current_block_number(); + if when >= round_ending_block { + // Create a new round. + let _new_round = VotingRoundInfo::::new(); + } + for project_id in &projects_id{ ProjectInfo::::new(project_id.clone()); + // Prepare the proposal call + let call0: ::RuntimeCall = crate::Call::::on_registration {project_id: project_id.clone()}.into(); + let call = ::RuntimeCall::convert(call0); + let call_f = T::Preimages::bound(call).unwrap(); + Democracy::Pallet::::propose(origin.clone(), call_f, T::MinimumDeposit::get())?; } Self::deposit_event(Event::Projectslisted { when, projects_id }); @@ -378,5 +387,14 @@ pub mod pallet { Err(DispatchError::Other("Not Claiming Period")) } } + + #[pallet::call_index(6)] + #[transactional] + pub fn on_registration(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + // prepare reward distribution + // for now we are using the temporary-constant reward. + let _ = Self::calculate_rewards(T::TemporaryRewards::get())?; + Ok(()) + } } } diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 403e41215e29..e6bbb19cb443 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -18,18 +18,18 @@ //! Test environment for OPF pallet. use crate as pallet_opf; use codec::{Decode, Encode, MaxEncodedLen}; +use crate::Convert; pub use frame_support::{ derive_impl, ord_parameter_types, pallet_prelude::TypeInfo, parameter_types, traits::{ - ConstU32, ConstU64, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, VoteTally, + ConstU32, ConstU64, SortedMembers, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, VoteTally, }, weights::Weight, PalletId, }; -pub use frame_system::{EnsureRoot, EnsureSignedBy}; -use pallet_referenda::{impl_tracksinfo_get, Curve, TrackInfo, TracksInfo}; +pub use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; pub use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup}, BuildStorage, Perbill, @@ -38,33 +38,6 @@ pub type Block = frame_system::mocking::MockBlock; pub type Balance = u64; pub type AccountId = u64; -#[derive(Encode, Debug, Decode, TypeInfo, Eq, PartialEq, Clone, MaxEncodedLen)] -pub struct Tally { - pub ayes: u32, - pub nays: u32, -} - -impl VoteTally for Tally { - fn new(_: Class) -> Self { - Self { ayes: 0, nays: 0 } - } - - fn ayes(&self, _: Class) -> u32 { - self.ayes - } - - fn support(&self, _: Class) -> Perbill { - Perbill::from_percent(self.ayes) - } - - fn approval(&self, _: Class) -> Perbill { - if self.ayes + self.nays > 0 { - Perbill::from_rational(self.ayes, self.ayes + self.nays) - } else { - Perbill::zero() - } - } -} // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( @@ -74,7 +47,7 @@ frame_support::construct_runtime!( Preimage: pallet_preimage, Scheduler: pallet_scheduler, Opf: pallet_opf, - Referenda: pallet_referenda, + Democracy: pallet_democracy, } ); @@ -116,7 +89,8 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub static AlarmInterval: u64 = 1; + pub static PreimageByteDeposit: u64 = 0; + pub static InstantAllowed: bool = false; } ord_parameter_types! { pub const One: u64 = 1; @@ -127,113 +101,44 @@ ord_parameter_types! { pub const Six: u64 = 6; } -pub struct TestTracksInfo; -impl TracksInfo for TestTracksInfo { - type Id = u8; - type RuntimeOrigin = ::PalletsOrigin; - fn tracks() -> &'static [(Self::Id, TrackInfo)] { - static DATA: [(u8, TrackInfo); 3] = [ - ( - 0u8, - TrackInfo { - name: "root", - max_deciding: 1, - decision_deposit: 10, - prepare_period: 4, - decision_period: 4, - confirm_period: 2, - min_enactment_period: 4, - min_approval: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(50), - ceil: Perbill::from_percent(100), - }, - min_support: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(0), - ceil: Perbill::from_percent(100), - }, - }, - ), - ( - 1u8, - TrackInfo { - name: "none", - max_deciding: 3, - decision_deposit: 1, - prepare_period: 2, - decision_period: 2, - confirm_period: 1, - min_enactment_period: 2, - min_approval: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(95), - ceil: Perbill::from_percent(100), - }, - min_support: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(90), - ceil: Perbill::from_percent(100), - }, - }, - ), - ( - 2u8, - TrackInfo { - name: "none", - max_deciding: 3, - decision_deposit: 1, - prepare_period: 2, - decision_period: 2, - confirm_period: 1, - min_enactment_period: 0, - min_approval: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(95), - ceil: Perbill::from_percent(100), - }, - min_support: Curve::LinearDecreasing { - length: Perbill::from_percent(100), - floor: Perbill::from_percent(90), - ceil: Perbill::from_percent(100), - }, - }, - ), - ]; - &DATA[..] - } - fn track_for(id: &Self::RuntimeOrigin) -> Result { - if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { - match system_origin { - frame_system::RawOrigin::Root => Ok(0), - frame_system::RawOrigin::None => Ok(1), - frame_system::RawOrigin::Signed(1) => Ok(2), - _ => Err(()), - } - } else { - Err(()) - } +pub struct OneToFive; +impl SortedMembers for OneToFive { + fn sorted_members() -> Vec { + vec![1, 2, 3, 4, 5] } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &u64) {} } -impl_tracksinfo_get!(TestTracksInfo, u64, u64); -impl pallet_referenda::Config for Test { - type WeightInfo = (); - type RuntimeCall = RuntimeCall; +impl pallet_democracy::Config for Test { type RuntimeEvent = RuntimeEvent; - type Scheduler = Scheduler; type Currency = pallet_balances::Pallet; - type SubmitOrigin = frame_system::EnsureSigned; - type CancelOrigin = EnsureSignedBy; - type KillOrigin = EnsureRoot; + type EnactmentPeriod = ConstU64<2>; + type LaunchPeriod = ConstU64<2>; + type VotingPeriod = ConstU64<2>; + type VoteLockingPeriod = ConstU64<3>; + type FastTrackVotingPeriod = ConstU64<2>; + type MinimumDeposit = ConstU64<1>; + type MaxDeposits = ConstU32<1000>; + type MaxBlacklisted = ConstU32<5>; + type SubmitOrigin = EnsureSigned; + type ExternalOrigin = EnsureSignedBy; + type ExternalMajorityOrigin = EnsureSignedBy; + type ExternalDefaultOrigin = EnsureSignedBy; + type FastTrackOrigin = EnsureSignedBy; + type CancellationOrigin = EnsureSignedBy; + type BlacklistOrigin = EnsureRoot; + type CancelProposalOrigin = EnsureRoot; + type VetoOrigin = EnsureSignedBy; + type CooloffPeriod = ConstU64<2>; type Slash = (); - type Votes = u32; - type Tally = Tally; - type SubmissionDeposit = ConstU64<2>; - type MaxQueued = ConstU32<3>; - type UndecidingTimeout = ConstU64<20>; - type AlarmInterval = AlarmInterval; - type Tracks = TestTracksInfo; + type InstantOrigin = EnsureSignedBy; + type InstantAllowed = InstantAllowed; + type Scheduler = Scheduler; + type MaxVotes = ConstU32<100>; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); + type MaxProposals = ConstU32<100>; type Preimages = Preimage; } @@ -246,6 +151,7 @@ parameter_types! { } impl pallet_opf::Config for Test { type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type AdminOrigin = frame_system::EnsureRoot; type NativeBalance = Balances; type PotId = PotId; @@ -259,6 +165,18 @@ impl pallet_opf::Config for Test { type WeightInfo = (); } +impl Convert for RuntimeCall { + fn convert(call: RuntimeCall) -> RuntimeCall { + let call_encoded: Vec = call.encode(); + let ref_call_encoded = &call_encoded; + if let Ok(call_formatted) = RuntimeCall::decode(&mut &ref_call_encoded[..]){ + call_formatted + } else{ + call + } + } +} + //Define some accounts and use them pub const ALICE: AccountId = 10; pub const BOB: AccountId = 11; diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index ce7975346407..dcd42430d054 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -32,6 +32,11 @@ pub fn next_block() { ); } +pub fn project_list() -> Vec>{ + vec![ALICE, BOB, DAVE] + +} + pub fn run_to_block(n: BlockNumberFor) { while ::BlockNumberProvider::current_block_number() < n { if ::BlockNumberProvider::current_block_number() > 1 { @@ -46,7 +51,8 @@ pub fn run_to_block(n: BlockNumberFor) { #[test] fn project_registration_works() { new_test_ext().execute_with(|| { - assert_ok!(Opf::register_project(RawOrigin::Root.into(), BOB)); + let batch = project_list(); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); let project_list = WhiteListedProjectAccounts::::get(BOB); assert!(project_list.is_some()); }) diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 4a10d0756d8a..ae6ac1581654 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -21,6 +21,8 @@ pub use super::*; pub use frame_support::{ pallet_prelude::*, + traits::UnfilteredDispatchable, + dispatch::GetDispatchInfo, traits::{ fungible, fungible::{Inspect, Mutate, MutateHold}, @@ -186,14 +188,16 @@ pub struct VotingRoundInfo { pub round_ending_block: ProvidedBlockNumberFor, pub total_positive_votes_amount: BalanceOf, pub total_negative_votes_amount: BalanceOf, + pub batch_submitted: bool, } impl VotingRoundInfo { pub fn new() -> Self { let round_starting_block = T::BlockNumberProvider::current_block_number(); + let batch_submitted = false; let round_ending_block = round_starting_block .clone() - .saturating_add(T::VotingPeriod::get()); + .saturating_add(::VotingPeriod::get()); let round_number = NextVotingRoundNumber::::mutate(|n| { let res = *n; *n = n.saturating_add(1); res }); let total_positive_votes_amount = BalanceOf::::zero(); let total_negative_votes_amount = BalanceOf::::zero(); @@ -209,6 +213,7 @@ impl VotingRoundInfo { round_ending_block, total_positive_votes_amount, total_negative_votes_amount, + batch_submitted, }; VotingRounds::::insert(round_number, round_infos.clone()); round_infos From d3b9928eb18783120f549975a1a04ed34c5b5f20 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 7 Jan 2025 17:28:59 +0900 Subject: [PATCH 21/62] Referendums started on project batch submission --- substrate/frame/opf/src/lib.rs | 6 +++++- substrate/frame/opf/src/tests.rs | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 5342d66b486d..d5701080f36e 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -289,7 +289,11 @@ pub mod pallet { let call0: ::RuntimeCall = crate::Call::::on_registration {project_id: project_id.clone()}.into(); let call = ::RuntimeCall::convert(call0); let call_f = T::Preimages::bound(call).unwrap(); - Democracy::Pallet::::propose(origin.clone(), call_f, T::MinimumDeposit::get())?; + let call_hash = call_f.hash(); + let threshold = Democracy::VoteThreshold::SimpleMajority; + Democracy::Pallet::::propose(origin.clone(), call_f.clone(), T::MinimumDeposit::get())?; + Democracy::Pallet::::internal_start_referendum(call_f,threshold, Zero::zero()); + } Self::deposit_event(Event::Projectslisted { when, projects_id }); diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index dcd42430d054..18a3b29baad0 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -55,5 +55,8 @@ fn project_registration_works() { assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); let project_list = WhiteListedProjectAccounts::::get(BOB); assert!(project_list.is_some()); + // we should have 3 referendum started + assert_eq!(pallet_democracy::PublicProps::::get().len(), 3); + assert_eq!(pallet_democracy::ReferendumCount::::get(), 3); }) } From 1e317a52240b59e814731c2bc07e32d359271d05 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 9 Jan 2025 13:18:37 +0900 Subject: [PATCH 22/62] Remove unused variable --- substrate/frame/opf/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index d5701080f36e..9858033cb899 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -289,7 +289,6 @@ pub mod pallet { let call0: ::RuntimeCall = crate::Call::::on_registration {project_id: project_id.clone()}.into(); let call = ::RuntimeCall::convert(call0); let call_f = T::Preimages::bound(call).unwrap(); - let call_hash = call_f.hash(); let threshold = Democracy::VoteThreshold::SimpleMajority; Democracy::Pallet::::propose(origin.clone(), call_f.clone(), T::MinimumDeposit::get())?; Democracy::Pallet::::internal_start_referendum(call_f,threshold, Zero::zero()); From 6bc36f09fab3a8c2292ccbf79ae675132de5ef9c Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 11 Jan 2025 19:51:35 +0900 Subject: [PATCH 23/62] vote extrinsic --- substrate/frame/opf/src/functions.rs | 113 ++++++++++++++++++++- substrate/frame/opf/src/lib.rs | 146 ++++++++++++++++++--------- substrate/frame/opf/src/mock.rs | 10 +- substrate/frame/opf/src/tests.rs | 3 +- substrate/frame/opf/src/types.rs | 32 +++--- 5 files changed, 235 insertions(+), 69 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 863cddad927f..dccb2abc753c 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -18,6 +18,110 @@ pub use super::*; impl Pallet { + // Helper function for voting action. Existing votes are over-written, and Hold is adjusted + pub fn try_vote( + voter_id: VoterId, + project: ProjectId, + amount: BalanceOf, + is_fund: bool, + conviction: Democracy::Conviction, + ) -> DispatchResult { + if !ProjectFunds::::contains_key(&project) { + let bounded = BoundedVec::, ConstU32<2>>::try_from(vec![ + BalanceOf::::zero(), + BalanceOf::::zero(), + ]) + .expect("It works"); + ProjectFunds::::insert(&project, bounded); + } + + let projects = WhiteListedProjectAccounts::::get(project.clone()) + .ok_or(Error::::NoProjectAvailable); + let conviction_fund = amount.saturating_add( + amount.saturating_mul(>::from(conviction).into()), + ); + + // Create vote infos and store/adjust them + let round_number = NextVotingRoundNumber::::get().saturating_sub(1); + let mut round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; + if is_fund { + round.total_positive_votes_amount = + round.total_positive_votes_amount.saturating_add(conviction_fund); + } else { + round.total_negative_votes_amount = + round.total_negative_votes_amount.saturating_add(conviction_fund); + } + + VotingRounds::::mutate(round_number, |val| { + *val = Some(round.clone()); + }); + + let mut new_vote = VoteInfo { + amount, + round: round.clone(), + is_fund, + conviction, + funds_unlock_block: round.round_ending_block, + }; + + // Update Funds unlock block according to the selected conviction + new_vote.funds_unlock(); + if Votes::::contains_key(&project, &voter_id) { + let old_vote = Votes::::get(&project, &voter_id).ok_or(Error::::NoVoteData)?; + let old_amount = old_vote.amount; + let old_conviction = old_vote.conviction; + let old_conviction_amount = + old_amount.saturating_add(old_amount.saturating_mul( + >::from(old_conviction).into(), + )); + ProjectFunds::::mutate(&project, |val| { + let mut val0 = val.clone().into_inner(); + if is_fund { + val0[0] = val0[0 as usize] + .saturating_add(conviction_fund) + .saturating_sub(old_conviction_amount); + } else { + val0[1] = val0[1 as usize] + .saturating_add(conviction_fund) + .saturating_sub(old_conviction_amount); + } + *val = BoundedVec::, ConstU32<2>>::try_from(val0).expect("It works"); + }); + + Votes::::mutate(&project, &voter_id, |value| { + *value = Some(new_vote); + }); + + // Adjust locked amount + let total_hold = T::NativeBalance::total_balance_on_hold(&voter_id); + let new_hold = total_hold.saturating_sub(old_amount).saturating_add(amount); + T::NativeBalance::set_on_hold(&HoldReason::FundsReserved.into(), &voter_id, new_hold)?; + } else { + Votes::::insert(&project, &voter_id, new_vote); + ProjectFunds::::mutate(&project, |val| { + let mut val0 = val.clone().into_inner(); + if is_fund { + val0[0] = val0[0 as usize].saturating_add(conviction_fund); + } else { + val0[1] = val0[1 as usize].saturating_add(conviction_fund); + } + *val = BoundedVec::, ConstU32<2>>::try_from(val0).expect("It works"); + }); + // Lock the necessary amount + T::NativeBalance::hold(&HoldReason::FundsReserved.into(), &voter_id, amount)?; + } + /* + let ref_index = + ReferendumIndexLog::::get(&project).ok_or(Error::::NoProjectAvailable)?; + + let vote = Democracy::Vote { aye: is_fund, conviction }; + let account_vote = Democracy::AccountVote::Standard{ vote, balance: amount }; + + Democracy::Pallet::::vote(&voter_id, ref_index, vote)?;*/ + + Ok(()) + } + pub fn pot_account() -> AccountIdOf { // Get Pot account T::PotId::get().into_account_truncating() @@ -70,7 +174,9 @@ impl Pallet { // Reward calculation is executed within the Voting period pub fn calculate_rewards(total_reward: BalanceOf) -> DispatchResult { let projects: Vec> = WhiteListedProjectAccounts::::iter_keys().collect(); - if projects.is_empty() { return Ok(()) } + if projects.is_empty() { + return Ok(()) + } let round_number = NextVotingRoundNumber::::get().saturating_sub(1); let round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; if projects.clone().len() > 0 as usize { @@ -95,12 +201,16 @@ impl Pallet { let project_percentage = Percent::from_rational(project_reward, total_votes_amount); let final_amount = project_percentage * total_reward; + let infos = WhiteListedProjectAccounts::::get(&project_id) + .ok_or(Error::::NoProjectAvailable)?; + let ref_index = infos.index; // Send calculated reward for reward distribution let project_info = ProjectInfo { project_id: project_id.clone(), submission_block: when, amount: final_amount, + index: ref_index, }; // create a spend for project to be rewarded @@ -151,7 +261,6 @@ impl Pallet { // Conditions for reward distribution preparations are: // - We are at the end of voting_round period if now > round_ending_block { - // Clear ProjectFunds storage ProjectFunds::::drain(); // Emmit events diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 9858033cb899..69f6d40f6013 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -41,20 +41,20 @@ pub mod pallet { pub trait Config: frame_system::Config + Democracy::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type RuntimeCall: Convert<::RuntimeCall, ::RuntimeCall> - + Parameter - + UnfilteredDispatchable::RuntimeOrigin> - + From> - + GetDispatchInfo; + + Parameter + + UnfilteredDispatchable::RuntimeOrigin> + + From> + + GetDispatchInfo; /// The admin origin that can list and un-list whitelisted projects. type AdminOrigin: EnsureOrigin; /// Type to access the Balances Pallet. type NativeBalance: fungible::Inspect - + fungible::Mutate - + fungible::hold::Inspect - + fungible::hold::Mutate - + fungible::freeze::Inspect - + fungible::freeze::Mutate; + + fungible::Mutate + + fungible::hold::Inspect + + fungible::hold::Mutate + + fungible::freeze::Inspect + + fungible::freeze::Mutate; type RuntimeHoldReason: From; /// Provider for the block number. @@ -116,7 +116,7 @@ pub mod pallet { /// List of Whitelisted Project registered #[pallet::storage] pub type WhiteListedProjectAccounts = - CountedStorageMap<_, Twox64Concat, ProjectId, ProjectInfo, OptionQuery>; + CountedStorageMap<_, Twox64Concat, ProjectId, ProjectInfo, OptionQuery>; /// Returns (positive_funds,negative_funds) of Whitelisted Project accounts #[pallet::storage] @@ -128,6 +128,18 @@ pub mod pallet { ValueQuery, >; + /// Returns Votes Infos against (project_id, voter_id) key + #[pallet::storage] + pub type Votes = StorageDoubleMap< + _, + Blake2_128Concat, + ProjectId, + Twox64Concat, + VoterId, + VoteInfo, + OptionQuery, + >; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -224,7 +236,9 @@ pub mod pallet { /// Another project has already been submitted under the same project_id SubmittedProjectId, /// Project batch already submitted - BatchAlreadySubmitted + BatchAlreadySubmitted, + NoVoteData, + NotEnoughFunds, } #[pallet::hooks] @@ -236,7 +250,6 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// OPF Projects registration /// /// ## Dispatch Origin @@ -256,20 +269,23 @@ pub mod pallet { /// ## Events /// Emits [`Event::::Projectslisted`]. #[pallet::call_index(1)] - pub fn register_projects_batch(origin: OriginFor, projects_id: Vec>) -> DispatchResult { + pub fn register_projects_batch( + origin: OriginFor, + projects_id: Vec>, + ) -> DispatchResult { //T::AdminOrigin::ensure_origin_or_root(origin.clone())?; let who = T::SubmitOrigin::ensure_origin(origin.clone())?; // Only 1 batch submission per round let mut round_index = NextVotingRoundNumber::::get(); - // No active round? - if round_index == 0 { - // Start the first voting round - let _round0 = VotingRoundInfo::::new(); - round_index = NextVotingRoundNumber::::get(); - } + // No active round? + if round_index == 0 { + // Start the first voting round + let _round0 = VotingRoundInfo::::new(); + round_index = NextVotingRoundNumber::::get(); + } - let current_round_index = round_index.saturating_sub(1); + let current_round_index = round_index.saturating_sub(1); let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); // Check no Project batch has been submitted yet @@ -280,21 +296,36 @@ pub mod pallet { let when = T::BlockNumberProvider::current_block_number(); if when >= round_ending_block { // Create a new round. - let _new_round = VotingRoundInfo::::new(); + let _new_round = VotingRoundInfo::::new(); } - for project_id in &projects_id{ + for project_id in &projects_id { ProjectInfo::::new(project_id.clone()); // Prepare the proposal call - let call0: ::RuntimeCall = crate::Call::::on_registration {project_id: project_id.clone()}.into(); + let call0: ::RuntimeCall = + crate::Call::::on_registration { project_id: project_id.clone() }.into(); let call = ::RuntimeCall::convert(call0); - let call_f = T::Preimages::bound(call).unwrap(); + let call_f = T::Preimages::bound(call)?; let threshold = Democracy::VoteThreshold::SimpleMajority; - Democracy::Pallet::::propose(origin.clone(), call_f.clone(), T::MinimumDeposit::get())?; - Democracy::Pallet::::internal_start_referendum(call_f,threshold, Zero::zero()); - + Democracy::Pallet::::propose( + origin.clone(), + call_f.clone(), + T::MinimumDeposit::get(), + )?; + let referendum_index = Democracy::Pallet::::internal_start_referendum( + call_f, + threshold, + Zero::zero(), + ); + let mut new_infos = WhiteListedProjectAccounts::::get(&project_id) + .ok_or(Error::::NoProjectAvailable)?; + new_infos.index = referendum_index; + + WhiteListedProjectAccounts::::mutate(project_id, |value| { + *value = Some(new_infos); + }); } - + Self::deposit_event(Event::Projectslisted { when, projects_id }); Ok(()) } @@ -333,7 +364,33 @@ pub mod pallet { #[pallet::call_index(3)] #[transactional] - pub fn vote(origin: OriginFor) -> DispatchResult { + pub fn vote( + origin: OriginFor, + project_id: ProjectId, + #[pallet::compact] amount: BalanceOf, + is_fund: bool, + conviction: Democracy::Conviction, + ) -> DispatchResult { + let voter = ensure_signed(origin.clone())?; + // Get current voting round & check if we are in voting period or not + Self::period_check()?; + // Check that voter has enough funds to vote + let voter_balance = T::NativeBalance::total_balance(&voter); + ensure!(voter_balance > amount, Error::::NotEnoughFunds); + + // Check the available un-holded balance + let voter_holds = T::NativeBalance::total_balance_on_hold(&voter); + let available_funds = voter_balance.saturating_sub(voter_holds); + ensure!(available_funds > amount, Error::::NotEnoughFunds); + + let infos = WhiteListedProjectAccounts::::get(&project_id) + .ok_or(Error::::NoProjectAvailable)?; + let ref_index = infos.index; + let vote = Democracy::Vote { aye: is_fund, conviction }; + // let account_vote = Democracy::AccountVote::Standard { vote, balance: amount }; + + // Democracy::Pallet::::vote(origin, ref_index, account_vote)?; + Ok(()) } @@ -370,23 +427,20 @@ pub mod pallet { let now = T::BlockNumberProvider::current_block_number(); let info = Spends::::get(&project_id).ok_or(Error::::InexistentSpend)?; if now >= info.expire { - Spends::::remove(&project_id); - Self::deposit_event(Event::ExpiredClaim { - expired_when: info.expire, - project_id, - }); - Ok(()) - } else if now < info.expire { - // transfer the funds - Self::spend(info.amount, project_id.clone())?; - Self::deposit_event(Event::RewardClaimed { - when: now, - amount: info.amount, - project_id: project_id.clone(), - }); - Self::unlist_project(project_id)?; - Ok(()) - } else { + Spends::::remove(&project_id); + Self::deposit_event(Event::ExpiredClaim { expired_when: info.expire, project_id }); + Ok(()) + } else if now < info.expire { + // transfer the funds + Self::spend(info.amount, project_id.clone())?; + Self::deposit_event(Event::RewardClaimed { + when: now, + amount: info.amount, + project_id: project_id.clone(), + }); + Self::unlist_project(project_id)?; + Ok(()) + } else { Err(DispatchError::Other("Not Claiming Period")) } } diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index e6bbb19cb443..1d10d9d741f1 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -17,14 +17,15 @@ //! Test environment for OPF pallet. use crate as pallet_opf; -use codec::{Decode, Encode, MaxEncodedLen}; use crate::Convert; +use codec::{Decode, Encode, MaxEncodedLen}; pub use frame_support::{ derive_impl, ord_parameter_types, pallet_prelude::TypeInfo, parameter_types, traits::{ - ConstU32, ConstU64, SortedMembers, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, VoteTally, + ConstU32, ConstU64, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, + SortedMembers, VoteTally, }, weights::Weight, PalletId, @@ -38,7 +39,6 @@ pub type Block = frame_system::mocking::MockBlock; pub type Balance = u64; pub type AccountId = u64; - // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub struct Test { @@ -169,9 +169,9 @@ impl Convert for RuntimeCall { fn convert(call: RuntimeCall) -> RuntimeCall { let call_encoded: Vec = call.encode(); let ref_call_encoded = &call_encoded; - if let Ok(call_formatted) = RuntimeCall::decode(&mut &ref_call_encoded[..]){ + if let Ok(call_formatted) = RuntimeCall::decode(&mut &ref_call_encoded[..]) { call_formatted - } else{ + } else { call } } diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 18a3b29baad0..8a2fd819f1b4 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -32,9 +32,8 @@ pub fn next_block() { ); } -pub fn project_list() -> Vec>{ +pub fn project_list() -> Vec> { vec![ALICE, BOB, DAVE] - } pub fn run_to_block(n: BlockNumberFor) { diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index ae6ac1581654..906288f48e42 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -20,12 +20,11 @@ pub use super::*; pub use frame_support::{ - pallet_prelude::*, - traits::UnfilteredDispatchable, dispatch::GetDispatchInfo, + pallet_prelude::*, traits::{ fungible, - fungible::{Inspect, Mutate, MutateHold}, + fungible::{Inspect, InspectHold, Mutate, MutateHold}, fungibles, schedule::{ v3::{Anon as ScheduleAnon, Named as ScheduleNamed}, @@ -33,14 +32,13 @@ pub use frame_support::{ }, tokens::{Precision, Preservation}, Bounded, DefensiveOption, EnsureOrigin, LockIdentifier, OriginTrait, QueryPreimage, - StorePreimage, + StorePreimage, UnfilteredDispatchable, }, transactional, weights::WeightMeter, PalletId, Serialize, }; pub use frame_system::{pallet_prelude::*, RawOrigin}; -pub use pallet_conviction_voting::Conviction; pub use scale_info::prelude::vec::Vec; pub use sp_runtime::{ traits::{ @@ -125,16 +123,19 @@ pub struct ProjectInfo { /// Amount to be locked & payed for this project pub amount: BalanceOf, + + /// Referendum Index + pub index: u32, } impl ProjectInfo { pub fn new(project_id: ProjectId) { let submission_block = T::BlockNumberProvider::current_block_number(); let amount = Zero::zero(); - let project_info = ProjectInfo { project_id: project_id.clone(), submission_block, amount }; + let project_info = + ProjectInfo { project_id: project_id.clone(), submission_block, amount, index: 0 }; WhiteListedProjectAccounts::::insert(project_id, project_info); } - } #[derive(Encode, Decode, Clone, PartialEq, MaxEncodedLen, RuntimeDebug, TypeInfo)] @@ -149,7 +150,7 @@ pub struct VoteInfo { /// Whether the vote is "fund" / "not fund" pub is_fund: bool, - pub conviction: Conviction, + pub conviction: Democracy::Conviction, pub funds_unlock_block: ProvidedBlockNumberFor, } @@ -157,7 +158,7 @@ pub struct VoteInfo { // If no conviction, user's funds are released at the end of the voting round impl VoteInfo { pub fn funds_unlock(&mut self) { - let conviction_coeff = >::from(self.conviction); + let conviction_coeff = >::from(self.conviction); let funds_unlock_block = self .round .round_ending_block @@ -173,7 +174,7 @@ impl Default for VoteInfo { let round = VotingRounds::::get(0).expect("Round 0 exists"); let amount = Zero::zero(); let is_fund = false; - let conviction = Conviction::None; + let conviction = Democracy::Conviction::None; let funds_unlock_block = round.round_ending_block; VoteInfo { amount, round, is_fund, conviction, funds_unlock_block } } @@ -195,10 +196,13 @@ impl VotingRoundInfo { pub fn new() -> Self { let round_starting_block = T::BlockNumberProvider::current_block_number(); let batch_submitted = false; - let round_ending_block = round_starting_block - .clone() - .saturating_add(::VotingPeriod::get()); - let round_number = NextVotingRoundNumber::::mutate(|n| { let res = *n; *n = n.saturating_add(1); res }); + let round_ending_block = + round_starting_block.clone().saturating_add(::VotingPeriod::get()); + let round_number = NextVotingRoundNumber::::mutate(|n| { + let res = *n; + *n = n.saturating_add(1); + res + }); let total_positive_votes_amount = BalanceOf::::zero(); let total_negative_votes_amount = BalanceOf::::zero(); From e8a8f90dff968e30102c42e07524b94379ff276d Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 11 Jan 2025 21:13:44 +0900 Subject: [PATCH 24/62] Vote extrinsic --- substrate/frame/opf/src/functions.rs | 11 +++++++++++ substrate/frame/opf/src/lib.rs | 5 +++-- substrate/frame/opf/src/types.rs | 13 ++++++++++--- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index dccb2abc753c..b32cc978aa9f 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -127,6 +127,17 @@ impl Pallet { T::PotId::get().into_account_truncating() } + pub fn convert_balance(amount: BalanceOf) -> Option> { + let value = match TryInto::::try_into(amount) { + Ok(val) => { + TryInto::>::try_into(val).ok() + }, + Err(e) => None + }; + + value + + } /// Funds transfer from the Pot to a project account pub fn spend(amount: BalanceOf, beneficiary: AccountIdOf) -> DispatchResult { // Get Pot account diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 69f6d40f6013..e1fcfd4301f5 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -387,9 +387,10 @@ pub mod pallet { .ok_or(Error::::NoProjectAvailable)?; let ref_index = infos.index; let vote = Democracy::Vote { aye: is_fund, conviction }; - // let account_vote = Democracy::AccountVote::Standard { vote, balance: amount }; + let converted_amount = Self::convert_balance(amount).ok_or("Failed Conversion!!!")?; + let account_vote = Democracy::AccountVote::Standard { vote, balance: converted_amount }; - // Democracy::Pallet::::vote(origin, ref_index, account_vote)?; + Democracy::Pallet::::vote(origin, ref_index, account_vote)?; Ok(()) } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 906288f48e42..bc53b66ba21e 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -23,7 +23,7 @@ pub use frame_support::{ dispatch::GetDispatchInfo, pallet_prelude::*, traits::{ - fungible, + fungible, Currency, fungible::{Inspect, InspectHold, Mutate, MutateHold}, fungibles, schedule::{ @@ -35,7 +35,7 @@ pub use frame_support::{ StorePreimage, UnfilteredDispatchable, }, transactional, - weights::WeightMeter, + weights::{WeightMeter, WeightToFee}, PalletId, Serialize, }; pub use frame_system::{pallet_prelude::*, RawOrigin}; @@ -45,13 +45,16 @@ pub use sp_runtime::{ AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, Zero, }, - Percent, + Percent, SaturatedConversion, }; pub use sp_std::boxed::Box; pub type BalanceOf = <::NativeBalance as fungible::Inspect< ::AccountId, >>::Balance; + +pub type BalanceOfD = <::Currency as Currency<::AccountId>>::Balance; + pub type AccountIdOf = ::AccountId; /// A reward index. pub type SpendIndex = u32; @@ -223,3 +226,7 @@ impl VotingRoundInfo { round_infos } } + + + + From 1b1d4d6452de00abd630532128e59e488c9a6ef6 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 11 Jan 2025 21:14:20 +0900 Subject: [PATCH 25/62] cargo fmt --- substrate/frame/opf/src/functions.rs | 7 ++----- substrate/frame/opf/src/types.rs | 14 ++++++-------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index b32cc978aa9f..9c5b750dab2a 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -129,14 +129,11 @@ impl Pallet { pub fn convert_balance(amount: BalanceOf) -> Option> { let value = match TryInto::::try_into(amount) { - Ok(val) => { - TryInto::>::try_into(val).ok() - }, - Err(e) => None + Ok(val) => TryInto::>::try_into(val).ok(), + Err(e) => None, }; value - } /// Funds transfer from the Pot to a project account pub fn spend(amount: BalanceOf, beneficiary: AccountIdOf) -> DispatchResult { diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index bc53b66ba21e..eb2043d6681f 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -23,7 +23,7 @@ pub use frame_support::{ dispatch::GetDispatchInfo, pallet_prelude::*, traits::{ - fungible, Currency, + fungible, fungible::{Inspect, InspectHold, Mutate, MutateHold}, fungibles, schedule::{ @@ -31,8 +31,8 @@ pub use frame_support::{ DispatchTime, MaybeHashed, }, tokens::{Precision, Preservation}, - Bounded, DefensiveOption, EnsureOrigin, LockIdentifier, OriginTrait, QueryPreimage, - StorePreimage, UnfilteredDispatchable, + Bounded, Currency, DefensiveOption, EnsureOrigin, LockIdentifier, OriginTrait, + QueryPreimage, StorePreimage, UnfilteredDispatchable, }, transactional, weights::{WeightMeter, WeightToFee}, @@ -53,7 +53,9 @@ pub type BalanceOf = <::NativeBalance as fungible::Inspect< ::AccountId, >>::Balance; -pub type BalanceOfD = <::Currency as Currency<::AccountId>>::Balance; +pub type BalanceOfD = <::Currency as Currency< + ::AccountId, +>>::Balance; pub type AccountIdOf = ::AccountId; /// A reward index. @@ -226,7 +228,3 @@ impl VotingRoundInfo { round_infos } } - - - - From 54eb17795478f976ceeedb15e681c0fd96c1caed Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 11 Jan 2025 21:23:21 +0900 Subject: [PATCH 26/62] Funds are locked by Democracy Pallet --- substrate/frame/opf/src/functions.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 9c5b750dab2a..5ca5088a90c5 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -65,7 +65,7 @@ impl Pallet { }; // Update Funds unlock block according to the selected conviction - new_vote.funds_unlock(); + //new_vote.funds_unlock(); if Votes::::contains_key(&project, &voter_id) { let old_vote = Votes::::get(&project, &voter_id).ok_or(Error::::NoVoteData)?; let old_amount = old_vote.amount; @@ -93,9 +93,9 @@ impl Pallet { }); // Adjust locked amount - let total_hold = T::NativeBalance::total_balance_on_hold(&voter_id); + /*let total_hold = T::NativeBalance::total_balance_on_hold(&voter_id); let new_hold = total_hold.saturating_sub(old_amount).saturating_add(amount); - T::NativeBalance::set_on_hold(&HoldReason::FundsReserved.into(), &voter_id, new_hold)?; + T::NativeBalance::set_on_hold(&HoldReason::FundsReserved.into(), &voter_id, new_hold)?;*/ } else { Votes::::insert(&project, &voter_id, new_vote); ProjectFunds::::mutate(&project, |val| { @@ -108,7 +108,7 @@ impl Pallet { *val = BoundedVec::, ConstU32<2>>::try_from(val0).expect("It works"); }); // Lock the necessary amount - T::NativeBalance::hold(&HoldReason::FundsReserved.into(), &voter_id, amount)?; + // T::NativeBalance::hold(&HoldReason::FundsReserved.into(), &voter_id, amount)?; } /* let ref_index = From 43147c97dbe3e4667bbf9304ac08167cf54b1293 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 11 Jan 2025 21:56:04 +0900 Subject: [PATCH 27/62] Removed some warnings --- substrate/frame/opf/src/functions.rs | 27 +++++++++------------------ substrate/frame/opf/src/lib.rs | 9 +++++---- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 5ca5088a90c5..8dae181c595b 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -26,6 +26,7 @@ impl Pallet { is_fund: bool, conviction: Democracy::Conviction, ) -> DispatchResult { + let origin = T::RuntimeOrigin::from(RawOrigin::Signed(voter_id.clone())); if !ProjectFunds::::contains_key(&project) { let bounded = BoundedVec::, ConstU32<2>>::try_from(vec![ BalanceOf::::zero(), @@ -35,8 +36,10 @@ impl Pallet { ProjectFunds::::insert(&project, bounded); } - let projects = WhiteListedProjectAccounts::::get(project.clone()) - .ok_or(Error::::NoProjectAvailable); + let infos = WhiteListedProjectAccounts::::get(project.clone()) + .ok_or(Error::::NoProjectAvailable)?; + let ref_index = infos.index; + let conviction_fund = amount.saturating_add( amount.saturating_mul(>::from(conviction).into()), ); @@ -56,7 +59,7 @@ impl Pallet { *val = Some(round.clone()); }); - let mut new_vote = VoteInfo { + let new_vote = VoteInfo { amount, round: round.clone(), is_fund, @@ -64,8 +67,6 @@ impl Pallet { funds_unlock_block: round.round_ending_block, }; - // Update Funds unlock block according to the selected conviction - //new_vote.funds_unlock(); if Votes::::contains_key(&project, &voter_id) { let old_vote = Votes::::get(&project, &voter_id).ok_or(Error::::NoVoteData)?; let old_amount = old_vote.amount; @@ -92,10 +93,8 @@ impl Pallet { *value = Some(new_vote); }); - // Adjust locked amount - /*let total_hold = T::NativeBalance::total_balance_on_hold(&voter_id); - let new_hold = total_hold.saturating_sub(old_amount).saturating_add(amount); - T::NativeBalance::set_on_hold(&HoldReason::FundsReserved.into(), &voter_id, new_hold)?;*/ + // Remove previous vote from Referendum + Democracy::Pallet::::remove_vote(origin, ref_index)?; } else { Votes::::insert(&project, &voter_id, new_vote); ProjectFunds::::mutate(&project, |val| { @@ -110,14 +109,6 @@ impl Pallet { // Lock the necessary amount // T::NativeBalance::hold(&HoldReason::FundsReserved.into(), &voter_id, amount)?; } - /* - let ref_index = - ReferendumIndexLog::::get(&project).ok_or(Error::::NoProjectAvailable)?; - - let vote = Democracy::Vote { aye: is_fund, conviction }; - let account_vote = Democracy::AccountVote::Standard{ vote, balance: amount }; - - Democracy::Pallet::::vote(&voter_id, ref_index, vote)?;*/ Ok(()) } @@ -130,7 +121,7 @@ impl Pallet { pub fn convert_balance(amount: BalanceOf) -> Option> { let value = match TryInto::::try_into(amount) { Ok(val) => TryInto::>::try_into(val).ok(), - Err(e) => None, + Err(_) => None, }; value diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index e1fcfd4301f5..8c20548f380d 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -274,7 +274,7 @@ pub mod pallet { projects_id: Vec>, ) -> DispatchResult { //T::AdminOrigin::ensure_origin_or_root(origin.clone())?; - let who = T::SubmitOrigin::ensure_origin(origin.clone())?; + let _who = T::SubmitOrigin::ensure_origin(origin.clone())?; // Only 1 batch submission per round let mut round_index = NextVotingRoundNumber::::get(); @@ -302,8 +302,7 @@ pub mod pallet { for project_id in &projects_id { ProjectInfo::::new(project_id.clone()); // Prepare the proposal call - let call0: ::RuntimeCall = - crate::Call::::on_registration { project_id: project_id.clone() }.into(); + let call0: ::RuntimeCall = crate::Call::::on_registration {}.into(); let call = ::RuntimeCall::convert(call0); let call_f = T::Preimages::bound(call)?; let threshold = Democracy::VoteThreshold::SimpleMajority; @@ -448,10 +447,12 @@ pub mod pallet { #[pallet::call_index(6)] #[transactional] - pub fn on_registration(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + pub fn on_registration(origin: OriginFor) -> DispatchResult { + let _who = T::SubmitOrigin::ensure_origin(origin.clone())?; // prepare reward distribution // for now we are using the temporary-constant reward. let _ = Self::calculate_rewards(T::TemporaryRewards::get())?; + Ok(()) } } From 65bf165aabbac59f29a9097132579eedd5c97172 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 13 Jan 2025 15:05:40 +0900 Subject: [PATCH 28/62] Voting action tested -> works --- substrate/frame/opf/src/functions.rs | 11 +++++++++-- substrate/frame/opf/src/lib.rs | 8 ++++++-- substrate/frame/opf/src/tests.rs | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 8dae181c595b..f064ff7f55ff 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -59,7 +59,7 @@ impl Pallet { *val = Some(round.clone()); }); - let new_vote = VoteInfo { + let mut new_vote = VoteInfo { amount, round: round.clone(), is_fund, @@ -67,6 +67,8 @@ impl Pallet { funds_unlock_block: round.round_ending_block, }; + // Update Funds unlock block according to the selected conviction + new_vote.funds_unlock(); if Votes::::contains_key(&project, &voter_id) { let old_vote = Votes::::get(&project, &voter_id).ok_or(Error::::NoVoteData)?; let old_amount = old_vote.amount; @@ -93,6 +95,11 @@ impl Pallet { *value = Some(new_vote); }); + // Adjust locked amount + let total_hold = T::NativeBalance::total_balance_on_hold(&voter_id); + let new_hold = total_hold.saturating_sub(old_amount).saturating_add(amount); + T::NativeBalance::set_on_hold(&HoldReason::FundsReserved.into(), &voter_id, new_hold)?; + // Remove previous vote from Referendum Democracy::Pallet::::remove_vote(origin, ref_index)?; } else { @@ -107,7 +114,7 @@ impl Pallet { *val = BoundedVec::, ConstU32<2>>::try_from(val0).expect("It works"); }); // Lock the necessary amount - // T::NativeBalance::hold(&HoldReason::FundsReserved.into(), &voter_id, amount)?; + T::NativeBalance::hold(&HoldReason::FundsReserved.into(), &voter_id, amount)?; } Ok(()) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 8c20548f380d..51c110d451be 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -385,10 +385,14 @@ pub mod pallet { let infos = WhiteListedProjectAccounts::::get(&project_id) .ok_or(Error::::NoProjectAvailable)?; let ref_index = infos.index; - let vote = Democracy::Vote { aye: is_fund, conviction }; - let converted_amount = Self::convert_balance(amount).ok_or("Failed Conversion!!!")?; + + // Funds lock is handled by the opf pallet + let conv = Democracy::Conviction::None; + let vote = Democracy::Vote { aye: is_fund, conviction: conv }; + let converted_amount = Self::convert_balance(amount.clone()).ok_or("Failed Conversion!!!")?; let account_vote = Democracy::AccountVote::Standard { vote, balance: converted_amount }; + Self::try_vote(voter, project_id, amount, is_fund, conviction )?; Democracy::Pallet::::vote(origin, ref_index, account_vote)?; Ok(()) diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 8a2fd819f1b4..e8eb4caea703 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -59,3 +59,25 @@ fn project_registration_works() { assert_eq!(pallet_democracy::ReferendumCount::::get(), 3); }) } + +#[test] +fn vote_works(){ + new_test_ext().execute_with(|| { + let batch = project_list(); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + // Bob vote for Alice + assert_ok!(Opf::vote(RuntimeOrigin::signed(BOB), ALICE, 100, true,pallet_democracy::Conviction::Locked1x)); + // Dave vote for Alice + assert_ok!(Opf::vote(RuntimeOrigin::signed(DAVE), ALICE, 100, true,pallet_democracy::Conviction::Locked2x)); + //Round number is 0 + let round_number = NextVotingRoundNumber::::get().saturating_sub(1); + assert_eq!(round_number, 0); + + //Bobs funds are locked + let bob_hold = ::NativeBalance::total_balance_on_hold(&BOB); + let dave_hold = ::NativeBalance::total_balance_on_hold(&DAVE); + assert_eq!(bob_hold, 100); + assert_eq!(dave_hold, 100); + + }) +} From cce03048f3182cf12156351cc854976e206d328f Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 13 Jan 2025 15:13:39 +0900 Subject: [PATCH 29/62] clippy & format --- substrate/frame/opf/src/lib.rs | 11 ++++++----- substrate/frame/opf/src/tests.rs | 19 +++++++++++++++---- substrate/frame/opf/src/types.rs | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 51c110d451be..718fe9dbb4ae 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -289,7 +289,7 @@ pub mod pallet { let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); // Check no Project batch has been submitted yet - ensure!(round_infos.batch_submitted == false, Error::::BatchAlreadySubmitted); + ensure!(!round_infos.batch_submitted, Error::::BatchAlreadySubmitted); let round_ending_block = round_infos.round_ending_block; // If current voting round is over, start a new one @@ -385,14 +385,15 @@ pub mod pallet { let infos = WhiteListedProjectAccounts::::get(&project_id) .ok_or(Error::::NoProjectAvailable)?; let ref_index = infos.index; - - // Funds lock is handled by the opf pallet + + // Funds lock is handled by the opf pallet let conv = Democracy::Conviction::None; let vote = Democracy::Vote { aye: is_fund, conviction: conv }; - let converted_amount = Self::convert_balance(amount.clone()).ok_or("Failed Conversion!!!")?; + let converted_amount = + Self::convert_balance(amount).ok_or("Failed Conversion!!!")?; let account_vote = Democracy::AccountVote::Standard { vote, balance: converted_amount }; - Self::try_vote(voter, project_id, amount, is_fund, conviction )?; + Self::try_vote(voter, project_id, amount, is_fund, conviction)?; Democracy::Pallet::::vote(origin, ref_index, account_vote)?; Ok(()) diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index e8eb4caea703..c18d9450138a 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -61,14 +61,26 @@ fn project_registration_works() { } #[test] -fn vote_works(){ +fn vote_works() { new_test_ext().execute_with(|| { let batch = project_list(); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); // Bob vote for Alice - assert_ok!(Opf::vote(RuntimeOrigin::signed(BOB), ALICE, 100, true,pallet_democracy::Conviction::Locked1x)); + assert_ok!(Opf::vote( + RuntimeOrigin::signed(BOB), + ALICE, + 100, + true, + pallet_democracy::Conviction::Locked1x + )); // Dave vote for Alice - assert_ok!(Opf::vote(RuntimeOrigin::signed(DAVE), ALICE, 100, true,pallet_democracy::Conviction::Locked2x)); + assert_ok!(Opf::vote( + RuntimeOrigin::signed(DAVE), + ALICE, + 100, + true, + pallet_democracy::Conviction::Locked2x + )); //Round number is 0 let round_number = NextVotingRoundNumber::::get().saturating_sub(1); assert_eq!(round_number, 0); @@ -78,6 +90,5 @@ fn vote_works(){ let dave_hold = ::NativeBalance::total_balance_on_hold(&DAVE); assert_eq!(bob_hold, 100); assert_eq!(dave_hold, 100); - }) } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index eb2043d6681f..3e3ad904f40f 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -202,7 +202,7 @@ impl VotingRoundInfo { let round_starting_block = T::BlockNumberProvider::current_block_number(); let batch_submitted = false; let round_ending_block = - round_starting_block.clone().saturating_add(::VotingPeriod::get()); + round_starting_block.saturating_add(::VotingPeriod::get()); let round_number = NextVotingRoundNumber::::mutate(|n| { let res = *n; *n = n.saturating_add(1); From 2c047a426dd5e4e7e1d31d98017e28ea173eeb7a Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 13 Jan 2025 15:34:04 +0900 Subject: [PATCH 30/62] Vote_removal function added --- substrate/frame/opf/src/functions.rs | 49 ++++++++++++++++++++++++++-- substrate/frame/opf/src/lib.rs | 22 ++++++++++--- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index f064ff7f55ff..5c9e9047b922 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -170,9 +170,54 @@ impl Pallet { Ok(()) } - pub fn unlist_project(project_id: ProjectId) -> DispatchResult { - WhiteListedProjectAccounts::::remove(&project_id); + // Helper function for complete vote data removal from storage. + pub fn try_remove_vote(voter_id: VoterId, project: ProjectId) -> DispatchResult { + if Votes::::contains_key(&project, &voter_id) { + let infos = Votes::::get(&project, &voter_id).ok_or(Error::::NoVoteData)?; + let amount = infos.amount; + let conviction = infos.conviction; + let is_fund = infos.is_fund; + + let conviction_fund = amount.saturating_add( + amount.saturating_mul(>::from(conviction).into()), + ); + + // Update Round infos + let round_number = NextVotingRoundNumber::::get().saturating_sub(1); + let mut round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; + if is_fund { + round.total_positive_votes_amount = + round.total_positive_votes_amount.saturating_sub(conviction_fund); + } else { + round.total_negative_votes_amount = + round.total_negative_votes_amount.saturating_sub(conviction_fund); + } + + VotingRounds::::mutate(round_number, |val| { + *val = Some(round.clone()); + }); + + // Update ProjectFund Storage + ProjectFunds::::mutate(&project, |val| { + let mut val0 = val.clone().into_inner(); + if is_fund { + val0[0] = val0[0 as usize].saturating_sub(conviction_fund); + } else { + val0[1] = val0[1 as usize].saturating_sub(conviction_fund); + } + *val = BoundedVec::, ConstU32<2>>::try_from(val0).expect("It works"); + }); + + // Remove Vote Infos + Votes::::remove(&project, &voter_id); + T::NativeBalance::release( + &HoldReason::FundsReserved.into(), + &voter_id, + amount, + Precision::Exact, + )?; + } Ok(()) } diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 718fe9dbb4ae..a4304353b6c7 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -355,7 +355,7 @@ pub mod pallet { ) -> DispatchResult { T::AdminOrigin::ensure_origin_or_root(origin)?; let when = T::BlockNumberProvider::current_block_number(); - Self::unlist_project(project_id.clone())?; + WhiteListedProjectAccounts::::remove(&project_id); Self::deposit_event(Event::::ProjectUnlisted { when, project_id }); Ok(()) @@ -389,8 +389,7 @@ pub mod pallet { // Funds lock is handled by the opf pallet let conv = Democracy::Conviction::None; let vote = Democracy::Vote { aye: is_fund, conviction: conv }; - let converted_amount = - Self::convert_balance(amount).ok_or("Failed Conversion!!!")?; + let converted_amount = Self::convert_balance(amount).ok_or("Failed Conversion!!!")?; let account_vote = Democracy::AccountVote::Standard { vote, balance: converted_amount }; Self::try_vote(voter, project_id, amount, is_fund, conviction)?; @@ -401,7 +400,20 @@ pub mod pallet { #[pallet::call_index(4)] #[transactional] - pub fn remove_vote(origin: OriginFor) -> DispatchResult { + pub fn remove_vote(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let voter = ensure_signed(origin.clone())?; + // Get current voting round & check if we are in voting period or not + Self::period_check()?; + // Removal action executed + Self::try_remove_vote(voter.clone(), project_id.clone())?; + // Remove previous vote from Referendum + let infos = WhiteListedProjectAccounts::::get(project_id.clone()) + .ok_or(Error::::NoProjectAvailable)?; + let ref_index = infos.index; + Democracy::Pallet::::remove_vote(origin, ref_index)?; + + let when = T::BlockNumberProvider::current_block_number(); + Self::deposit_event(Event::::VoteRemoved { who: voter, when, project_id }); Ok(()) } @@ -443,7 +455,7 @@ pub mod pallet { amount: info.amount, project_id: project_id.clone(), }); - Self::unlist_project(project_id)?; + WhiteListedProjectAccounts::::remove(&project_id); Ok(()) } else { Err(DispatchError::Other("Not Claiming Period")) From 6d6254a0ef9c63d3a0d0372564a53313ba043da9 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 16 Jan 2025 16:34:39 +0900 Subject: [PATCH 31/62] added some tests --- substrate/frame/opf/src/lib.rs | 35 +++++++++++++++++- substrate/frame/opf/src/mock.rs | 4 +- substrate/frame/opf/src/tests.rs | 63 +++++++++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index a4304353b6c7..252648b9fc0d 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -221,6 +221,8 @@ pub mod pallet { FailedSpendOperation, /// Still not in claiming period NotClaimingPeriod, + /// Still not in funds unlock period + NotUnlockPeriod, /// Funds locking failed FundsReserveFailed, /// An invalid result was returned @@ -237,7 +239,9 @@ pub mod pallet { SubmittedProjectId, /// Project batch already submitted BatchAlreadySubmitted, + /// Requested vote data do not exist NoVoteData, + /// Not enough funds to process the transaction NotEnoughFunds, } @@ -287,9 +291,12 @@ pub mod pallet { let current_round_index = round_index.saturating_sub(1); - let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); + let mut round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); + + // Check no Project batch has been submitted yet ensure!(!round_infos.batch_submitted, Error::::BatchAlreadySubmitted); + round_infos.batch_submitted = true; let round_ending_block = round_infos.round_ending_block; // If current voting round is over, start a new one @@ -324,10 +331,14 @@ pub mod pallet { *value = Some(new_infos); }); } + VotingRounds::::mutate(current_round_index, |round|{ + *round = Some(round_infos) + }); Self::deposit_event(Event::Projectslisted { when, projects_id }); Ok(()) } + /// OPF Projects de-listing /// @@ -472,5 +483,27 @@ pub mod pallet { Ok(()) } + + #[pallet::call_index(7)] + #[transactional] + pub fn release_voter_funds(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + let voter_id = ensure_signed(origin)?; + ensure!(Votes::::contains_key(&project_id, &voter_id), Error::::NoVoteData); + let infos = Votes::::get(&project_id, &voter_id).ok_or( Error::::NoVoteData)?; + let release_block = infos.funds_unlock_block; + let amount = infos.amount; + + let now = T::BlockNumberProvider::current_block_number(); + ensure!(now>=release_block, Error::::NotUnlockPeriod); + T::NativeBalance::release( + &HoldReason::FundsReserved.into(), + &voter_id, + amount, + Precision::Exact, + )?; + + Votes::::remove(&project_id, &voter_id); + Ok(()) + } } } diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 1d10d9d741f1..6e0ce61cd99a 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -146,8 +146,8 @@ parameter_types! { pub const PotId: PalletId = PalletId(*b"py/potid"); pub const MaxProjects:u32 = 50; pub const TemporaryRewards: Balance = 100_000; - pub const VoteLockingPeriod:u32 = 10; - pub const VotingPeriod:u32 = 30; + pub const VoteLockingPeriod:u64 = 10; + pub const VotingPeriod:u64 = 30; } impl pallet_opf::Config for Test { type RuntimeEvent = RuntimeEvent; diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index c18d9450138a..3d62f4be2d1a 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -47,23 +47,73 @@ pub fn run_to_block(n: BlockNumberFor) { } } + +#[test] +fn first_round_creation_works() { + new_test_ext().execute_with(|| { + + let batch = project_list(); + + // First round is created + next_block(); + let voting_period = ::VotingPeriod::get(); + let now = + ::BlockNumberProvider::current_block_number(); + + let round_ending_block = now.saturating_add(voting_period.into()); + + let first_round_info: VotingRoundInfo = VotingRoundInfo { + round_number: 0, + round_starting_block: now, + round_ending_block, + total_positive_votes_amount: 0, + total_negative_votes_amount: 0, + batch_submitted: false, + }; + + // The righ event was emitted + expect_events(vec![RuntimeEvent::Opf(Event::VotingRoundStarted { + when: now, + round_number: 0, + })]); + + // The storage infos are correct + let round_info = VotingRounds::::get(0).unwrap(); + assert_eq!(first_round_info, round_info); + }) +} + #[test] fn project_registration_works() { new_test_ext().execute_with(|| { let batch = project_list(); + next_block(); + let mut round_info = VotingRounds::::get(0).unwrap(); + assert_eq!(round_info.batch_submitted, false); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); let project_list = WhiteListedProjectAccounts::::get(BOB); assert!(project_list.is_some()); // we should have 3 referendum started assert_eq!(pallet_democracy::PublicProps::::get().len(), 3); assert_eq!(pallet_democracy::ReferendumCount::::get(), 3); + // The storage infos are correct + round_info = VotingRounds::::get(0).unwrap(); + assert_eq!(round_info.batch_submitted, true); }) } #[test] -fn vote_works() { +fn conviction_vote_works() { new_test_ext().execute_with(|| { + next_block(); let batch = project_list(); + let voting_period = ::VotingPeriod::get(); + let vote_validity = ::VoteValidityPeriod::get(); + let now = + ::BlockNumberProvider::current_block_number(); + //round_end_block + let round_end = now.saturating_add(voting_period); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); // Bob vote for Alice assert_ok!(Opf::vote( @@ -90,5 +140,16 @@ fn vote_works() { let dave_hold = ::NativeBalance::total_balance_on_hold(&DAVE); assert_eq!(bob_hold, 100); assert_eq!(dave_hold, 100); + let round_number = NextVotingRoundNumber::::get().saturating_sub(1); + assert_eq!(round_number, 0); + + let bob_vote_unlock = round_end.saturating_add(vote_validity); + let dave_vote_unlock = bob_vote_unlock.clone().saturating_add(vote_validity); + + let bob_vote_info = Votes::::get(ALICE, BOB).unwrap(); + let dave_vote_info = Votes::::get(ALICE, DAVE).unwrap(); + + assert_eq!(bob_vote_info.funds_unlock_block, bob_vote_unlock); + assert_eq!(dave_vote_info.funds_unlock_block, dave_vote_unlock); }) } From 31f308e712cfd1bf8b4e2dd210f720377b640bcc Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 16 Jan 2025 16:42:02 +0900 Subject: [PATCH 32/62] cargo fmt --- substrate/frame/opf/src/lib.rs | 18 +++++++++--------- substrate/frame/opf/src/tests.rs | 12 ++++-------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 252648b9fc0d..5cc558e4bcbb 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -291,8 +291,8 @@ pub mod pallet { let current_round_index = round_index.saturating_sub(1); - let mut round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); - + let mut round_infos = + VotingRounds::::get(current_round_index).expect("InvalidResult"); // Check no Project batch has been submitted yet ensure!(!round_infos.batch_submitted, Error::::BatchAlreadySubmitted); @@ -331,14 +331,11 @@ pub mod pallet { *value = Some(new_infos); }); } - VotingRounds::::mutate(current_round_index, |round|{ - *round = Some(round_infos) - }); + VotingRounds::::mutate(current_round_index, |round| *round = Some(round_infos)); Self::deposit_event(Event::Projectslisted { when, projects_id }); Ok(()) } - /// OPF Projects de-listing /// @@ -486,15 +483,18 @@ pub mod pallet { #[pallet::call_index(7)] #[transactional] - pub fn release_voter_funds(origin: OriginFor, project_id: ProjectId) -> DispatchResult { + pub fn release_voter_funds( + origin: OriginFor, + project_id: ProjectId, + ) -> DispatchResult { let voter_id = ensure_signed(origin)?; ensure!(Votes::::contains_key(&project_id, &voter_id), Error::::NoVoteData); - let infos = Votes::::get(&project_id, &voter_id).ok_or( Error::::NoVoteData)?; + let infos = Votes::::get(&project_id, &voter_id).ok_or(Error::::NoVoteData)?; let release_block = infos.funds_unlock_block; let amount = infos.amount; let now = T::BlockNumberProvider::current_block_number(); - ensure!(now>=release_block, Error::::NotUnlockPeriod); + ensure!(now >= release_block, Error::::NotUnlockPeriod); T::NativeBalance::release( &HoldReason::FundsReserved.into(), &voter_id, diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 3d62f4be2d1a..fe3601836498 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -47,18 +47,15 @@ pub fn run_to_block(n: BlockNumberFor) { } } - #[test] fn first_round_creation_works() { new_test_ext().execute_with(|| { - let batch = project_list(); // First round is created next_block(); let voting_period = ::VotingPeriod::get(); - let now = - ::BlockNumberProvider::current_block_number(); + let now = ::BlockNumberProvider::current_block_number(); let round_ending_block = now.saturating_add(voting_period.into()); @@ -109,11 +106,10 @@ fn conviction_vote_works() { let batch = project_list(); let voting_period = ::VotingPeriod::get(); let vote_validity = ::VoteValidityPeriod::get(); - let now = - ::BlockNumberProvider::current_block_number(); + let now = ::BlockNumberProvider::current_block_number(); //round_end_block let round_end = now.saturating_add(voting_period); - + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); // Bob vote for Alice assert_ok!(Opf::vote( @@ -142,7 +138,7 @@ fn conviction_vote_works() { assert_eq!(dave_hold, 100); let round_number = NextVotingRoundNumber::::get().saturating_sub(1); assert_eq!(round_number, 0); - + let bob_vote_unlock = round_end.saturating_add(vote_validity); let dave_vote_unlock = bob_vote_unlock.clone().saturating_add(vote_validity); From 87b40ea3180a13816f4b3e46a5042dedcb691262 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 16 Jan 2025 22:34:44 +0900 Subject: [PATCH 33/62] Enactment not working --- substrate/frame/opf/src/functions.rs | 7 +- substrate/frame/opf/src/lib.rs | 4 +- substrate/frame/opf/src/mock.rs | 3 +- substrate/frame/opf/src/tests.rs | 119 +++++++++++++++++++++++++-- substrate/frame/opf/src/types.rs | 3 + 5 files changed, 122 insertions(+), 14 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 5c9e9047b922..d9bda5860b08 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -263,6 +263,9 @@ impl Pallet { amount: final_amount, index: ref_index, }; + WhiteListedProjectAccounts::::mutate(project_id.clone(), |val|{ + *val = Some(project_info.clone()); + }); // create a spend for project to be rewarded let _ = SpendInfo::::new(&project_info); @@ -311,9 +314,7 @@ impl Pallet { // Conditions for reward distribution preparations are: // - We are at the end of voting_round period - if now > round_ending_block { - // Clear ProjectFunds storage - ProjectFunds::::drain(); + if now >= round_ending_block { // Emmit events Self::deposit_event(Event::::VotingRoundEnded { when: now, diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 5cc558e4bcbb..8b92ad128380 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -321,7 +321,7 @@ pub mod pallet { let referendum_index = Democracy::Pallet::::internal_start_referendum( call_f, threshold, - Zero::zero(), + T::EnactmentPeriod::get(), ); let mut new_infos = WhiteListedProjectAccounts::::get(&project_id) .ok_or(Error::::NoProjectAvailable)?; @@ -477,6 +477,8 @@ pub mod pallet { // prepare reward distribution // for now we are using the temporary-constant reward. let _ = Self::calculate_rewards(T::TemporaryRewards::get())?; + // Clear ProjectFunds storage + ProjectFunds::::drain(); Ok(()) } diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 6e0ce61cd99a..27d27f84f2ac 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -146,8 +146,7 @@ parameter_types! { pub const PotId: PalletId = PalletId(*b"py/potid"); pub const MaxProjects:u32 = 50; pub const TemporaryRewards: Balance = 100_000; - pub const VoteLockingPeriod:u64 = 10; - pub const VotingPeriod:u64 = 30; + pub const VotingPeriod:u64 = 2; } impl pallet_opf::Config for Test { type RuntimeEvent = RuntimeEvent; diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index fe3601836498..78842d6d7ce0 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -33,7 +33,7 @@ pub fn next_block() { } pub fn project_list() -> Vec> { - vec![ALICE, BOB, DAVE] + vec![101, 102, 103] } pub fn run_to_block(n: BlockNumberFor) { @@ -88,7 +88,7 @@ fn project_registration_works() { let mut round_info = VotingRounds::::get(0).unwrap(); assert_eq!(round_info.batch_submitted, false); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); - let project_list = WhiteListedProjectAccounts::::get(BOB); + let project_list = WhiteListedProjectAccounts::::get(101); assert!(project_list.is_some()); // we should have 3 referendum started assert_eq!(pallet_democracy::PublicProps::::get().len(), 3); @@ -111,18 +111,18 @@ fn conviction_vote_works() { let round_end = now.saturating_add(voting_period); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); - // Bob vote for Alice + // Bob vote for project_101 assert_ok!(Opf::vote( RuntimeOrigin::signed(BOB), - ALICE, + 101, 100, true, pallet_democracy::Conviction::Locked1x )); - // Dave vote for Alice + // Dave vote for project_102 assert_ok!(Opf::vote( RuntimeOrigin::signed(DAVE), - ALICE, + 102, 100, true, pallet_democracy::Conviction::Locked2x @@ -142,10 +142,113 @@ fn conviction_vote_works() { let bob_vote_unlock = round_end.saturating_add(vote_validity); let dave_vote_unlock = bob_vote_unlock.clone().saturating_add(vote_validity); - let bob_vote_info = Votes::::get(ALICE, BOB).unwrap(); - let dave_vote_info = Votes::::get(ALICE, DAVE).unwrap(); + let bob_vote_info = Votes::::get(101, BOB).unwrap(); + let dave_vote_info = Votes::::get(102, DAVE).unwrap(); assert_eq!(bob_vote_info.funds_unlock_block, bob_vote_unlock); assert_eq!(dave_vote_info.funds_unlock_block, dave_vote_unlock); }) } + +#[test] +fn rewards_calculation_works() { + new_test_ext().execute_with(|| { + let batch = project_list(); + let voting_period = ::VotingPeriod::get(); + let vote_validity = ::VoteValidityPeriod::get(); + let now = ::BlockNumberProvider::current_block_number(); + //round_end_block + let round_end = now.saturating_add(voting_period); + + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + + // Bob nominate project_101 with an amount of 1000*BSX with a conviction x2 => equivalent to + // 3000*BSX locked + assert_ok!(Opf::vote( + RawOrigin::Signed(BOB).into(), + 101, + 1000 * BSX, + true, + pallet_democracy::Conviction::Locked2x + )); + + // Alice nominate project_101 with an amount of 5000*BSX with conviction 1x => equivalent to + // 10000*BSX locked + assert_ok!(Opf::vote( + RawOrigin::Signed(ALICE).into(), + 101, + 5000 * BSX, + true, + pallet_democracy::Conviction::Locked1x + )); + let p1 = ProjectFunds::::get(101); + println!("the reward is: {:?}", p1); + + // DAVE vote against project_102 with an amount of 3000*BSX with conviction 1x => equivalent + // to 6000*BSX locked + assert_ok!(Opf::vote( + RawOrigin::Signed(DAVE).into(), + 102, + 3000 * BSX, + false, + pallet_democracy::Conviction::Locked1x + )); + // Eve nominate project_102 with an amount of 5000*BSX with conviction 1x => equivalent to + // 10000*BSX locked + assert_ok!(Opf::vote( + RawOrigin::Signed(EVE).into(), + 102, + 5000 * BSX, + true, + pallet_democracy::Conviction::Locked1x + )); + + let p2 = ProjectFunds::::get(102); + println!("the reward is: {:?}", p2); + + let round_info = VotingRounds::::get(0).unwrap(); + + run_to_block(round_end); + let mut now = + ::BlockNumberProvider::current_block_number(); + + println!("Now is:{}", now); + assert_eq!(now, round_info.round_ending_block); + + // The right events are emitted + expect_events(vec![RuntimeEvent::Opf(Event::VotingRoundEnded { + when: now, + round_number: 0, + })]); + + // The total equivalent amount voted is 17000 + // Project 101: 13000 -> ~76.5%; Project 102: 4000 -> ~23.5% + // Distributed to project 101 -> 44%*100_000; Distributed to project 102 -> 55%*100_000 + //Opf::calculate_rewards(::TemporaryRewards::get()); + next_block(); + next_block(); + let reward_101 = WhiteListedProjectAccounts::::get(101).unwrap(); + let reward_102 = WhiteListedProjectAccounts::::get(102).unwrap(); + println!("p101_infos:{:?}", reward_101); + println!("p101_infos:{:?}", reward_102); + let referenda = pallet_democracy::ReferendumInfoOf::::get(0); + + println!("ref:{:?}", referenda); + // New round is properly started + /* run_to_block(round_info.round_ending_block); + now = round_info.round_ending_block; + expect_events(vec![RuntimeEvent::Opf(Event::VotingRoundEnded { + when: now, + round_number: 0, + })]); + let new_round_number = VotingRoundNumber::::get() - 1; + assert_eq!(new_round_number, 1); + let next_round = VotingRounds::::get(1); + assert_eq!(next_round.is_some(), true); + + now = now.saturating_add(::VoteLockingPeriod::get().into()); + // Unlock funds + run_to_block(now); + assert_ok!(Opf::unlock_funds(RawOrigin::Signed(ALICE).into(), 101));*/ + }) +} \ No newline at end of file diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 3e3ad904f40f..dc84dedb839e 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -112,6 +112,9 @@ impl SpendInfo { //Add it to the Spends storage Spends::::insert(whitelisted.project_id.clone(), spend.clone()); + //Update Project infos in project list storage + + spend } From 6d1485247cf3c604a8176fb48c8edbab9b37f61d Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 18 Jan 2025 00:21:39 +0900 Subject: [PATCH 34/62] Referendum working & tested --- substrate/frame/opf/src/functions.rs | 49 +++++++++++++++++++++----- substrate/frame/opf/src/lib.rs | 52 +++++++++++++--------------- substrate/frame/opf/src/tests.rs | 41 ++++++++-------------- substrate/frame/opf/src/types.rs | 2 -- 4 files changed, 80 insertions(+), 64 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index d9bda5860b08..6dd921e4f321 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -18,6 +18,31 @@ pub use super::*; impl Pallet { + pub fn get_formatted_call(call: Call) -> ::RuntimeCall { + call.into() + } + + pub fn make_proposal(call: CallOf) -> BoundedCallOf { + ::Preimages::bound(call).unwrap() + } + + pub fn add_proposal(who: T::AccountId, call: CallOf) -> DispatchResult { + let value = ::MinimumDeposit::get(); + let proposal: BoundedCallOf = T::Preimages::bound(call.into())?; + Democracy::Pallet::::propose(RawOrigin::Signed(who).into(), proposal.clone(), value)?; + Ok(()) + } + + pub fn start_dem_referendum( + proposal: BoundedCallOf, + delay: BlockNumberFor, + ) -> Democracy::ReferendumIndex { + let threshold = Democracy::VoteThreshold::SimpleMajority; + let referendum_index = + Democracy::Pallet::::internal_start_referendum(proposal, threshold, delay); + referendum_index + } + // Helper function for voting action. Existing votes are over-written, and Hold is adjusted pub fn try_vote( voter_id: VoterId, @@ -263,7 +288,7 @@ impl Pallet { amount: final_amount, index: ref_index, }; - WhiteListedProjectAccounts::::mutate(project_id.clone(), |val|{ + WhiteListedProjectAccounts::::mutate(project_id.clone(), |val| { *val = Some(project_info.clone()); }); @@ -272,13 +297,10 @@ impl Pallet { Self::deposit_event(Event::::ProjectFundingAccepted { project_id: project_id.clone(), - when, - round_number, amount: project_info.amount, }) } else { Self::deposit_event(Event::::ProjectFundingRejected { - when, project_id: project_id.clone(), }) } @@ -312,16 +334,27 @@ impl Pallet { let round_infos = VotingRounds::::get(current_round_index).expect("InvalidResult"); let round_ending_block = round_infos.round_ending_block; - // Conditions for reward distribution preparations are: - // - We are at the end of voting_round period - if now >= round_ending_block { + if now == round_ending_block { + // Emmit event + Self::deposit_event(Event::::VoteActionLocked { + when: now, + round_number: round_infos.round_number, + }); // Emmit events Self::deposit_event(Event::::VotingRoundEnded { when: now, round_number: round_infos.round_number, }); + // prepare reward distribution + // for now we are using the temporary-constant reward. + let _ = Self::calculate_rewards(T::TemporaryRewards::get()) + .map_err(|_| Error::::FailedRewardCalculation); + + // Clear Votes storage + Votes::::drain(); + // Clear ProjectFunds storage + ProjectFunds::::drain(); } - meter.consumed() } } diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 8b92ad128380..d9e0c0c621bb 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -44,6 +44,7 @@ pub mod pallet { + Parameter + UnfilteredDispatchable::RuntimeOrigin> + From> + + Into<::RuntimeCall> + GetDispatchInfo; /// The admin origin that can list and un-list whitelisted projects. type AdminOrigin: EnsureOrigin; @@ -182,18 +183,13 @@ pub mod pallet { ProjectUnlisted { when: ProvidedBlockNumberFor, project_id: ProjectId }, /// Project Funding Accepted by voters - ProjectFundingAccepted { - project_id: ProjectId, - when: ProvidedBlockNumberFor, - round_number: u32, - amount: BalanceOf, - }, + ProjectFundingAccepted { project_id: ProjectId, amount: BalanceOf }, /// Reward claim has expired ExpiredClaim { expired_when: ProvidedBlockNumberFor, project_id: ProjectId }, /// Project Funding rejected by voters - ProjectFundingRejected { when: ProvidedBlockNumberFor, project_id: ProjectId }, + ProjectFundingRejected { project_id: ProjectId }, /// A new voting round started VotingRoundStarted { when: ProvidedBlockNumberFor, round_number: u32 }, @@ -278,7 +274,7 @@ pub mod pallet { projects_id: Vec>, ) -> DispatchResult { //T::AdminOrigin::ensure_origin_or_root(origin.clone())?; - let _who = T::SubmitOrigin::ensure_origin(origin.clone())?; + let who = T::SubmitOrigin::ensure_origin(origin.clone())?; // Only 1 batch submission per round let mut round_index = NextVotingRoundNumber::::get(); @@ -309,20 +305,12 @@ pub mod pallet { for project_id in &projects_id { ProjectInfo::::new(project_id.clone()); // Prepare the proposal call - let call0: ::RuntimeCall = crate::Call::::on_registration {}.into(); - let call = ::RuntimeCall::convert(call0); - let call_f = T::Preimages::bound(call)?; - let threshold = Democracy::VoteThreshold::SimpleMajority; - Democracy::Pallet::::propose( - origin.clone(), - call_f.clone(), - T::MinimumDeposit::get(), - )?; - let referendum_index = Democracy::Pallet::::internal_start_referendum( - call_f, - threshold, - T::EnactmentPeriod::get(), - ); + let out_call = Call::::on_registration { project_id: project_id.clone() }; + let call0 = Self::get_formatted_call(out_call); + let proposal = Self::make_proposal(call0.clone().into()); + Self::add_proposal(who.clone(), call0.clone().into())?; + let delay = T::EnactmentPeriod::get(); + let referendum_index = Self::start_dem_referendum(proposal, delay); let mut new_infos = WhiteListedProjectAccounts::::get(&project_id) .ok_or(Error::::NoProjectAvailable)?; new_infos.index = referendum_index; @@ -472,13 +460,21 @@ pub mod pallet { #[pallet::call_index(6)] #[transactional] - pub fn on_registration(origin: OriginFor) -> DispatchResult { + pub fn on_registration(origin: OriginFor, project_id: ProjectId) -> DispatchResult { let _who = T::SubmitOrigin::ensure_origin(origin.clone())?; - // prepare reward distribution - // for now we are using the temporary-constant reward. - let _ = Self::calculate_rewards(T::TemporaryRewards::get())?; - // Clear ProjectFunds storage - ProjectFunds::::drain(); + let infos = WhiteListedProjectAccounts::::get(project_id.clone()) + .ok_or(Error::::NoProjectAvailable)?; + let ref_index = infos.index; + let amount = infos.amount; + if let Some(ref_infos) = Democracy::ReferendumInfoOf::::get(ref_index) { + match ref_infos { + Democracy::ReferendumInfo::Finished { approved: true, .. } => + Self::deposit_event(Event::ProjectFundingAccepted { project_id, amount }), + Democracy::ReferendumInfo::Finished { approved: false, .. } => + Self::deposit_event(Event::ProjectFundingRejected { project_id }), + Democracy::ReferendumInfo::Ongoing(_) => (), + } + } Ok(()) } diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 78842d6d7ce0..ebd8fb66bf32 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -171,7 +171,7 @@ fn rewards_calculation_works() { true, pallet_democracy::Conviction::Locked2x )); - + // Alice nominate project_101 with an amount of 5000*BSX with conviction 1x => equivalent to // 10000*BSX locked assert_ok!(Opf::vote( @@ -209,8 +209,7 @@ fn rewards_calculation_works() { let round_info = VotingRounds::::get(0).unwrap(); run_to_block(round_end); - let mut now = - ::BlockNumberProvider::current_block_number(); + let now = ::BlockNumberProvider::current_block_number(); println!("Now is:{}", now); assert_eq!(now, round_info.round_ending_block); @@ -225,30 +224,20 @@ fn rewards_calculation_works() { // Project 101: 13000 -> ~76.5%; Project 102: 4000 -> ~23.5% // Distributed to project 101 -> 44%*100_000; Distributed to project 102 -> 55%*100_000 //Opf::calculate_rewards(::TemporaryRewards::get()); - next_block(); - next_block(); + + let reward_101 = WhiteListedProjectAccounts::::get(101).unwrap(); let reward_102 = WhiteListedProjectAccounts::::get(102).unwrap(); - println!("p101_infos:{:?}", reward_101); - println!("p101_infos:{:?}", reward_102); - let referenda = pallet_democracy::ReferendumInfoOf::::get(0); - - println!("ref:{:?}", referenda); - // New round is properly started - /* run_to_block(round_info.round_ending_block); - now = round_info.round_ending_block; - expect_events(vec![RuntimeEvent::Opf(Event::VotingRoundEnded { - when: now, - round_number: 0, + next_block(); + next_block(); + expect_events(vec![RuntimeEvent::Opf(Event::ProjectFundingAccepted { + project_id: 102, + amount: 23000, })]); - let new_round_number = VotingRoundNumber::::get() - 1; - assert_eq!(new_round_number, 1); - let next_round = VotingRounds::::get(1); - assert_eq!(next_round.is_some(), true); - - now = now.saturating_add(::VoteLockingPeriod::get().into()); - // Unlock funds - run_to_block(now); - assert_ok!(Opf::unlock_funds(RawOrigin::Signed(ALICE).into(), 101));*/ + expect_events(vec![RuntimeEvent::Opf(Event::ProjectFundingAccepted { + project_id: 101, + amount: 76000, + })]); + }) -} \ No newline at end of file +} diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index dc84dedb839e..c249ff7b3eb8 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -114,8 +114,6 @@ impl SpendInfo { Spends::::insert(whitelisted.project_id.clone(), spend.clone()); //Update Project infos in project list storage - - spend } } From 941956e50ad561814232ccccd6ba39657fc69416 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 18 Jan 2025 01:01:15 +0900 Subject: [PATCH 35/62] tests clean-up --- substrate/frame/opf/src/mock.rs | 5 ++--- substrate/frame/opf/src/tests.rs | 24 ++++++++++-------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 27d27f84f2ac..2a45bc6782d7 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -18,10 +18,9 @@ //! Test environment for OPF pallet. use crate as pallet_opf; use crate::Convert; -use codec::{Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; pub use frame_support::{ derive_impl, ord_parameter_types, - pallet_prelude::TypeInfo, parameter_types, traits::{ ConstU32, ConstU64, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, @@ -33,7 +32,7 @@ pub use frame_support::{ pub use frame_system::{EnsureRoot, EnsureSigned, EnsureSignedBy}; pub use sp_runtime::{ traits::{AccountIdConversion, IdentityLookup}, - BuildStorage, Perbill, + BuildStorage, }; pub type Block = frame_system::mocking::MockBlock; pub type Balance = u64; diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index ebd8fb66bf32..8c762d650c28 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -50,7 +50,7 @@ pub fn run_to_block(n: BlockNumberFor) { #[test] fn first_round_creation_works() { new_test_ext().execute_with(|| { - let batch = project_list(); + let _batch = project_list(); // First round is created next_block(); @@ -155,7 +155,6 @@ fn rewards_calculation_works() { new_test_ext().execute_with(|| { let batch = project_list(); let voting_period = ::VotingPeriod::get(); - let vote_validity = ::VoteValidityPeriod::get(); let now = ::BlockNumberProvider::current_block_number(); //round_end_block let round_end = now.saturating_add(voting_period); @@ -181,8 +180,6 @@ fn rewards_calculation_works() { true, pallet_democracy::Conviction::Locked1x )); - let p1 = ProjectFunds::::get(101); - println!("the reward is: {:?}", p1); // DAVE vote against project_102 with an amount of 3000*BSX with conviction 1x => equivalent // to 6000*BSX locked @@ -203,15 +200,11 @@ fn rewards_calculation_works() { pallet_democracy::Conviction::Locked1x )); - let p2 = ProjectFunds::::get(102); - println!("the reward is: {:?}", p2); - let round_info = VotingRounds::::get(0).unwrap(); run_to_block(round_end); let now = ::BlockNumberProvider::current_block_number(); - println!("Now is:{}", now); assert_eq!(now, round_info.round_ending_block); // The right events are emitted @@ -220,23 +213,26 @@ fn rewards_calculation_works() { round_number: 0, })]); - // The total equivalent amount voted is 17000 + // The total equivalent amount voted is 17000 = 23000 - 6000 // Project 101: 13000 -> ~76.5%; Project 102: 4000 -> ~23.5% - // Distributed to project 101 -> 44%*100_000; Distributed to project 102 -> 55%*100_000 + // Distributed to project 101 -> 76%*100_000; Distributed to project 102 -> 23%*100_000 //Opf::calculate_rewards(::TemporaryRewards::get()); - let reward_101 = WhiteListedProjectAccounts::::get(101).unwrap(); - let reward_102 = WhiteListedProjectAccounts::::get(102).unwrap(); + let reward_101 = WhiteListedProjectAccounts::::get(101).unwrap().amount; + let reward_102 = WhiteListedProjectAccounts::::get(102).unwrap().amount; + assert_eq!(reward_101, 76000); + assert_eq!(reward_102, 23000); + next_block(); next_block(); expect_events(vec![RuntimeEvent::Opf(Event::ProjectFundingAccepted { project_id: 102, - amount: 23000, + amount: reward_102, })]); expect_events(vec![RuntimeEvent::Opf(Event::ProjectFundingAccepted { project_id: 101, - amount: 76000, + amount: reward_101, })]); }) From c2bb7d94d0a12c3497f7a2f1901fbca5492f1992 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 18 Jan 2025 17:50:57 +0900 Subject: [PATCH 36/62] Fixed referendum --- substrate/frame/opf/src/functions.rs | 31 ++++++++++++++-------------- substrate/frame/opf/src/lib.rs | 25 ++++++++++++++++------ substrate/frame/opf/src/mock.rs | 5 ++--- substrate/frame/opf/src/tests.rs | 16 +++++++------- substrate/frame/opf/src/types.rs | 4 ++-- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 6dd921e4f321..348c0fd9cd3a 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -22,15 +22,22 @@ impl Pallet { call.into() } - pub fn make_proposal(call: CallOf) -> BoundedCallOf { - ::Preimages::bound(call).unwrap() - } - - pub fn add_proposal(who: T::AccountId, call: CallOf) -> DispatchResult { + pub fn create_proposal( + caller: T::AccountId, + proposal_call: pallet::Call, + ) -> BoundedCallOf { + let proposal = Box::new(Self::get_formatted_call(proposal_call.into())); let value = ::MinimumDeposit::get(); - let proposal: BoundedCallOf = T::Preimages::bound(call.into())?; - Democracy::Pallet::::propose(RawOrigin::Signed(who).into(), proposal.clone(), value)?; - Ok(()) + let call = Call::::execute_call_dispatch { caller: caller.clone(), proposal }; + let call_formatted = Self::get_formatted_call(call.into()); + let bounded_proposal = + ::Preimages::bound(call_formatted.into()).unwrap(); + let _hash = Democracy::Pallet::::propose( + RawOrigin::Signed(caller).into(), + bounded_proposal.clone(), + value, + ); + bounded_proposal } pub fn start_dem_referendum( @@ -295,14 +302,6 @@ impl Pallet { // create a spend for project to be rewarded let _ = SpendInfo::::new(&project_info); - Self::deposit_event(Event::::ProjectFundingAccepted { - project_id: project_id.clone(), - amount: project_info.amount, - }) - } else { - Self::deposit_event(Event::::ProjectFundingRejected { - project_id: project_id.clone(), - }) } } } diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index d9e0c0c621bb..80b0632e8043 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -304,13 +304,12 @@ pub mod pallet { for project_id in &projects_id { ProjectInfo::::new(project_id.clone()); + // Prepare the proposal call - let out_call = Call::::on_registration { project_id: project_id.clone() }; - let call0 = Self::get_formatted_call(out_call); - let proposal = Self::make_proposal(call0.clone().into()); - Self::add_proposal(who.clone(), call0.clone().into())?; - let delay = T::EnactmentPeriod::get(); - let referendum_index = Self::start_dem_referendum(proposal, delay); + let call = Call::::on_registration { project_id: project_id.clone() }; + let proposal = Self::create_proposal(who.clone(), call); + let referendum_index = + Self::start_dem_referendum(proposal, T::EnactmentPeriod::get()); let mut new_infos = WhiteListedProjectAccounts::::get(&project_id) .ok_or(Error::::NoProjectAvailable)?; new_infos.index = referendum_index; @@ -503,5 +502,19 @@ pub mod pallet { Votes::::remove(&project_id, &voter_id); Ok(()) } + + #[pallet::call_index(8)] + #[transactional] + pub fn execute_call_dispatch( + origin: OriginFor, + caller: T::AccountId, + proposal: Box<::RuntimeCall>, + ) -> DispatchResult { + ensure_root(origin)?; + proposal + .dispatch_bypass_filter(frame_system::RawOrigin::Signed(caller.clone()).into()) + .ok(); + Ok(().into()) + } } } diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 2a45bc6782d7..a7f9661fdce2 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -20,8 +20,7 @@ use crate as pallet_opf; use crate::Convert; use codec::{Decode, Encode}; pub use frame_support::{ - derive_impl, ord_parameter_types, - parameter_types, + derive_impl, ord_parameter_types, parameter_types, traits::{ ConstU32, ConstU64, EqualPrivilegeOnly, OnFinalize, OnInitialize, OriginTrait, SortedMembers, VoteTally, @@ -112,7 +111,7 @@ impl SortedMembers for OneToFive { impl pallet_democracy::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = pallet_balances::Pallet; - type EnactmentPeriod = ConstU64<2>; + type EnactmentPeriod = ConstU64<1>; type LaunchPeriod = ConstU64<2>; type VotingPeriod = ConstU64<2>; type VoteLockingPeriod = ConstU64<3>; diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 8c762d650c28..83e826edafa2 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -91,7 +91,7 @@ fn project_registration_works() { let project_list = WhiteListedProjectAccounts::::get(101); assert!(project_list.is_some()); // we should have 3 referendum started - assert_eq!(pallet_democracy::PublicProps::::get().len(), 3); + //assert_eq!(pallet_democracy::PublicProps::::get().len(), 3); assert_eq!(pallet_democracy::ReferendumCount::::get(), 3); // The storage infos are correct round_info = VotingRounds::::get(0).unwrap(); @@ -156,9 +156,11 @@ fn rewards_calculation_works() { let batch = project_list(); let voting_period = ::VotingPeriod::get(); let now = ::BlockNumberProvider::current_block_number(); + println!("Now is: {:?}", now); //round_end_block let round_end = now.saturating_add(voting_period); + println!("Round will end at block: {:?}", round_end); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); // Bob nominate project_101 with an amount of 1000*BSX with a conviction x2 => equivalent to @@ -208,24 +210,23 @@ fn rewards_calculation_works() { assert_eq!(now, round_info.round_ending_block); // The right events are emitted - expect_events(vec![RuntimeEvent::Opf(Event::VotingRoundEnded { - when: now, - round_number: 0, - })]); + expect_events(vec![ + RuntimeEvent::Opf(Event::VotingRoundEnded { when: now, round_number: 0 }), + RuntimeEvent::Democracy(pallet_democracy::Event::Passed { ref_index: 0 }), + ]); // The total equivalent amount voted is 17000 = 23000 - 6000 // Project 101: 13000 -> ~76.5%; Project 102: 4000 -> ~23.5% // Distributed to project 101 -> 76%*100_000; Distributed to project 102 -> 23%*100_000 //Opf::calculate_rewards(::TemporaryRewards::get()); - let reward_101 = WhiteListedProjectAccounts::::get(101).unwrap().amount; let reward_102 = WhiteListedProjectAccounts::::get(102).unwrap().amount; assert_eq!(reward_101, 76000); assert_eq!(reward_102, 23000); next_block(); - next_block(); + expect_events(vec![RuntimeEvent::Opf(Event::ProjectFundingAccepted { project_id: 102, amount: reward_102, @@ -234,6 +235,5 @@ fn rewards_calculation_works() { project_id: 101, amount: reward_101, })]); - }) } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index c249ff7b3eb8..5052ef2d4634 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -42,8 +42,8 @@ pub use frame_system::{pallet_prelude::*, RawOrigin}; pub use scale_info::prelude::vec::Vec; pub use sp_runtime::{ traits::{ - AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Saturating, StaticLookup, - Zero, + AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Hash, Saturating, + StaticLookup, Zero, }, Percent, SaturatedConversion, }; From b63130db19895281cdd6ad1bb6fbcfab0a650a59 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 18 Jan 2025 18:04:04 +0900 Subject: [PATCH 37/62] More testing --- substrate/frame/opf/src/functions.rs | 3 +-- substrate/frame/opf/src/lib.rs | 6 +++++- substrate/frame/opf/src/tests.rs | 9 ++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 348c0fd9cd3a..59994104a8e5 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -299,8 +299,7 @@ impl Pallet { *val = Some(project_info.clone()); }); - // create a spend for project to be rewarded - let _ = SpendInfo::::new(&project_info); + } } diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 80b0632e8043..51a4a933cf56 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -463,12 +463,16 @@ pub mod pallet { let _who = T::SubmitOrigin::ensure_origin(origin.clone())?; let infos = WhiteListedProjectAccounts::::get(project_id.clone()) .ok_or(Error::::NoProjectAvailable)?; + let ref_index = infos.index; let amount = infos.amount; if let Some(ref_infos) = Democracy::ReferendumInfoOf::::get(ref_index) { match ref_infos { Democracy::ReferendumInfo::Finished { approved: true, .. } => - Self::deposit_event(Event::ProjectFundingAccepted { project_id, amount }), + { + // create a spend for project to be rewarded + let _ = SpendInfo::::new(&infos); + Self::deposit_event(Event::ProjectFundingAccepted { project_id, amount })}, Democracy::ReferendumInfo::Finished { approved: false, .. } => Self::deposit_event(Event::ProjectFundingRejected { project_id }), Democracy::ReferendumInfo::Ongoing(_) => (), diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 83e826edafa2..93253c1cf0aa 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -156,11 +156,8 @@ fn rewards_calculation_works() { let batch = project_list(); let voting_period = ::VotingPeriod::get(); let now = ::BlockNumberProvider::current_block_number(); - println!("Now is: {:?}", now); //round_end_block let round_end = now.saturating_add(voting_period); - - println!("Round will end at block: {:?}", round_end); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); // Bob nominate project_101 with an amount of 1000*BSX with a conviction x2 => equivalent to @@ -225,7 +222,13 @@ fn rewards_calculation_works() { assert_eq!(reward_101, 76000); assert_eq!(reward_102, 23000); + // Proposal Enactment did not happened yet + assert_eq!(Spends::::contains_key(101),false); + next_block(); + + // Enactment happened as expected + assert_eq!(Spends::::contains_key(101),true); expect_events(vec![RuntimeEvent::Opf(Event::ProjectFundingAccepted { project_id: 102, From b4287405fdbc1a547b2b0835147d8bc96e5097c5 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 18 Jan 2025 18:04:55 +0900 Subject: [PATCH 38/62] cargo fmt --- substrate/frame/opf/src/functions.rs | 3 --- substrate/frame/opf/src/lib.rs | 10 +++++----- substrate/frame/opf/src/tests.rs | 6 +++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 59994104a8e5..29c4d5693868 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -298,9 +298,6 @@ impl Pallet { WhiteListedProjectAccounts::::mutate(project_id.clone(), |val| { *val = Some(project_info.clone()); }); - - - } } } diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 51a4a933cf56..fa38f80c3862 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -463,16 +463,16 @@ pub mod pallet { let _who = T::SubmitOrigin::ensure_origin(origin.clone())?; let infos = WhiteListedProjectAccounts::::get(project_id.clone()) .ok_or(Error::::NoProjectAvailable)?; - + let ref_index = infos.index; let amount = infos.amount; if let Some(ref_infos) = Democracy::ReferendumInfoOf::::get(ref_index) { match ref_infos { - Democracy::ReferendumInfo::Finished { approved: true, .. } => - { - // create a spend for project to be rewarded + Democracy::ReferendumInfo::Finished { approved: true, .. } => { + // create a spend for project to be rewarded let _ = SpendInfo::::new(&infos); - Self::deposit_event(Event::ProjectFundingAccepted { project_id, amount })}, + Self::deposit_event(Event::ProjectFundingAccepted { project_id, amount }) + }, Democracy::ReferendumInfo::Finished { approved: false, .. } => Self::deposit_event(Event::ProjectFundingRejected { project_id }), Democracy::ReferendumInfo::Ongoing(_) => (), diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 93253c1cf0aa..05b017959476 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -223,12 +223,12 @@ fn rewards_calculation_works() { assert_eq!(reward_102, 23000); // Proposal Enactment did not happened yet - assert_eq!(Spends::::contains_key(101),false); + assert_eq!(Spends::::contains_key(101), false); next_block(); - + // Enactment happened as expected - assert_eq!(Spends::::contains_key(101),true); + assert_eq!(Spends::::contains_key(101), true); expect_events(vec![RuntimeEvent::Opf(Event::ProjectFundingAccepted { project_id: 102, From 934420475f8ad658779d9f015f737b6d86c2fab3 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sun, 19 Jan 2025 15:30:43 +0900 Subject: [PATCH 39/62] Added more tests --- substrate/frame/opf/src/functions.rs | 4 +- substrate/frame/opf/src/lib.rs | 5 +- substrate/frame/opf/src/tests.rs | 214 +++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 3 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 29c4d5693868..12c3828a5bb7 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -30,8 +30,8 @@ impl Pallet { let value = ::MinimumDeposit::get(); let call = Call::::execute_call_dispatch { caller: caller.clone(), proposal }; let call_formatted = Self::get_formatted_call(call.into()); - let bounded_proposal = - ::Preimages::bound(call_formatted.into()).unwrap(); + let bounded_proposal = ::Preimages::bound(call_formatted.into()) + .expect("Operation failed"); let _hash = Democracy::Pallet::::propose( RawOrigin::Signed(caller).into(), bounded_proposal.clone(), diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index fa38f80c3862..39d4fcee3db6 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -387,9 +387,12 @@ pub mod pallet { let converted_amount = Self::convert_balance(amount).ok_or("Failed Conversion!!!")?; let account_vote = Democracy::AccountVote::Standard { vote, balance: converted_amount }; - Self::try_vote(voter, project_id, amount, is_fund, conviction)?; + Self::try_vote(voter.clone(), project_id.clone(), amount, is_fund, conviction)?; Democracy::Pallet::::vote(origin, ref_index, account_vote)?; + let when = T::BlockNumberProvider::current_block_number(); + Self::deposit_event(Event::::VoteCasted { who: voter, when, project_id }); + Ok(()) } diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 05b017959476..0c4844b059d7 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -240,3 +240,217 @@ fn rewards_calculation_works() { })]); }) } + +#[test] +fn vote_removal_works() { + new_test_ext().execute_with(|| { + let batch = project_list(); + //round_end_block + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + + // Bob nominate project_102 with an amount of 1000 & conviction of 1 equivalent to + // 2000 + assert_ok!(Opf::vote( + RawOrigin::Signed(BOB).into(), + 101, + 1000, + true, + pallet_democracy::Conviction::Locked1x + )); + + // Eve nominate project_101 with an amount of 5000 & conviction 1x => equivalent to + // 10000 + assert_ok!(Opf::vote( + RawOrigin::Signed(EVE).into(), + 101, + 5000, + true, + pallet_democracy::Conviction::Locked1x + )); + + // ProjectFund is correctly updated + let project_fund_before = ProjectFunds::::get(101); + assert_eq!(project_fund_before[0], 12000); + + // Voter's funds are locked + let locked_balance0 = + <::NativeBalance as fungible::hold::Inspect>::balance_on_hold( + &HoldReason::FundsReserved.into(), + &BOB, + ); + + assert_eq!(locked_balance0, 1000); + assert_eq!(Votes::::get(101, BOB).is_some(), true); + + // Bob removes his vote + assert_ok!(Opf::remove_vote(RawOrigin::Signed(BOB).into(), 101,)); + + let locked_balance1 = + <::NativeBalance as fungible::hold::Inspect>::balance_on_hold( + &HoldReason::FundsReserved.into(), + &BOB, + ); + + // No more votes in storage and balance is unlocked + assert_eq!(Votes::::get(101, BOB).is_some(), false); + assert_eq!(locked_balance1, 0); + + // ProjectFund is correctly updated + let project_fund_after = ProjectFunds::::get(101); + assert_eq!(project_fund_after[0], 10000); + }) +} + +#[test] +fn vote_move_works() { + new_test_ext().execute_with(|| { + let batch = project_list(); + let now = ::BlockNumberProvider::current_block_number(); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + // Bob nominate project_101 with an amount of 1000 with a conviction of 2 => amount+amount*2 + // is the amount allocated to the project + assert_ok!(Opf::vote( + RawOrigin::Signed(BOB).into(), + 101, + 1000, + true, + pallet_democracy::Conviction::Locked2x + )); + + expect_events(vec![RuntimeEvent::Opf(Event::VoteCasted { + who: BOB, + when: now, + project_id: 101, + })]); + + // 3000 is allocated to project 101 + let mut funds = ProjectFunds::::get(101); + assert_eq!(funds[0], 3000); + + // Bob nominate project_103 with an amount of 5000 with a conviction of 1 => amount+amount*1 + // is the amount allocated to the project + assert_ok!(Opf::vote( + RawOrigin::Signed(BOB).into(), + 103, + 5000, + true, + pallet_democracy::Conviction::Locked1x + )); + + // 10000 is allocated to project 103 + funds = ProjectFunds::::get(103); + assert_eq!(funds[0], 10000); + + // Voter's funds are locked + let mut locked_balance0 = <::NativeBalance as fungible::hold::Inspect< + u64, + >>::balance_on_hold(&HoldReason::FundsReserved.into(), &BOB); + assert!(locked_balance0 > 0); + assert_eq!(locked_balance0, 6000); + + // Bob changes amount in project_103 to 4500 + assert_ok!(Opf::vote( + RawOrigin::Signed(BOB).into(), + 103, + 4500, + true, + pallet_democracy::Conviction::Locked2x + )); + + // Allocated amount to project 103 is now 13500 + funds = ProjectFunds::::get(103); + assert_eq!(funds[0], 13500); + + // Storage was correctly updated + let vote_info = Votes::::get(103, BOB).unwrap(); + + locked_balance0 = + <::NativeBalance as fungible::hold::Inspect>::balance_on_hold( + &HoldReason::FundsReserved.into(), + &BOB, + ); + + assert_eq!(4500, vote_info.amount); + assert_eq!(pallet_democracy::Conviction::Locked2x, vote_info.conviction); + assert_eq!(locked_balance0, 5500); + }) +} + +#[test] +fn voting_action_locked() { + new_test_ext().execute_with(|| { + let batch = project_list(); + let now = ::BlockNumberProvider::current_block_number(); + + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + + // Bob nominate project_101 with an amount of 1000 and conviction 3 => 3000 locked + assert_ok!(Opf::vote( + RawOrigin::Signed(BOB).into(), + 101, + 1000, + true, + pallet_democracy::Conviction::Locked3x + )); + + expect_events(vec![RuntimeEvent::Opf(Event::VoteCasted { + who: BOB, + when: now, + project_id: 101, + })]); + + // Bob nominate project_103 with an amount of 5000 + assert_ok!(Opf::vote( + RawOrigin::Signed(BOB).into(), + 103, + 5000, + true, + pallet_democracy::Conviction::Locked1x + )); + + // Voter's funds are locked + let locked_balance0 = + <::NativeBalance as fungible::hold::Inspect>::balance_on_hold( + &HoldReason::FundsReserved.into(), + &BOB, + ); + assert!(locked_balance0 > Zero::zero()); + + let round_info = VotingRounds::::get(0).unwrap(); + run_to_block(round_info.round_ending_block); + + // Bob cannot edit his vote for project 101 + assert_noop!( + Opf::vote( + RawOrigin::Signed(BOB).into(), + 101, + 2000, + true, + pallet_democracy::Conviction::Locked2x + ), + Error::::VotingRoundOver + ); + }) +} + +#[test] +fn not_enough_funds_to_vote() { + new_test_ext().execute_with(|| { + let batch = project_list(); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + let balance_plus = + <::NativeBalance as fungible::Inspect>::balance(&BOB) + 100; + + // Bob vote with wrong amount + assert_noop!( + Opf::vote( + RawOrigin::Signed(BOB).into(), + 101, + balance_plus, + true, + pallet_democracy::Conviction::Locked1x + ), + Error::::NotEnoughFunds + ); + }) +} From 11c3264efbceab4a5fa37adf2ea7d4d9b3567a5d Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sun, 19 Jan 2025 15:47:54 +0900 Subject: [PATCH 40/62] Documentation --- substrate/frame/opf/src/lib.rs | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 39d4fcee3db6..3c7cf7a8c5ad 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -308,6 +308,8 @@ pub mod pallet { // Prepare the proposal call let call = Call::::on_registration { project_id: project_id.clone() }; let proposal = Self::create_proposal(who.clone(), call); + + // Start Referendum let referendum_index = Self::start_dem_referendum(proposal, T::EnactmentPeriod::get()); let mut new_infos = WhiteListedProjectAccounts::::get(&project_id) @@ -356,6 +358,37 @@ pub mod pallet { Ok(()) } + /// OPF voting logic + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// This extrinsic allows users to [vote for/nominate] a whitelisted project using their + /// funds. The amount defined by the user is locked and released only when the project + /// reward is ready for distribution, or when the project is not dimmed fundable. + /// Users can edit/over-write an existing vote within the vote-casting period. + /// At the end of the voting period, rewards are calculated based on the total user amount + /// attributed to each project by the user’s votes. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// - `amount`: Amount that will be locked in user’s balance to nominate a project. + /// - `is_fund`: Parameter that defines if user’s vote is in favor (*true*), or against + /// (*false*) + /// the project funding. + /// - `conviction`: Used to calculate the value allocated to the project, & determine + /// when the voter's funds will be unlocked. Amount actually locked is the amount without + /// conviction + /// + /// ### Errors + /// - [`Error::::NotEnoughFunds`]: The user does not have enough balance to cast a vote + /// + /// ## Events + /// - [`Event::::VoteCasted { who, when, project_id }`]: User's vote successfully + /// submitted #[pallet::call_index(3)] #[transactional] pub fn vote( @@ -396,6 +429,26 @@ pub mod pallet { Ok(()) } + /// OPF vote removal logic + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// This extrinsic allows users to remove a casted vote, as long as it is within the + /// vote-casting period. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// + /// ### Errors + /// - [`Error::::NotEnoughFunds`]: The user does not have enough balance to cast a vote + /// + /// ## Events + /// - [`Event::::VoteRemoved { who, when, project_id }`]: User's vote successfully + /// removed #[pallet::call_index(4)] #[transactional] pub fn remove_vote(origin: OriginFor, project_id: ProjectId) -> DispatchResult { From 1658e809696900b597b31e2f162d2d65cf65035f Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sun, 19 Jan 2025 15:55:58 +0900 Subject: [PATCH 41/62] Documentation --- substrate/frame/opf/src/functions.rs | 2 +- substrate/frame/opf/src/lib.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 12c3828a5bb7..74322a427bae 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -346,7 +346,7 @@ impl Pallet { .map_err(|_| Error::::FailedRewardCalculation); // Clear Votes storage - Votes::::drain(); + //Votes::::drain(); // Clear ProjectFunds storage ProjectFunds::::drain(); } diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 3c7cf7a8c5ad..48322f3051f2 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -538,6 +538,25 @@ pub mod pallet { Ok(()) } + + /// User's funds unlock + /// + /// ## Dispatch Origin + /// + /// Must be signed + /// + /// ## Details + /// + /// This extrinsic allows users to unlock funds related to a specific project, + /// provided the locking period (which is dependant of the conviction) has ended. + /// + /// ### Parameters + /// - `project_id`: The account that will receive the reward. + /// + /// ### Errors + /// - [`Error::::NoVoteData`]: No vote data found for the specified project + /// + /// ## Events #[pallet::call_index(7)] #[transactional] pub fn release_voter_funds( From 22e3b2d686e658096de701d38c5afdbe77e013ab Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sun, 19 Jan 2025 16:18:23 +0900 Subject: [PATCH 42/62] Clean-up --- substrate/frame/opf/src/functions.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 74322a427bae..0be0bf0ff440 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -345,8 +345,6 @@ impl Pallet { let _ = Self::calculate_rewards(T::TemporaryRewards::get()) .map_err(|_| Error::::FailedRewardCalculation); - // Clear Votes storage - //Votes::::drain(); // Clear ProjectFunds storage ProjectFunds::::drain(); } From 14f2a3e033e00a0a5646480f46a21bf63615c24b Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sun, 19 Jan 2025 22:03:16 +0900 Subject: [PATCH 43/62] cargo fmt & clippy --- substrate/bin/node/runtime/src/lib.rs | 18 ++++++++---------- substrate/frame/opf/src/lib.rs | 1 - substrate/frame/opf/src/tests.rs | 27 +++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 5634b0529b98..63bd9d842c3e 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1616,7 +1616,6 @@ impl pallet_offences::Config for Runtime { type OnOffenceHandler = Staking; } - parameter_types! { // Id of the treasury pub const PotId: PalletId = PalletId(*b"py/potid"); @@ -1626,7 +1625,7 @@ parameter_types! { /// This should be calculated as a percentage of inflation. pub const TemporaryRewards: Balance = 100000 * DOLLARS; - + } impl pallet_opf::Config for Runtime { @@ -1643,13 +1642,13 @@ impl pallet_opf::Config for Runtime { /// Maximum number of whitelisted projects type MaxProjects = MaxProjects; - /// Time period in which people can vote. - /// After the period has ended, the votes are counted (STOP THE COUNT) + /// Time period in which people can vote. + /// After the period has ended, the votes are counted (STOP THE COUNT) /// and then the funds are distributed into Spends. type VotingPeriod = VotingPeriod; - /// Time for claiming a Spend. - /// After the period has passed, a spend is thrown away + /// Time for claiming a Spend. + /// After the period has passed, a spend is thrown away /// and the funds are available again for distribution in the pot. type ClaimingPeriod = ClaimingPeriod; @@ -1660,7 +1659,7 @@ impl pallet_opf::Config for Runtime { /// This should be calculated as a percentage of inflation. type TemporaryRewards = TemporaryRewards; - + type WeightInfo = (); //pallet_opf::weights::SubstrateWeight; } @@ -2766,10 +2765,9 @@ mod runtime { #[runtime::pallet_index(84)] pub type AssetsFreezer = pallet_assets_freezer::Pallet; - - #[runtime::pallet_index(85)] - pub type Opf = pallet_opf::Pallet; + #[runtime::pallet_index(85)] + pub type Opf = pallet_opf::Pallet; } impl TryFrom for pallet_revive::Call { diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 48322f3051f2..059b8b6d8c5b 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -538,7 +538,6 @@ pub mod pallet { Ok(()) } - /// User's funds unlock /// /// ## Dispatch Origin diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 0c4844b059d7..a31a20c9f063 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -99,6 +99,29 @@ fn project_registration_works() { }) } +#[test] +fn cannot_register_twice_in_same_round() { + new_test_ext().execute_with(|| { + let batch = project_list(); + next_block(); + let mut round_info = VotingRounds::::get(0).unwrap(); + assert_eq!(round_info.batch_submitted, false); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch.clone())); + let project_list = WhiteListedProjectAccounts::::get(101); + assert!(project_list.is_some()); + // we should have 3 referendum started + //assert_eq!(pallet_democracy::PublicProps::::get().len(), 3); + assert_eq!(pallet_democracy::ReferendumCount::::get(), 3); + // The storage infos are correct + round_info = VotingRounds::::get(0).unwrap(); + assert_eq!(round_info.batch_submitted, true); + assert_noop!( + Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch), + Error::::BatchAlreadySubmitted + ); + }) +} + #[test] fn conviction_vote_works() { new_test_ext().execute_with(|| { @@ -140,7 +163,7 @@ fn conviction_vote_works() { assert_eq!(round_number, 0); let bob_vote_unlock = round_end.saturating_add(vote_validity); - let dave_vote_unlock = bob_vote_unlock.clone().saturating_add(vote_validity); + let dave_vote_unlock = bob_vote_unlock.saturating_add(vote_validity); let bob_vote_info = Votes::::get(101, BOB).unwrap(); let dave_vote_info = Votes::::get(102, DAVE).unwrap(); @@ -302,7 +325,7 @@ fn vote_removal_works() { } #[test] -fn vote_move_works() { +fn vote_overwrite_works() { new_test_ext().execute_with(|| { let batch = project_list(); let now = ::BlockNumberProvider::current_block_number(); From 55b1a6d3fb47c67121616c1503a1fc00e3bafa81 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sun, 19 Jan 2025 22:31:45 +0900 Subject: [PATCH 44/62] Clippy --- substrate/frame/opf/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 059b8b6d8c5b..4306de76ecb7 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -40,8 +40,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config + Democracy::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - type RuntimeCall: Convert<::RuntimeCall, ::RuntimeCall> - + Parameter + type RuntimeCall: Parameter + UnfilteredDispatchable::RuntimeOrigin> + From> + Into<::RuntimeCall> From 25a400d19b14bda0605aa8b6468e82baff0c25bc Mon Sep 17 00:00:00 2001 From: ndkazu Date: Mon, 20 Jan 2025 23:40:30 +0900 Subject: [PATCH 45/62] Funds claim tested --- substrate/frame/opf/src/lib.rs | 19 +++++++--- substrate/frame/opf/src/tests.rs | 61 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 4306de76ecb7..0e6c5422d1f8 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -218,8 +218,6 @@ pub mod pallet { NotClaimingPeriod, /// Still not in funds unlock period NotUnlockPeriod, - /// Funds locking failed - FundsReserveFailed, /// An invalid result was returned InvalidResult, /// The reward calculation failed due to an internal error @@ -521,12 +519,25 @@ pub mod pallet { let ref_index = infos.index; let amount = infos.amount; + let when = T::BlockNumberProvider::current_block_number(); if let Some(ref_infos) = Democracy::ReferendumInfoOf::::get(ref_index) { match ref_infos { Democracy::ReferendumInfo::Finished { approved: true, .. } => { + let pot = Self::pot_account(); + let balance = T::NativeBalance::balance(&pot); + let minimum_balance = T::NativeBalance::minimum_balance(); + // check if the pot has enough fund for the Spend + // Check that the Pot as enough funds for the transfer + let remaining_balance = balance.saturating_sub(infos.amount); + ensure!(remaining_balance > minimum_balance, Error::::NotEnoughFunds); // create a spend for project to be rewarded - let _ = SpendInfo::::new(&infos); - Self::deposit_event(Event::ProjectFundingAccepted { project_id, amount }) + let new_spend = SpendInfo::::new(&infos); + Self::deposit_event(Event::ProjectFundingAccepted { project_id, amount }); + Self::deposit_event(Event::SpendCreated { + when, + amount: new_spend.amount, + project_id: infos.project_id.clone(), + }); }, Democracy::ReferendumInfo::Finished { approved: false, .. } => Self::deposit_event(Event::ProjectFundingRejected { project_id }), diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index a31a20c9f063..57ce15a03cc5 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -477,3 +477,64 @@ fn not_enough_funds_to_vote() { ); }) } + +#[test] +fn spends_creation_works_but_not_executed_before_claim_period() { + new_test_ext().execute_with(|| { + let batch = project_list(); + let voting_period = ::VotingPeriod::get(); + let mut now = ::BlockNumberProvider::current_block_number(); + let amount1 = 400 * BSX; + let amount2 = 320 * BSX; + let amount3 = 280 * BSX; + //round_end_block + let round_end = now.saturating_add(voting_period); + assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); + + assert_ok!(Opf::vote( + RawOrigin::Signed(ALICE).into(), + 101, + amount1, + true, + pallet_democracy::Conviction::None + )); + + assert_ok!(Opf::vote( + RawOrigin::Signed(DAVE).into(), + 102, + amount2, + true, + pallet_democracy::Conviction::None + )); + + assert_ok!(Opf::vote( + RawOrigin::Signed(EVE).into(), + 103, + amount3, + true, + pallet_democracy::Conviction::None + )); + + // The Spends Storage should be empty + assert_eq!(Spends::::count(), 0); + + run_to_block(round_end); + + next_block(); + now = ::BlockNumberProvider::current_block_number(); + let expire = now.saturating_add(::ClaimingPeriod::get()); + + let info101 = WhiteListedProjectAccounts::::get(101).unwrap(); + let spend101: types::SpendInfo = SpendInfo { + amount: 40000, + valid_from: now, + whitelisted_project: info101, + claimed: false, + expire, + }; + assert_eq!(Spends::::get(101), Some(spend101)); + + // Claim works + assert_ok!(Opf::claim_reward_for(RawOrigin::Signed(EVE).into(), 101)); + }) +} From 8d05dc06a5e3a50a692d410003912ae76b766a9a Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 21 Jan 2025 10:11:26 +0900 Subject: [PATCH 46/62] tests + documentation --- substrate/frame/opf/src/lib.rs | 40 ++++++++++++++++++++++++++++---- substrate/frame/opf/src/tests.rs | 15 +++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 0e6c5422d1f8..fb431f808d29 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -15,6 +15,40 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! OPF pallet. +//! +//! The OPF Pallet handles the Optimistic Project Funding. +//! It allows users to nominate projects (whitelisted in OpenGov) with their DOT. +//! +//! ## Overview +//! +//! This mechanism will be funded with a constant stream of DOT taken directly from inflation +//! and distributed to projects based on the proportion of DOT that has nominated them. +//! +//! ### Terminology +//! +//! - **MaxWhitelistedProjects:** Maximum number of Whitelisted projects that can be handled by the +//! pallet. +//! - **VotingPeriod:**Period during which voting is enabled. +//! - **TemporaryRewards:**For test purposes only ⇒ used as a substitute for the inflation portion +//! used for the rewards. +//! - **PotId:** Pot containing the funds used to pay the rewards. +//! - **ClaimingPeriod:**Period during which allocated funds can be claimed +//! +//! ## Interface +//! +//! ### Permissionless Functions +//! +//! * `pot_account`: Output the pot account_id. +//! * `spend`: Funds transfer from the Pot to a project account. +//! +//! ### Privileged Functions +//! +//! * `vote`: Allows users to [vote for/nominate] a whitelisted project using their funds. +//! * `remove_vote`: Allows users to remove a casted vote. +//! * `release_voter_funds`: Allows users to unlock funds related to a specific project. +//! * `claim_reward_for`: Claim a reward for a nominated/whitelisted project. + #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; @@ -157,9 +191,6 @@ pub mod pallet { project_id: ProjectId, }, - /// Not yet in the claiming period - NotClaimingPeriod { project_id: ProjectId, claiming_period: ProvidedBlockNumberFor }, - /// Payment will be enacted for corresponding project WillBeEnacted { project_id: ProjectId }, @@ -506,7 +537,8 @@ pub mod pallet { WhiteListedProjectAccounts::::remove(&project_id); Ok(()) } else { - Err(DispatchError::Other("Not Claiming Period")) + // Claimin before proposal enactment + Err(Error::::NotClaimingPeriod.into()) } } diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 57ce15a03cc5..7ba3a5b49474 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -479,7 +479,7 @@ fn not_enough_funds_to_vote() { } #[test] -fn spends_creation_works_but_not_executed_before_claim_period() { +fn spends_creation_works_but_not_executed_after_claim_period() { new_test_ext().execute_with(|| { let batch = project_list(); let voting_period = ::VotingPeriod::get(); @@ -520,6 +520,9 @@ fn spends_creation_works_but_not_executed_before_claim_period() { run_to_block(round_end); + // Claim does not work before proposal enactment + assert_noop!(Opf::claim_reward_for(RawOrigin::Signed(EVE).into(), 102), Error::::InexistentSpend); + next_block(); now = ::BlockNumberProvider::current_block_number(); let expire = now.saturating_add(::ClaimingPeriod::get()); @@ -532,9 +535,19 @@ fn spends_creation_works_but_not_executed_before_claim_period() { claimed: false, expire, }; + // Spend correctly created assert_eq!(Spends::::get(101), Some(spend101)); // Claim works assert_ok!(Opf::claim_reward_for(RawOrigin::Signed(EVE).into(), 101)); + run_to_block(expire); + assert_ok!(Opf::claim_reward_for(RawOrigin::Signed(EVE).into(), 102)); + + // Claim does not work after claiming period + expect_events(vec![RuntimeEvent::Opf(Event::ExpiredClaim { + expired_when: expire, + project_id: 102, + })]); + }) } From 67f1c196a4cb47c796bd2c4203b4bfdff61e44ed Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 21 Jan 2025 17:22:41 +0900 Subject: [PATCH 47/62] Vote benchmark --- substrate/frame/opf/src/benchmarking.rs | 168 ++++++++++++++++++++++++ substrate/frame/opf/src/lib.rs | 7 +- substrate/frame/opf/src/tests.rs | 10 +- 3 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 substrate/frame/opf/src/benchmarking.rs diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs new file mode 100644 index 000000000000..90f939eed9fd --- /dev/null +++ b/substrate/frame/opf/src/benchmarking.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. + +//! OPF pallet benchmarking. + +#![cfg(feature = "runtime-benchmarks")] +use super::*; + +use crate::{Democracy::Conviction, Pallet as Opf}; +//use pallet_distribution as Distribution; +use frame_benchmarking::{ + v1::{account, BenchmarkError}, + v2::*, +}; +use frame_support::ensure; +use frame_system::RawOrigin; +use sp_runtime::traits::One; + +const SEED: u32 = 0; + +fn run_to_block(n: BlockNumberFor) { + while frame_system::Pallet::::block_number() < n { + let b = frame_system::Pallet::::block_number(); + crate::Pallet::::on_finalize(b); + frame_system::Pallet::::on_finalize(b); + frame_system::Pallet::::set_block_number(b + One::one()); + frame_system::Pallet::::on_initialize(b); + crate::Pallet::::on_initialize(b); + } +} + +fn on_idle_full_block() { + let remaining_weight = ::BlockWeights::get().max_block; + let when = frame_system::Pallet::::block_number(); + frame_system::Pallet::::on_idle(when, remaining_weight); + crate::Pallet::::on_idle(when, remaining_weight); +} + +fn add_whitelisted_project(n: u32, caller: T::AccountId) -> Result<(), &'static str> { + let mut batch: Vec<_> = Vec::new(); + for _i in 0..n { + let project_id = account("project", n, SEED); + batch.push(project_id); + } + let _ = crate::Pallet::::register_projects_batch(RawOrigin::Signed(caller).into(), batch); + + Ok(()) +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn vote(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + let account: T::AccountId = account("project", r, SEED); + add_whitelisted_project::(r, caller.clone())?; + ensure!( + WhiteListedProjectAccounts::::contains_key(account.clone()) == true, + "Project_id not set up correctly." + ); + + on_idle_full_block::(); + let when = frame_system::Pallet::::block_number() + One::one(); + run_to_block::(when); + + ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); + let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); + + let _ = T::NativeBalance::mint_into(&caller, caller_balance); + + let value: BalanceOf = T::NativeBalance::minimum_balance() * 10u32.into() * (r).into(); + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), account, value, true, Conviction::Locked1x); + + Ok(()) + } + + /*#[benchmark] + fn remove_vote( + r: Linear<1, { T::MaxWhitelistedProjects::get() }>, + ) -> Result<(), BenchmarkError> { + add_whitelisted_project::(r)?; + ensure!( + WhiteListedProjectAccounts::::get().len() as u32 == r, + "Project_id not set up correctly." + ); + + on_idle_full_block::(); + let when = T::BlockNumberProvider::current_block_number() + One::one(); + run_to_block::(when); + + ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); + let caller_balance = T::NativeBalance::minimum_balance() * 10000u32.into(); + let caller: T::AccountId = whitelisted_caller(); + let _ = T::NativeBalance::mint_into(&caller, caller_balance); + let account = WhiteListedProjectAccounts::::get()[(r - 1) as usize].clone(); + let value: BalanceOf = T::NativeBalance::minimum_balance() * 100u32.into() * (r).into(); + Opf::::vote( + RawOrigin::Signed(caller.clone()).into(), + account.clone(), + value, + true, + Conviction::Locked1x, + )?; + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), account); + + Ok(()) + } + + #[benchmark] + fn unlock_funds( + r: Linear<1, { T::MaxWhitelistedProjects::get() }>, + ) -> Result<(), BenchmarkError> { + add_whitelisted_project::(r)?; + ensure!( + WhiteListedProjectAccounts::::get().len() as u32 == r, + "Project_id not set up correctly." + ); + + on_idle_full_block::(); + let mut when = T::BlockNumberProvider::current_block_number() + One::one(); + run_to_block::(when); + + ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); + let caller_balance = T::NativeBalance::minimum_balance() * 1000000u32.into(); + let caller: T::AccountId = whitelisted_caller(); + let _ = T::NativeBalance::mint_into(&caller, caller_balance); + let account = WhiteListedProjectAccounts::::get()[(r - 1) as usize].clone(); + let value: BalanceOf = T::NativeBalance::minimum_balance() * 100u32.into() * (r).into(); + Opf::::vote( + RawOrigin::Signed(caller.clone()).into(), + account.clone(), + value, + true, + Conviction::Locked1x, + )?; + + when = Votes::::get(account.clone(), caller.clone()).unwrap().funds_unlock_block; + + run_to_block::(when); + on_idle_full_block::(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), account); + + Ok(()) + }*/ + + impl_benchmark_test_suite!(Opf, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index fb431f808d29..c060d480d9e9 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -33,7 +33,7 @@ //! - **TemporaryRewards:**For test purposes only ⇒ used as a substitute for the inflation portion //! used for the rewards. //! - **PotId:** Pot containing the funds used to pay the rewards. -//! - **ClaimingPeriod:**Period during which allocated funds can be claimed +//! - **ClaimingPeriod:**Period during which allocated funds can be claimed //! //! ## Interface //! @@ -57,6 +57,9 @@ mod types; pub use pallet_democracy as Democracy; pub use types::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + #[cfg(test)] mod mock; @@ -537,7 +540,7 @@ pub mod pallet { WhiteListedProjectAccounts::::remove(&project_id); Ok(()) } else { - // Claimin before proposal enactment + // Claimin before proposal enactment Err(Error::::NotClaimingPeriod.into()) } } diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 7ba3a5b49474..36a29118885c 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -520,9 +520,12 @@ fn spends_creation_works_but_not_executed_after_claim_period() { run_to_block(round_end); - // Claim does not work before proposal enactment - assert_noop!(Opf::claim_reward_for(RawOrigin::Signed(EVE).into(), 102), Error::::InexistentSpend); - + // Claim does not work before proposal enactment + assert_noop!( + Opf::claim_reward_for(RawOrigin::Signed(EVE).into(), 102), + Error::::InexistentSpend + ); + next_block(); now = ::BlockNumberProvider::current_block_number(); let expire = now.saturating_add(::ClaimingPeriod::get()); @@ -548,6 +551,5 @@ fn spends_creation_works_but_not_executed_after_claim_period() { expired_when: expire, project_id: 102, })]); - }) } From db13ef3c7d4dce25b090202cee307d97c57cf52c Mon Sep 17 00:00:00 2001 From: ndkazu Date: Wed, 22 Jan 2025 22:36:41 +0900 Subject: [PATCH 48/62] Corrected rewards_calculation --- substrate/frame/opf/src/benchmarking.rs | 29 ++++++++-------- substrate/frame/opf/src/functions.rs | 26 +++++++------- substrate/frame/opf/src/tests.rs | 46 ++++++++++++------------- 3 files changed, 52 insertions(+), 49 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index 90f939eed9fd..af7bbb627f05 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::*; use crate::{Democracy::Conviction, Pallet as Opf}; //use pallet_distribution as Distribution; -use frame_benchmarking::{ +pub use frame_benchmarking::{ v1::{account, BenchmarkError}, v2::*, }; @@ -68,10 +68,10 @@ mod benchmarks { #[benchmark] fn vote(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); - let account: T::AccountId = account("project", r, SEED); + let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; ensure!( - WhiteListedProjectAccounts::::contains_key(account.clone()) == true, + WhiteListedProjectAccounts::::contains_key(account0.clone()) == true, "Project_id not set up correctly." ); @@ -86,45 +86,46 @@ mod benchmarks { let value: BalanceOf = T::NativeBalance::minimum_balance() * 10u32.into() * (r).into(); #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), account, value, true, Conviction::Locked1x); + _(RawOrigin::Signed(caller.clone()), account0, value, true, Conviction::Locked1x); Ok(()) } - /*#[benchmark] + #[benchmark] fn remove_vote( - r: Linear<1, { T::MaxWhitelistedProjects::get() }>, + r: Linear<1, 1000>, ) -> Result<(), BenchmarkError> { - add_whitelisted_project::(r)?; + let caller: T::AccountId = whitelisted_caller(); + let account0: T::AccountId = account("project", r, SEED); + add_whitelisted_project::(r, caller.clone())?; ensure!( - WhiteListedProjectAccounts::::get().len() as u32 == r, + WhiteListedProjectAccounts::::contains_key(account0.clone()) == true, "Project_id not set up correctly." ); on_idle_full_block::(); - let when = T::BlockNumberProvider::current_block_number() + One::one(); + let when = frame_system::Pallet::::block_number() + One::one(); run_to_block::(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); - let caller_balance = T::NativeBalance::minimum_balance() * 10000u32.into(); + let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); let caller: T::AccountId = whitelisted_caller(); let _ = T::NativeBalance::mint_into(&caller, caller_balance); - let account = WhiteListedProjectAccounts::::get()[(r - 1) as usize].clone(); let value: BalanceOf = T::NativeBalance::minimum_balance() * 100u32.into() * (r).into(); Opf::::vote( RawOrigin::Signed(caller.clone()).into(), - account.clone(), + account0.clone(), value, true, Conviction::Locked1x, )?; #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), account); + _(RawOrigin::Signed(caller.clone()), account0); Ok(()) } - +/* #[benchmark] fn unlock_funds( r: Linear<1, { T::MaxWhitelistedProjects::get() }>, diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 0be0bf0ff440..a26bcc7dda38 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -21,6 +21,16 @@ impl Pallet { pub fn get_formatted_call(call: Call) -> ::RuntimeCall { call.into() } + pub fn conviction_amount(amount: BalanceOf, conviction:Democracy::Conviction) -> Option>{ + + let conviction_amount: BalanceOf = match conviction { + Democracy::Conviction::None => { + Percent::from_percent(10) * amount + } + _ => amount.saturating_mul(>::from(conviction).into()) + }; + Some(conviction_amount) + } pub fn create_proposal( caller: T::AccountId, @@ -72,9 +82,7 @@ impl Pallet { .ok_or(Error::::NoProjectAvailable)?; let ref_index = infos.index; - let conviction_fund = amount.saturating_add( - amount.saturating_mul(>::from(conviction).into()), - ); + let conviction_fund = Self::conviction_amount(amount, conviction).ok_or("Invalid conviction")?; // Create vote infos and store/adjust them let round_number = NextVotingRoundNumber::::get().saturating_sub(1); @@ -105,10 +113,7 @@ impl Pallet { let old_vote = Votes::::get(&project, &voter_id).ok_or(Error::::NoVoteData)?; let old_amount = old_vote.amount; let old_conviction = old_vote.conviction; - let old_conviction_amount = - old_amount.saturating_add(old_amount.saturating_mul( - >::from(old_conviction).into(), - )); + let old_conviction_amount = Self::conviction_amount(old_amount, old_conviction).ok_or("Invalid conviction")?; ProjectFunds::::mutate(&project, |val| { let mut val0 = val.clone().into_inner(); if is_fund { @@ -210,9 +215,7 @@ impl Pallet { let conviction = infos.conviction; let is_fund = infos.is_fund; - let conviction_fund = amount.saturating_add( - amount.saturating_mul(>::from(conviction).into()), - ); + let conviction_fund = Self::conviction_amount(amount, conviction).ok_or("Invalid conviction")?; // Update Round infos let round_number = NextVotingRoundNumber::::get().saturating_sub(1); @@ -264,10 +267,9 @@ impl Pallet { let round = VotingRounds::::get(round_number).ok_or(Error::::NoRoundFound)?; if projects.clone().len() > 0 as usize { let total_positive_votes_amount = round.total_positive_votes_amount; - let total_negative_votes_amount = round.total_negative_votes_amount; let when = T::BlockNumberProvider::current_block_number(); let total_votes_amount = - total_positive_votes_amount.saturating_sub(total_negative_votes_amount); + total_positive_votes_amount; // for each project, calculate the percentage of votes, the amount to be distributed, // and then populate the storage Projects diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 36a29118885c..5ad74d4ef347 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -184,7 +184,7 @@ fn rewards_calculation_works() { assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); // Bob nominate project_101 with an amount of 1000*BSX with a conviction x2 => equivalent to - // 3000*BSX locked + // 2000*BSX locked assert_ok!(Opf::vote( RawOrigin::Signed(BOB).into(), 101, @@ -194,7 +194,7 @@ fn rewards_calculation_works() { )); // Alice nominate project_101 with an amount of 5000*BSX with conviction 1x => equivalent to - // 10000*BSX locked + // 5000*BSX locked assert_ok!(Opf::vote( RawOrigin::Signed(ALICE).into(), 101, @@ -204,7 +204,7 @@ fn rewards_calculation_works() { )); // DAVE vote against project_102 with an amount of 3000*BSX with conviction 1x => equivalent - // to 6000*BSX locked + // to 3000*BSX locked assert_ok!(Opf::vote( RawOrigin::Signed(DAVE).into(), 102, @@ -213,7 +213,7 @@ fn rewards_calculation_works() { pallet_democracy::Conviction::Locked1x )); // Eve nominate project_102 with an amount of 5000*BSX with conviction 1x => equivalent to - // 10000*BSX locked + // 5000*BSX locked assert_ok!(Opf::vote( RawOrigin::Signed(EVE).into(), 102, @@ -235,15 +235,15 @@ fn rewards_calculation_works() { RuntimeEvent::Democracy(pallet_democracy::Event::Passed { ref_index: 0 }), ]); - // The total equivalent amount voted is 17000 = 23000 - 6000 - // Project 101: 13000 -> ~76.5%; Project 102: 4000 -> ~23.5% - // Distributed to project 101 -> 76%*100_000; Distributed to project 102 -> 23%*100_000 + // The total equivalent positive amount voted is 12000 + // Project 101: 7000 -> ~58.3%; Project 102: 2000 -> ~16.6% + // Distributed to project 101 -> 56%*100_000; Distributed to project 102 -> 16%*100_000 //Opf::calculate_rewards(::TemporaryRewards::get()); let reward_101 = WhiteListedProjectAccounts::::get(101).unwrap().amount; let reward_102 = WhiteListedProjectAccounts::::get(102).unwrap().amount; - assert_eq!(reward_101, 76000); - assert_eq!(reward_102, 23000); + assert_eq!(reward_101, 58000); + assert_eq!(reward_102, 16000); // Proposal Enactment did not happened yet assert_eq!(Spends::::contains_key(101), false); @@ -272,7 +272,7 @@ fn vote_removal_works() { assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); // Bob nominate project_102 with an amount of 1000 & conviction of 1 equivalent to - // 2000 + // 1000 assert_ok!(Opf::vote( RawOrigin::Signed(BOB).into(), 101, @@ -282,7 +282,7 @@ fn vote_removal_works() { )); // Eve nominate project_101 with an amount of 5000 & conviction 1x => equivalent to - // 10000 + // 5000 assert_ok!(Opf::vote( RawOrigin::Signed(EVE).into(), 101, @@ -293,7 +293,7 @@ fn vote_removal_works() { // ProjectFund is correctly updated let project_fund_before = ProjectFunds::::get(101); - assert_eq!(project_fund_before[0], 12000); + assert_eq!(project_fund_before[0], 6000); // Voter's funds are locked let locked_balance0 = @@ -320,7 +320,7 @@ fn vote_removal_works() { // ProjectFund is correctly updated let project_fund_after = ProjectFunds::::get(101); - assert_eq!(project_fund_after[0], 10000); + assert_eq!(project_fund_after[0], 5000); }) } @@ -330,7 +330,7 @@ fn vote_overwrite_works() { let batch = project_list(); let now = ::BlockNumberProvider::current_block_number(); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); - // Bob nominate project_101 with an amount of 1000 with a conviction of 2 => amount+amount*2 + // Bob nominate project_101 with an amount of 1000 with a conviction of 2 => amount*2 // is the amount allocated to the project assert_ok!(Opf::vote( RawOrigin::Signed(BOB).into(), @@ -346,11 +346,11 @@ fn vote_overwrite_works() { project_id: 101, })]); - // 3000 is allocated to project 101 + // 2000 is allocated to project 101 let mut funds = ProjectFunds::::get(101); - assert_eq!(funds[0], 3000); + assert_eq!(funds[0], 2000); - // Bob nominate project_103 with an amount of 5000 with a conviction of 1 => amount+amount*1 + // Bob nominate project_103 with an amount of 5000 with a conviction of 1 => amount // is the amount allocated to the project assert_ok!(Opf::vote( RawOrigin::Signed(BOB).into(), @@ -362,7 +362,7 @@ fn vote_overwrite_works() { // 10000 is allocated to project 103 funds = ProjectFunds::::get(103); - assert_eq!(funds[0], 10000); + assert_eq!(funds[0], 5000); // Voter's funds are locked let mut locked_balance0 = <::NativeBalance as fungible::hold::Inspect< @@ -371,7 +371,7 @@ fn vote_overwrite_works() { assert!(locked_balance0 > 0); assert_eq!(locked_balance0, 6000); - // Bob changes amount in project_103 to 4500 + // Bob changes amount in project_103 to 4500 with conviction 2=> 9000 assert_ok!(Opf::vote( RawOrigin::Signed(BOB).into(), 103, @@ -382,7 +382,7 @@ fn vote_overwrite_works() { // Allocated amount to project 103 is now 13500 funds = ProjectFunds::::get(103); - assert_eq!(funds[0], 13500); + assert_eq!(funds[0], 9000); // Storage was correctly updated let vote_info = Votes::::get(103, BOB).unwrap(); @@ -484,9 +484,9 @@ fn spends_creation_works_but_not_executed_after_claim_period() { let batch = project_list(); let voting_period = ::VotingPeriod::get(); let mut now = ::BlockNumberProvider::current_block_number(); - let amount1 = 400 * BSX; - let amount2 = 320 * BSX; - let amount3 = 280 * BSX; + let amount1 = 400 ; + let amount2 = 320 ; + let amount3 = 280 ; //round_end_block let round_end = now.saturating_add(voting_period); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); From b6add3c5be4944e729cf6930eb7c5e0353be7891 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Wed, 22 Jan 2025 22:39:08 +0900 Subject: [PATCH 49/62] cargo fmt --- substrate/frame/opf/src/benchmarking.rs | 6 ++---- substrate/frame/opf/src/functions.rs | 25 ++++++++++++++----------- substrate/frame/opf/src/tests.rs | 6 +++--- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index af7bbb627f05..cf3bbfdf11e2 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -92,9 +92,7 @@ mod benchmarks { } #[benchmark] - fn remove_vote( - r: Linear<1, 1000>, - ) -> Result<(), BenchmarkError> { + fn remove_vote(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -125,7 +123,7 @@ mod benchmarks { Ok(()) } -/* + /* #[benchmark] fn unlock_funds( r: Linear<1, { T::MaxWhitelistedProjects::get() }>, diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index a26bcc7dda38..2036ceab569e 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -21,13 +21,14 @@ impl Pallet { pub fn get_formatted_call(call: Call) -> ::RuntimeCall { call.into() } - pub fn conviction_amount(amount: BalanceOf, conviction:Democracy::Conviction) -> Option>{ - + pub fn conviction_amount( + amount: BalanceOf, + conviction: Democracy::Conviction, + ) -> Option> { let conviction_amount: BalanceOf = match conviction { - Democracy::Conviction::None => { - Percent::from_percent(10) * amount - } - _ => amount.saturating_mul(>::from(conviction).into()) + Democracy::Conviction::None => Percent::from_percent(10) * amount, + _ => + amount.saturating_mul(>::from(conviction).into()), }; Some(conviction_amount) } @@ -82,7 +83,8 @@ impl Pallet { .ok_or(Error::::NoProjectAvailable)?; let ref_index = infos.index; - let conviction_fund = Self::conviction_amount(amount, conviction).ok_or("Invalid conviction")?; + let conviction_fund = + Self::conviction_amount(amount, conviction).ok_or("Invalid conviction")?; // Create vote infos and store/adjust them let round_number = NextVotingRoundNumber::::get().saturating_sub(1); @@ -113,7 +115,8 @@ impl Pallet { let old_vote = Votes::::get(&project, &voter_id).ok_or(Error::::NoVoteData)?; let old_amount = old_vote.amount; let old_conviction = old_vote.conviction; - let old_conviction_amount = Self::conviction_amount(old_amount, old_conviction).ok_or("Invalid conviction")?; + let old_conviction_amount = + Self::conviction_amount(old_amount, old_conviction).ok_or("Invalid conviction")?; ProjectFunds::::mutate(&project, |val| { let mut val0 = val.clone().into_inner(); if is_fund { @@ -215,7 +218,8 @@ impl Pallet { let conviction = infos.conviction; let is_fund = infos.is_fund; - let conviction_fund = Self::conviction_amount(amount, conviction).ok_or("Invalid conviction")?; + let conviction_fund = + Self::conviction_amount(amount, conviction).ok_or("Invalid conviction")?; // Update Round infos let round_number = NextVotingRoundNumber::::get().saturating_sub(1); @@ -268,8 +272,7 @@ impl Pallet { if projects.clone().len() > 0 as usize { let total_positive_votes_amount = round.total_positive_votes_amount; let when = T::BlockNumberProvider::current_block_number(); - let total_votes_amount = - total_positive_votes_amount; + let total_votes_amount = total_positive_votes_amount; // for each project, calculate the percentage of votes, the amount to be distributed, // and then populate the storage Projects diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 5ad74d4ef347..3c09a846bcf1 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -484,9 +484,9 @@ fn spends_creation_works_but_not_executed_after_claim_period() { let batch = project_list(); let voting_period = ::VotingPeriod::get(); let mut now = ::BlockNumberProvider::current_block_number(); - let amount1 = 400 ; - let amount2 = 320 ; - let amount3 = 280 ; + let amount1 = 400; + let amount2 = 320; + let amount3 = 280; //round_end_block let round_end = now.saturating_add(voting_period); assert_ok!(Opf::register_projects_batch(RuntimeOrigin::signed(EVE), batch)); From 68e40c1f4b1a50a9c5554cc2b90dd3c7b46011d5 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 23 Jan 2025 11:25:21 +0900 Subject: [PATCH 50/62] benchmark voter funds release --- substrate/frame/opf/src/benchmarking.rs | 32 ++++++++++++------------- substrate/frame/opf/src/functions.rs | 3 ++- substrate/frame/opf/src/tests.rs | 11 ++++++--- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index cf3bbfdf11e2..2aa47918b5be 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -123,45 +123,45 @@ mod benchmarks { Ok(()) } - /* + #[benchmark] - fn unlock_funds( - r: Linear<1, { T::MaxWhitelistedProjects::get() }>, - ) -> Result<(), BenchmarkError> { - add_whitelisted_project::(r)?; + fn release_voter_funds(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + let account0: T::AccountId = account("project", r, SEED); + add_whitelisted_project::(r, caller.clone())?; ensure!( - WhiteListedProjectAccounts::::get().len() as u32 == r, + WhiteListedProjectAccounts::::contains_key(account0.clone()) == true, "Project_id not set up correctly." ); on_idle_full_block::(); - let mut when = T::BlockNumberProvider::current_block_number() + One::one(); - run_to_block::(when); + let mut when = T::BlockNumberProvider::current_block_number().saturating_add(One::one()); + T::BlockNumberProvider::set_block_number(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); - let caller_balance = T::NativeBalance::minimum_balance() * 1000000u32.into(); - let caller: T::AccountId = whitelisted_caller(); + let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); + let _ = T::NativeBalance::mint_into(&caller, caller_balance); - let account = WhiteListedProjectAccounts::::get()[(r - 1) as usize].clone(); + let value: BalanceOf = T::NativeBalance::minimum_balance() * 100u32.into() * (r).into(); Opf::::vote( RawOrigin::Signed(caller.clone()).into(), - account.clone(), + account0.clone(), value, true, Conviction::Locked1x, )?; - when = Votes::::get(account.clone(), caller.clone()).unwrap().funds_unlock_block; + when = Votes::::get(account0.clone(), caller.clone()).unwrap().funds_unlock_block; - run_to_block::(when); + T::BlockNumberProvider::set_block_number(when); on_idle_full_block::(); #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), account); + _(RawOrigin::Signed(caller.clone()), account0); Ok(()) - }*/ + } impl_benchmark_test_suite!(Opf, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/substrate/frame/opf/src/functions.rs b/substrate/frame/opf/src/functions.rs index 2036ceab569e..c59cc0ea3ef1 100644 --- a/substrate/frame/opf/src/functions.rs +++ b/substrate/frame/opf/src/functions.rs @@ -26,7 +26,8 @@ impl Pallet { conviction: Democracy::Conviction, ) -> Option> { let conviction_amount: BalanceOf = match conviction { - Democracy::Conviction::None => Percent::from_percent(10) * amount, + Democracy::Conviction::None => + amount.checked_div(&10u8.into()).unwrap_or_else(Zero::zero), _ => amount.saturating_mul(>::from(conviction).into()), }; diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 3c09a846bcf1..b6f62dd88dfe 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -479,7 +479,7 @@ fn not_enough_funds_to_vote() { } #[test] -fn spends_creation_works_but_not_executed_after_claim_period() { +fn spends_creation_works_but_claim_blocked_after_claim_period() { new_test_ext().execute_with(|| { let batch = project_list(); let voting_period = ::VotingPeriod::get(); @@ -512,7 +512,7 @@ fn spends_creation_works_but_not_executed_after_claim_period() { 103, amount3, true, - pallet_democracy::Conviction::None + pallet_democracy::Conviction::Locked1x )); // The Spends Storage should be empty @@ -531,8 +531,13 @@ fn spends_creation_works_but_not_executed_after_claim_period() { let expire = now.saturating_add(::ClaimingPeriod::get()); let info101 = WhiteListedProjectAccounts::::get(101).unwrap(); + + // Allocations including convictions: + // project_101: 40, project_102: 32, project_103: 280 + // Rewards percentage to be distributed: + // project_101: 11%, project_102: 9%, project_103: 79% (of 100,000) let spend101: types::SpendInfo = SpendInfo { - amount: 40000, + amount: 11000, valid_from: now, whitelisted_project: info101, claimed: false, From c0bea8601a7d587e42014c8d527ca21bb96c99ab Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 23 Jan 2025 14:51:44 +0900 Subject: [PATCH 51/62] need to fix claim benchmark --- substrate/bin/node/runtime/src/lib.rs | 3 ++ substrate/frame/opf/src/benchmarking.rs | 61 ++++++++++++++++++++++++- substrate/frame/opf/src/lib.rs | 5 +- substrate/frame/opf/src/mock.rs | 1 + substrate/frame/opf/src/types.rs | 1 + 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 63bd9d842c3e..ebd9062791c9 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1655,6 +1655,9 @@ impl pallet_opf::Config for Runtime { /// Period after which all the votes are resetted. type VoteValidityPeriod = VoteValidityPeriod; + /// Period after which the proposal is enacted + type EnactmentPeriod = EnactmentPeriod; + type BlockNumberProvider = System; /// This should be calculated as a percentage of inflation. diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index 2aa47918b5be..c2df9f197de0 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -61,6 +61,13 @@ fn add_whitelisted_project(n: u32, caller: T::AccountId) -> Result<() Ok(()) } +fn setup_pot_account() -> AccountIdOf { + let pot_account = crate::Pallet::::pot_account(); + let value = T::NativeBalance::minimum_balance().saturating_mul(1_000_000_000u32.into()); + let _ = T::NativeBalance::set_balance(&pot_account, value); + pot_account +} + #[benchmarks] mod benchmarks { use super::*; @@ -76,8 +83,8 @@ mod benchmarks { ); on_idle_full_block::(); - let when = frame_system::Pallet::::block_number() + One::one(); - run_to_block::(when); + let when = T::BlockNumberProvider::current_block_number()+ One::one(); + T::BlockNumberProvider::set_block_number(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); @@ -163,5 +170,55 @@ mod benchmarks { Ok(()) } + #[benchmark] + fn claim_reward_for(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + let account0: T::AccountId = account("project", r, SEED); + add_whitelisted_project::(r, caller.clone())?; + let _pot = setup_pot_account::(); + + ensure!( + WhiteListedProjectAccounts::::contains_key(account0.clone()) == true, + "Project_id not set up correctly." + ); + + let mut when = T::BlockNumberProvider::current_block_number()+ One::one(); + T::BlockNumberProvider::set_block_number(when); + ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); + let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); + + let _ = T::NativeBalance::mint_into(&caller, caller_balance); + let value: BalanceOf = T::NativeBalance::minimum_balance() * 100u32.into() * (r).into(); + Opf::::vote( + RawOrigin::Signed(caller.clone()).into(), + account0.clone(), + value, + true, + Conviction::Locked1x, + )?; + let round = VotingRounds::::get(0).unwrap(); + let round_end = round.round_ending_block; + // go to end of the round + T::BlockNumberProvider::set_block_number(round_end); + // go to claiming period + when = round_end.saturating_add(::EnactmentPeriod::get()); + let enact1 = ::EnactmentPeriod::get(); + let enact2 = ::EnactmentPeriod::get(); + T::BlockNumberProvider::set_block_number(when); + println!("round_end:{:?}\nenact2:{:?}\nnow:{:?}",round_end,enact2,when); + //SpendCreated? + let spend = Spends::::get(&account0.clone()).ok_or("Problem with spend creation")?; + + #[block] + { + on_idle_full_block::(); + } +/* + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), account0);*/ + + Ok(()) + } + impl_benchmark_test_suite!(Opf, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index c060d480d9e9..04d53ad369a2 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -107,6 +107,9 @@ pub mod pallet { #[pallet::constant] type VotingPeriod: Get>; + #[pallet::constant] + type EnactmentPeriod: Get>; + /// Maximum number projects that can be accepted by this pallet #[pallet::constant] type MaxProjects: Get; @@ -342,7 +345,7 @@ pub mod pallet { // Start Referendum let referendum_index = - Self::start_dem_referendum(proposal, T::EnactmentPeriod::get()); + Self::start_dem_referendum(proposal, ::EnactmentPeriod::get()); let mut new_infos = WhiteListedProjectAccounts::::get(&project_id) .ok_or(Error::::NoProjectAvailable)?; new_infos.index = referendum_index; diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index a7f9661fdce2..576a8b7f0d7f 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -159,6 +159,7 @@ impl pallet_opf::Config for Test { type VoteValidityPeriod = VotingPeriod; type BlockNumberProvider = System; type TemporaryRewards = TemporaryRewards; + type EnactmentPeriod = ConstU64<1>; type WeightInfo = (); } diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 5052ef2d4634..9d54c16a91da 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -40,6 +40,7 @@ pub use frame_support::{ }; pub use frame_system::{pallet_prelude::*, RawOrigin}; pub use scale_info::prelude::vec::Vec; +pub use sp_runtime::traits::UniqueSaturatedInto; pub use sp_runtime::{ traits::{ AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Hash, Saturating, From e9a832d1e44a2670d38c40aa668e983fb2524d1a Mon Sep 17 00:00:00 2001 From: ndkazu Date: Thu, 23 Jan 2025 14:54:50 +0900 Subject: [PATCH 52/62] clean-up --- substrate/frame/opf/src/benchmarking.rs | 19 ++++++------------- substrate/frame/opf/src/lib.rs | 6 ++++-- substrate/frame/opf/src/types.rs | 3 +-- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index c2df9f197de0..219c52aa4ffd 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -82,8 +82,7 @@ mod benchmarks { "Project_id not set up correctly." ); - on_idle_full_block::(); - let when = T::BlockNumberProvider::current_block_number()+ One::one(); + let when = T::BlockNumberProvider::current_block_number() + One::one(); T::BlockNumberProvider::set_block_number(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); @@ -108,7 +107,6 @@ mod benchmarks { "Project_id not set up correctly." ); - on_idle_full_block::(); let when = frame_system::Pallet::::block_number() + One::one(); run_to_block::(when); @@ -141,7 +139,6 @@ mod benchmarks { "Project_id not set up correctly." ); - on_idle_full_block::(); let mut when = T::BlockNumberProvider::current_block_number().saturating_add(One::one()); T::BlockNumberProvider::set_block_number(when); @@ -162,7 +159,6 @@ mod benchmarks { when = Votes::::get(account0.clone(), caller.clone()).unwrap().funds_unlock_block; T::BlockNumberProvider::set_block_number(when); - on_idle_full_block::(); #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), account0); @@ -182,11 +178,11 @@ mod benchmarks { "Project_id not set up correctly." ); - let mut when = T::BlockNumberProvider::current_block_number()+ One::one(); + let mut when = T::BlockNumberProvider::current_block_number() + One::one(); T::BlockNumberProvider::set_block_number(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); - + let _ = T::NativeBalance::mint_into(&caller, caller_balance); let value: BalanceOf = T::NativeBalance::minimum_balance() * 100u32.into() * (r).into(); Opf::::vote( @@ -202,18 +198,15 @@ mod benchmarks { T::BlockNumberProvider::set_block_number(round_end); // go to claiming period when = round_end.saturating_add(::EnactmentPeriod::get()); - let enact1 = ::EnactmentPeriod::get(); - let enact2 = ::EnactmentPeriod::get(); T::BlockNumberProvider::set_block_number(when); - println!("round_end:{:?}\nenact2:{:?}\nnow:{:?}",round_end,enact2,when); //SpendCreated? - let spend = Spends::::get(&account0.clone()).ok_or("Problem with spend creation")?; - + let _spend = Spends::::get(&account0.clone()).ok_or("Problem with spend creation")?; + #[block] { on_idle_full_block::(); } -/* + /* #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), account0);*/ diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 04d53ad369a2..59371bb7bb6d 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -344,8 +344,10 @@ pub mod pallet { let proposal = Self::create_proposal(who.clone(), call); // Start Referendum - let referendum_index = - Self::start_dem_referendum(proposal, ::EnactmentPeriod::get()); + let referendum_index = Self::start_dem_referendum( + proposal, + ::EnactmentPeriod::get(), + ); let mut new_infos = WhiteListedProjectAccounts::::get(&project_id) .ok_or(Error::::NoProjectAvailable)?; new_infos.index = referendum_index; diff --git a/substrate/frame/opf/src/types.rs b/substrate/frame/opf/src/types.rs index 9d54c16a91da..5c247305b592 100644 --- a/substrate/frame/opf/src/types.rs +++ b/substrate/frame/opf/src/types.rs @@ -40,11 +40,10 @@ pub use frame_support::{ }; pub use frame_system::{pallet_prelude::*, RawOrigin}; pub use scale_info::prelude::vec::Vec; -pub use sp_runtime::traits::UniqueSaturatedInto; pub use sp_runtime::{ traits::{ AccountIdConversion, BlockNumberProvider, Convert, Dispatchable, Hash, Saturating, - StaticLookup, Zero, + StaticLookup, UniqueSaturatedInto, Zero, }, Percent, SaturatedConversion, }; From 07c3191119fadd7cbb917a03ccc78e83ac240d00 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 25 Jan 2025 02:03:23 +0900 Subject: [PATCH 53/62] Benchmarking --- substrate/frame/opf/src/benchmarking.rs | 90 ++++++++++++------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index 219c52aa4ffd..07a4465a95b8 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -19,41 +19,44 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; - use crate::{Democracy::Conviction, Pallet as Opf}; //use pallet_distribution as Distribution; pub use frame_benchmarking::{ v1::{account, BenchmarkError}, v2::*, }; -use frame_support::ensure; +use frame_support::{ensure, traits::Hooks}; use frame_system::RawOrigin; use sp_runtime::traits::One; const SEED: u32 = 0; -fn run_to_block(n: BlockNumberFor) { - while frame_system::Pallet::::block_number() < n { - let b = frame_system::Pallet::::block_number(); - crate::Pallet::::on_finalize(b); - frame_system::Pallet::::on_finalize(b); - frame_system::Pallet::::set_block_number(b + One::one()); - frame_system::Pallet::::on_initialize(b); - crate::Pallet::::on_initialize(b); - } +pub fn next_block() { + let when = T::BlockNumberProvider::current_block_number().saturating_add( One::one()); + run_to_block::(when); + Democracy::Pallet::::on_initialize( + frame_system::Pallet::::block_number(), + ); + crate::Pallet::::on_idle( + frame_system::Pallet::::block_number(), + Weight::MAX, + ); } - -fn on_idle_full_block() { - let remaining_weight = ::BlockWeights::get().max_block; - let when = frame_system::Pallet::::block_number(); - frame_system::Pallet::::on_idle(when, remaining_weight); - crate::Pallet::::on_idle(when, remaining_weight); +pub fn run_to_block(n: ProvidedBlockNumberFor) { + while T::BlockNumberProvider::current_block_number() < n { + if T::BlockNumberProvider::current_block_number() > One::one() { + Democracy::Pallet::::on_finalize( + frame_system::Pallet::::block_number(), + ); + } + next_block::(); + } } fn add_whitelisted_project(n: u32, caller: T::AccountId) -> Result<(), &'static str> { let mut batch: Vec<_> = Vec::new(); - for _i in 0..n { - let project_id = account("project", n, SEED); + for i in 0..n+1 { + let project_id = account("project", i, SEED); batch.push(project_id); } let _ = crate::Pallet::::register_projects_batch(RawOrigin::Signed(caller).into(), batch); @@ -73,20 +76,20 @@ mod benchmarks { use super::*; #[benchmark] - fn vote(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { + fn vote(r: Linear<1, 500>){ let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); - add_whitelisted_project::(r, caller.clone())?; - ensure!( - WhiteListedProjectAccounts::::contains_key(account0.clone()) == true, - "Project_id not set up correctly." + let _ = add_whitelisted_project::(r, caller.clone()); + assert_eq!( + WhiteListedProjectAccounts::::contains_key(account0.clone()), + true ); let when = T::BlockNumberProvider::current_block_number() + One::one(); - T::BlockNumberProvider::set_block_number(when); + run_to_block::(when); - ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); - let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); + let _ = assert_eq!(VotingRounds::::get(0).is_some(), true); + let caller_balance = T::NativeBalance::minimum_balance() * 1000000u32.into(); let _ = T::NativeBalance::mint_into(&caller, caller_balance); @@ -94,11 +97,11 @@ mod benchmarks { #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), account0, value, true, Conviction::Locked1x); - Ok(()) + } #[benchmark] - fn remove_vote(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { + fn remove_vote(r: Linear<1, 500>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -107,7 +110,7 @@ mod benchmarks { "Project_id not set up correctly." ); - let when = frame_system::Pallet::::block_number() + One::one(); + let when = T::BlockNumberProvider::current_block_number() + One::one(); run_to_block::(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); @@ -130,7 +133,7 @@ mod benchmarks { } #[benchmark] - fn release_voter_funds(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { + fn release_voter_funds(r: Linear<1, 500>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -140,7 +143,7 @@ mod benchmarks { ); let mut when = T::BlockNumberProvider::current_block_number().saturating_add(One::one()); - T::BlockNumberProvider::set_block_number(when); + run_to_block::(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); @@ -158,7 +161,7 @@ mod benchmarks { when = Votes::::get(account0.clone(), caller.clone()).unwrap().funds_unlock_block; - T::BlockNumberProvider::set_block_number(when); + run_to_block::(when); #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), account0); @@ -167,7 +170,7 @@ mod benchmarks { } #[benchmark] - fn claim_reward_for(r: Linear<1, 1000>) -> Result<(), BenchmarkError> { + fn claim_reward_for(r: Linear<1, 500>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -179,12 +182,12 @@ mod benchmarks { ); let mut when = T::BlockNumberProvider::current_block_number() + One::one(); - T::BlockNumberProvider::set_block_number(when); + run_to_block::(when); ensure!(VotingRounds::::get(0).is_some(), "Round not created!"); let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); let _ = T::NativeBalance::mint_into(&caller, caller_balance); - let value: BalanceOf = T::NativeBalance::minimum_balance() * 100u32.into() * (r).into(); + let value: BalanceOf = T::NativeBalance::minimum_balance().saturating_mul( 100u32.into()).saturating_mul(r.into()); Opf::::vote( RawOrigin::Signed(caller.clone()).into(), account0.clone(), @@ -195,20 +198,17 @@ mod benchmarks { let round = VotingRounds::::get(0).unwrap(); let round_end = round.round_ending_block; // go to end of the round - T::BlockNumberProvider::set_block_number(round_end); + run_to_block::(round_end); + assert_eq!(Democracy::ReferendumCount::::get(), r, "referenda not created"); + // go to claiming period when = round_end.saturating_add(::EnactmentPeriod::get()); - T::BlockNumberProvider::set_block_number(when); - //SpendCreated? + run_to_block::(when); + let _spend = Spends::::get(&account0.clone()).ok_or("Problem with spend creation")?; - #[block] - { - on_idle_full_block::(); - } - /* #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), account0);*/ + _(RawOrigin::Signed(caller.clone()), account0); Ok(()) } From 920958ced496bf9e8dc8f52348abce0f88b9ed34 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 25 Jan 2025 02:04:39 +0900 Subject: [PATCH 54/62] cargo fmt --- substrate/frame/opf/src/benchmarking.rs | 30 +++++++++---------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index 07a4465a95b8..c9ee74dcc816 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -32,22 +32,15 @@ use sp_runtime::traits::One; const SEED: u32 = 0; pub fn next_block() { - let when = T::BlockNumberProvider::current_block_number().saturating_add( One::one()); + let when = T::BlockNumberProvider::current_block_number().saturating_add(One::one()); run_to_block::(when); - Democracy::Pallet::::on_initialize( - frame_system::Pallet::::block_number(), - ); - crate::Pallet::::on_idle( - frame_system::Pallet::::block_number(), - Weight::MAX, - ); + Democracy::Pallet::::on_initialize(frame_system::Pallet::::block_number()); + crate::Pallet::::on_idle(frame_system::Pallet::::block_number(), Weight::MAX); } pub fn run_to_block(n: ProvidedBlockNumberFor) { while T::BlockNumberProvider::current_block_number() < n { if T::BlockNumberProvider::current_block_number() > One::one() { - Democracy::Pallet::::on_finalize( - frame_system::Pallet::::block_number(), - ); + Democracy::Pallet::::on_finalize(frame_system::Pallet::::block_number()); } next_block::(); } @@ -55,7 +48,7 @@ pub fn run_to_block(n: ProvidedBlockNumberFor) { fn add_whitelisted_project(n: u32, caller: T::AccountId) -> Result<(), &'static str> { let mut batch: Vec<_> = Vec::new(); - for i in 0..n+1 { + for i in 0..n + 1 { let project_id = account("project", i, SEED); batch.push(project_id); } @@ -76,14 +69,11 @@ mod benchmarks { use super::*; #[benchmark] - fn vote(r: Linear<1, 500>){ + fn vote(r: Linear<1, 500>) { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); let _ = add_whitelisted_project::(r, caller.clone()); - assert_eq!( - WhiteListedProjectAccounts::::contains_key(account0.clone()), - true - ); + assert_eq!(WhiteListedProjectAccounts::::contains_key(account0.clone()), true); let when = T::BlockNumberProvider::current_block_number() + One::one(); run_to_block::(when); @@ -96,8 +86,6 @@ mod benchmarks { let value: BalanceOf = T::NativeBalance::minimum_balance() * 10u32.into() * (r).into(); #[extrinsic_call] _(RawOrigin::Signed(caller.clone()), account0, value, true, Conviction::Locked1x); - - } #[benchmark] @@ -187,7 +175,9 @@ mod benchmarks { let caller_balance = T::NativeBalance::minimum_balance() * 100000000u32.into(); let _ = T::NativeBalance::mint_into(&caller, caller_balance); - let value: BalanceOf = T::NativeBalance::minimum_balance().saturating_mul( 100u32.into()).saturating_mul(r.into()); + let value: BalanceOf = T::NativeBalance::minimum_balance() + .saturating_mul(100u32.into()) + .saturating_mul(r.into()); Opf::::vote( RawOrigin::Signed(caller.clone()).into(), account0.clone(), From 13fa7983eaad4b8a99dbf08d4df0563e63819001 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 25 Jan 2025 13:21:14 +0900 Subject: [PATCH 55/62] Update mock.rs --- substrate/frame/opf/src/mock.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 576a8b7f0d7f..4f89fc367883 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -56,7 +56,7 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type AccountId = AccountId; - type AccountData = pallet_balances::AccountData; + type AccountData = pallet_balances::AccountData; type Block = Block; type Lookup = IdentityLookup; } @@ -196,8 +196,9 @@ pub fn new_test_ext() -> sp_io::TestExternalities { (BOB, 200_000 * BSX), (DAVE, 150_000 * BSX), (EVE, 150_000 * BSX), - (pot_account, 150_000_000 * BSX), + (pot_account, 150_000_000 * BSX), ], + ..Default::default() } .assimilate_storage(&mut t) .unwrap(); From 56d53598aca05593ec5df228169fd2f2c88afd20 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 25 Jan 2025 16:25:51 +0900 Subject: [PATCH 56/62] Use boundedvec for batch submission --- substrate/frame/opf/src/benchmarking.rs | 14 +++++++------- substrate/frame/opf/src/lib.rs | 4 ++-- substrate/frame/opf/src/tests.rs | 8 ++++++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index c9ee74dcc816..d385ad406b9a 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -47,10 +47,10 @@ pub fn run_to_block(n: ProvidedBlockNumberFor) { } fn add_whitelisted_project(n: u32, caller: T::AccountId) -> Result<(), &'static str> { - let mut batch: Vec<_> = Vec::new(); - for i in 0..n + 1 { + let mut batch = BoundedVec::,::MaxProjects>::new(); + for i in 1..n + 1 { let project_id = account("project", i, SEED); - batch.push(project_id); + let _ = batch.try_push(project_id); } let _ = crate::Pallet::::register_projects_batch(RawOrigin::Signed(caller).into(), batch); @@ -69,7 +69,7 @@ mod benchmarks { use super::*; #[benchmark] - fn vote(r: Linear<1, 500>) { + fn vote(r: Linear<1, {T::MaxProjects::get()}>) { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); let _ = add_whitelisted_project::(r, caller.clone()); @@ -89,7 +89,7 @@ mod benchmarks { } #[benchmark] - fn remove_vote(r: Linear<1, 500>) -> Result<(), BenchmarkError> { + fn remove_vote(r: Linear<1, {T::MaxProjects::get()}>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -121,7 +121,7 @@ mod benchmarks { } #[benchmark] - fn release_voter_funds(r: Linear<1, 500>) -> Result<(), BenchmarkError> { + fn release_voter_funds(r: Linear<1, {T::MaxProjects::get()}>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -158,7 +158,7 @@ mod benchmarks { } #[benchmark] - fn claim_reward_for(r: Linear<1, 500>) -> Result<(), BenchmarkError> { + fn claim_reward_for(r: Linear<1, {T::MaxProjects::get()}>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; diff --git a/substrate/frame/opf/src/lib.rs b/substrate/frame/opf/src/lib.rs index 59371bb7bb6d..015d9d0381c3 100644 --- a/substrate/frame/opf/src/lib.rs +++ b/substrate/frame/opf/src/lib.rs @@ -305,7 +305,7 @@ pub mod pallet { #[pallet::call_index(1)] pub fn register_projects_batch( origin: OriginFor, - projects_id: Vec>, + projects_id: BoundedVec, T::MaxProjects>, ) -> DispatchResult { //T::AdminOrigin::ensure_origin_or_root(origin.clone())?; let who = T::SubmitOrigin::ensure_origin(origin.clone())?; @@ -358,7 +358,7 @@ pub mod pallet { } VotingRounds::::mutate(current_round_index, |round| *round = Some(round_infos)); - Self::deposit_event(Event::Projectslisted { when, projects_id }); + Self::deposit_event(Event::Projectslisted { when, projects_id: projects_id.to_vec() }); Ok(()) } diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index b6f62dd88dfe..25db481e93a8 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -32,8 +32,12 @@ pub fn next_block() { ); } -pub fn project_list() -> Vec> { - vec![101, 102, 103] +pub fn project_list() -> BoundedVec::MaxProjects> { + let mut batch = BoundedVec::::MaxProjects>::new(); + for i in 0..3{ + batch.try_push(101+i).expect("Should work"); +} +batch } pub fn run_to_block(n: BlockNumberFor) { From ed4dd3aec4d3aee2564756c43a20c2ece956daa2 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 25 Jan 2025 16:26:34 +0900 Subject: [PATCH 57/62] cargo fmt --- substrate/frame/opf/src/benchmarking.rs | 10 +++++----- substrate/frame/opf/src/mock.rs | 2 +- substrate/frame/opf/src/tests.rs | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index d385ad406b9a..245aa85324bb 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -47,7 +47,7 @@ pub fn run_to_block(n: ProvidedBlockNumberFor) { } fn add_whitelisted_project(n: u32, caller: T::AccountId) -> Result<(), &'static str> { - let mut batch = BoundedVec::,::MaxProjects>::new(); + let mut batch = BoundedVec::, ::MaxProjects>::new(); for i in 1..n + 1 { let project_id = account("project", i, SEED); let _ = batch.try_push(project_id); @@ -69,7 +69,7 @@ mod benchmarks { use super::*; #[benchmark] - fn vote(r: Linear<1, {T::MaxProjects::get()}>) { + fn vote(r: Linear<1, { T::MaxProjects::get() }>) { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); let _ = add_whitelisted_project::(r, caller.clone()); @@ -89,7 +89,7 @@ mod benchmarks { } #[benchmark] - fn remove_vote(r: Linear<1, {T::MaxProjects::get()}>) -> Result<(), BenchmarkError> { + fn remove_vote(r: Linear<1, { T::MaxProjects::get() }>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -121,7 +121,7 @@ mod benchmarks { } #[benchmark] - fn release_voter_funds(r: Linear<1, {T::MaxProjects::get()}>) -> Result<(), BenchmarkError> { + fn release_voter_funds(r: Linear<1, { T::MaxProjects::get() }>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -158,7 +158,7 @@ mod benchmarks { } #[benchmark] - fn claim_reward_for(r: Linear<1, {T::MaxProjects::get()}>) -> Result<(), BenchmarkError> { + fn claim_reward_for(r: Linear<1, { T::MaxProjects::get() }>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; diff --git a/substrate/frame/opf/src/mock.rs b/substrate/frame/opf/src/mock.rs index 4f89fc367883..6d3411e59807 100644 --- a/substrate/frame/opf/src/mock.rs +++ b/substrate/frame/opf/src/mock.rs @@ -196,7 +196,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { (BOB, 200_000 * BSX), (DAVE, 150_000 * BSX), (EVE, 150_000 * BSX), - (pot_account, 150_000_000 * BSX), + (pot_account, 150_000_000 * BSX), ], ..Default::default() } diff --git a/substrate/frame/opf/src/tests.rs b/substrate/frame/opf/src/tests.rs index 25db481e93a8..942189b7aa24 100644 --- a/substrate/frame/opf/src/tests.rs +++ b/substrate/frame/opf/src/tests.rs @@ -33,11 +33,11 @@ pub fn next_block() { } pub fn project_list() -> BoundedVec::MaxProjects> { - let mut batch = BoundedVec::::MaxProjects>::new(); - for i in 0..3{ - batch.try_push(101+i).expect("Should work"); -} -batch + let mut batch = BoundedVec::::MaxProjects>::new(); + for i in 0..3 { + batch.try_push(101 + i).expect("Should work"); + } + batch } pub fn run_to_block(n: BlockNumberFor) { From 70dc27e95f85bf3f4c878041c403ed07d30af07f Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 25 Jan 2025 16:39:41 +0900 Subject: [PATCH 58/62] Error control --- substrate/frame/opf/src/benchmarking.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index 245aa85324bb..14e612a03e7b 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -48,11 +48,11 @@ pub fn run_to_block(n: ProvidedBlockNumberFor) { fn add_whitelisted_project(n: u32, caller: T::AccountId) -> Result<(), &'static str> { let mut batch = BoundedVec::, ::MaxProjects>::new(); - for i in 1..n + 1 { + for i in 1..=n { let project_id = account("project", i, SEED); - let _ = batch.try_push(project_id); + let _ = batch.try_push(project_id).map_err(|_| "Exceeded max projects")?; } - let _ = crate::Pallet::::register_projects_batch(RawOrigin::Signed(caller).into(), batch); + crate::Pallet::::register_projects_batch(RawOrigin::Signed(caller).into(), batch)?; Ok(()) } From 3bc7b03eb8fe59370c48045f92bc099922c620dc Mon Sep 17 00:00:00 2001 From: ndkazu Date: Sat, 25 Jan 2025 17:25:36 +0900 Subject: [PATCH 59/62] trying to fix stack overflow --- substrate/frame/opf/src/benchmarking.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index 14e612a03e7b..ccebb3f93bd4 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -35,12 +35,17 @@ pub fn next_block() { let when = T::BlockNumberProvider::current_block_number().saturating_add(One::one()); run_to_block::(when); Democracy::Pallet::::on_initialize(frame_system::Pallet::::block_number()); + frame_system::Pallet::::on_initialize(frame_system::Pallet::::block_number()); crate::Pallet::::on_idle(frame_system::Pallet::::block_number(), Weight::MAX); + frame_system::Pallet::::on_idle(frame_system::Pallet::::block_number(), Weight::MAX); + crate::Pallet::::on_initialize(frame_system::Pallet::::block_number()); } pub fn run_to_block(n: ProvidedBlockNumberFor) { while T::BlockNumberProvider::current_block_number() < n { if T::BlockNumberProvider::current_block_number() > One::one() { Democracy::Pallet::::on_finalize(frame_system::Pallet::::block_number()); + crate::Pallet::::on_finalize(frame_system::Pallet::::block_number()); + frame_system::Pallet::::on_finalize(frame_system::Pallet::::block_number()); } next_block::(); } @@ -69,10 +74,10 @@ mod benchmarks { use super::*; #[benchmark] - fn vote(r: Linear<1, { T::MaxProjects::get() }>) { + fn vote(r: Linear<1, { T::MaxProjects::get() }>) -> Result<(), BenchmarkError>{ let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); - let _ = add_whitelisted_project::(r, caller.clone()); + add_whitelisted_project::(r, caller.clone())?; assert_eq!(WhiteListedProjectAccounts::::contains_key(account0.clone()), true); let when = T::BlockNumberProvider::current_block_number() + One::one(); @@ -81,11 +86,16 @@ mod benchmarks { let _ = assert_eq!(VotingRounds::::get(0).is_some(), true); let caller_balance = T::NativeBalance::minimum_balance() * 1000000u32.into(); - let _ = T::NativeBalance::mint_into(&caller, caller_balance); + T::NativeBalance::mint_into(&caller, caller_balance)?; let value: BalanceOf = T::NativeBalance::minimum_balance() * 10u32.into() * (r).into(); #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), account0, value, true, Conviction::Locked1x); + _(RawOrigin::Signed(caller.clone()), account0.clone(), value, true, Conviction::Locked1x); + + // Verify the vote was recorded + let vote_info = Votes::::get(account0.clone(), caller.clone()).ok_or("Vote not recorded!").unwrap(); + assert_eq!(vote_info.amount, value, "Vote value mismatch!"); + Ok(()) } #[benchmark] From d838f9e1f9e55d3eea905a2dc44315d6ed3dfb57 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 28 Jan 2025 00:18:24 +0900 Subject: [PATCH 60/62] Fix cargo.toml --- Cargo.lock | 2 -- substrate/frame/opf/Cargo.toml | 15 +++------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8dc4f152281..1fba887f6aff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14573,14 +14573,12 @@ dependencies = [ "log", "pallet-assets 29.1.0", "pallet-balances 28.0.0", - "pallet-conviction-voting 28.0.0", "pallet-democracy 28.0.0", "pallet-preimage 28.0.0", "pallet-scheduler 29.0.0", "pallet-sudo 28.0.0", "pallet-timestamp 27.0.0", "pallet-transaction-payment 28.0.0", - "pallet-transaction-payment-rpc-runtime-api 28.0.0", "parity-scale-codec", "scale-info", "serde", diff --git a/substrate/frame/opf/Cargo.toml b/substrate/frame/opf/Cargo.toml index d2c7c662d39b..54fd68852503 100644 --- a/substrate/frame/opf/Cargo.toml +++ b/substrate/frame/opf/Cargo.toml @@ -21,13 +21,9 @@ frame-benchmarking = { optional = true, workspace = true, default-features = fal frame-support = { workspace = true, default-features = false } frame-system = { workspace = true, default-features = false } log = { workspace = true } -pallet-conviction-voting = { workspace = true, default-features = false } pallet-democracy = { workspace = true, default-features = true } -pallet-scheduler = { workspace = true, default-features = false } -scale-info = { features = [ - "derive", -], workspace = true, default-features = false } -serde = { optional = true, workspace = true, default-features = true } +scale-info = { features = ["derive"], workspace = true } +serde = { features = ["derive"], optional = true, workspace = true, default-features = true } sp-core = { workspace = true, default-features = false } sp-io = { workspace = true, default-features = false } sp-runtime = { workspace = true, default-features = false } @@ -41,7 +37,6 @@ pallet-scheduler = { workspace = true, default-features = true } pallet-sudo = { workspace = true, default-features = true } pallet-timestamp = { workspace = true, default-features = true } pallet-transaction-payment = { workspace = true, default-features = true } -pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } [features] default = ["std"] @@ -51,14 +46,12 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "pallet-conviction-voting/runtime-benchmarks", "pallet-democracy/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-transaction-payment/runtime-benchmarks", - "scale-info/std", "serde", "sp-runtime/runtime-benchmarks", ] @@ -70,25 +63,23 @@ std = [ "log/std", "pallet-assets/std", "pallet-balances/std", - "pallet-conviction-voting/std", "pallet-democracy/std", "pallet-preimage/std", "pallet-scheduler/std", "pallet-sudo/std", "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", "pallet-transaction-payment/std", "scale-info/std", "sp-core/std", "sp-io/std", "sp-runtime/std", + "sp-std/std", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-assets/try-runtime", "pallet-balances/try-runtime", - "pallet-conviction-voting/try-runtime", "pallet-democracy/try-runtime", "pallet-preimage/try-runtime", "pallet-scheduler/try-runtime", From bc7e110ff9249894ad9553816bde536e4a73ffc9 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 28 Jan 2025 00:30:09 +0900 Subject: [PATCH 61/62] cargo fmt --- substrate/frame/opf/Cargo.toml | 6 ++++-- substrate/frame/opf/src/benchmarking.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/substrate/frame/opf/Cargo.toml b/substrate/frame/opf/Cargo.toml index 54fd68852503..af3f84c0d232 100644 --- a/substrate/frame/opf/Cargo.toml +++ b/substrate/frame/opf/Cargo.toml @@ -16,7 +16,9 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { workspace = true, default-features = false } +codec = { features = [ + "derive", +], workspace = true } frame-benchmarking = { optional = true, workspace = true, default-features = false } frame-support = { workspace = true, default-features = false } frame-system = { workspace = true, default-features = false } @@ -24,7 +26,6 @@ log = { workspace = true } pallet-democracy = { workspace = true, default-features = true } scale-info = { features = ["derive"], workspace = true } serde = { features = ["derive"], optional = true, workspace = true, default-features = true } -sp-core = { workspace = true, default-features = false } sp-io = { workspace = true, default-features = false } sp-runtime = { workspace = true, default-features = false } sp-std = { workspace = true, default-features = true } @@ -37,6 +38,7 @@ pallet-scheduler = { workspace = true, default-features = true } pallet-sudo = { workspace = true, default-features = true } pallet-timestamp = { workspace = true, default-features = true } pallet-transaction-payment = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } [features] default = ["std"] diff --git a/substrate/frame/opf/src/benchmarking.rs b/substrate/frame/opf/src/benchmarking.rs index ccebb3f93bd4..20a08632cfcb 100644 --- a/substrate/frame/opf/src/benchmarking.rs +++ b/substrate/frame/opf/src/benchmarking.rs @@ -74,7 +74,7 @@ mod benchmarks { use super::*; #[benchmark] - fn vote(r: Linear<1, { T::MaxProjects::get() }>) -> Result<(), BenchmarkError>{ + fn vote(r: Linear<1, { T::MaxProjects::get() }>) -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let account0: T::AccountId = account("project", r, SEED); add_whitelisted_project::(r, caller.clone())?; @@ -93,7 +93,9 @@ mod benchmarks { _(RawOrigin::Signed(caller.clone()), account0.clone(), value, true, Conviction::Locked1x); // Verify the vote was recorded - let vote_info = Votes::::get(account0.clone(), caller.clone()).ok_or("Vote not recorded!").unwrap(); + let vote_info = Votes::::get(account0.clone(), caller.clone()) + .ok_or("Vote not recorded!") + .unwrap(); assert_eq!(vote_info.amount, value, "Vote value mismatch!"); Ok(()) } From c695fec201ef277b89a569c76e1ae3966d329633 Mon Sep 17 00:00:00 2001 From: ndkazu Date: Tue, 28 Jan 2025 00:44:41 +0900 Subject: [PATCH 62/62] Prdoc fix --- prdoc/pr_6994.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_6994.prdoc b/prdoc/pr_6994.prdoc index 32805f2c145e..4f982bd5081a 100644 --- a/prdoc/pr_6994.prdoc +++ b/prdoc/pr_6994.prdoc @@ -16,6 +16,6 @@ crates: - name: frame-support bump: none - name: polkadot-sdk - bump: patch + bump: minor - name: polkadot bump: none \ No newline at end of file