Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Mainnet Parameters

Background

This is a proposal for configuring the blockchain parameters for Carthage and Mainnet network releases of the Joystream project.

Scope

Parameters

By parameters we here mean genesis configuration which sets initial state of the chain and FRAME pallet configuration trait implementations, where we are most focused on key values of basic data types and some key functions, and less so on relationships between pallets and other very boiler plate values which already are settled.

Networks

The work here is only focused on production mode of Mainnet and Carthage, not the staging or testing modes of these networks. The implementation of this proposal should attempt to try to update values for these other modes in concert. Also notice that unlike past testnets, Carthage will be very different in terms key council time period durations, which before were quite compressed for operational purposes. This may mean we may not even complete a full council before mainnet.

No model?

Originally, this proposal was supposed to be made based on an integrated model of the Joystream network, as described her: Joystream/joystream#2914. This took way too long, there was very large amount of baseline work and digging, both technically and practically, which ended up making that effort take way longer than originally anticipated, and the near term marginal value over this more practical proposal was unclear. Add to that other mounting priorities, as well as the fact that in the near term after launch we are likely to be at a relatively low level of on-chain utilization, and it was clear that a more practical approach was advisable.

Missing

There are still a few things missing from this proposal that are needed for it to be complete

  1. Configuration of key validation related pallets has been skipped (except staking pallet), and how exactly we implement the launch process goal of starting with POA validator set, and then transition to NPoS, has not yet been fully settled.
  2. New values are needed for key core weights for baseline extrinsic and block processing, as well as database interaction, and this can only be done on correct reference hardware and an otherwise finalized configuration.
  3. Cost of key cleanup transactions that remove objects from state storage are needed to have accurate lower bound constraints on a range of state bloat bond fees. These costs can only be derived on correct reference hardware and an otherwise finalized configuration, and it also requires a bit of tooling to capture the relevant values in a convenient way, for which there is a pending task here: Joystream/joystream#4266. We specifically have to update
    • TransactionByteFee = 0.1*currency::MILLICENTS
    • WeightToFeePolynomial::polynomial has two inlined values
      • let p = super::currency::CENTS;
      • let q = 100 * Balance::from(ExtrinsicBaseWeight::get());

Proposal

Market Cap and Resource Pricing

The key purpose of fees is to deter possible abuse of the liveness of the system by hostile actors, there is also a secondary purpose of rationing access among the most highest value uses, but this can be done with a tip market which needs no lower bound pricing.

Such an attack would essentially boil down to exhausting the throughput of the system for some period of time. It is not feasible to really fully anticipate the external payoff that an attacker could derive from such an attack, hence one just has to make baseline assumptions which seem plausible based on the context at hand. For our chain I believe it is safe to assume that we only need to deter "vandalism", where primary payoff is just hedonic or reputational. To make things concrete, let us just assume that this type of attacker has willingness to pay of $10,000 USD/day to entirely block the transactional activity in the system. Based on this assumption we need to work back how much $JOY to charge to exhaust block space and computation limits to deter this type of activity. This implies that the system has to, in its fee system, be aware of the rough $USD price of the $JOY asset. In fact, for this reason we explicitly specify an estimate of this

const BASE_UNIT_ISSUANCE_USD_MCAP: Balance = 60_000_000;

It is not pivotal that this value is entirely accurate, and other production chains at scale seem to be way off: Joystream/joystream#4097. For practical purposes, all $JOY parameter values are also therefore specified in terms of $USD in literal terms in the proposal below, where 100$ USD would be specified as dollars!(100).

Using the parameters for block resource constraints, specifically weight and length, along with pricing of these resources, we can determine the cost of conducting an 24 hour congestion attack, and then we can compare this to our hypothetical willingness to pay. Notice that with our current block time of 6s, there are 24*60*60/6 = 14400 blocks per 24 hours.

Our approach will be to specifically check two idealised cases which attempt to purely exploit one resources without consuming any othe other. Depending on the particular portfolio of extrinsics available at any given time, this may not be entirely feasible, but these two idealised cases are lower bounds on doing one of these sorts of congestion attacks. Importantly, we only need to consider these two extreme cases, because the cheapest attack has to be to exhaust the cheapest of the two limits purely, any deviation is wasteful.

Exhausting Block space

The block length is set to

/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used
/// by  Operational  extrinsics.
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);

pub RuntimeBlockLength: BlockLength =
    BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);

which boils down to NORMAL_DISPATCH_RATIO*5MB=3.75MB block space for normal extrinsics, hence using TransactionByteFee we see that the price of filling a block purely by occupying with compute-free extrinsic is 3.75MB*1024*1024*0.1*currency::MILLICENTS = 393216*currency::MILLICENTS = 393.216*currency::CENTS = $3.93216. This means that to do this for 24 hours you must pay 14400*$3.93216 = $56623.104, which is well above our $10,000 willingness to pay.

Exhausting Block compute (weight)

The block weight for normal extrinsics is set to

/// We allow for 2 seconds of compute with a 6 second average block time.
pub const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND;

pub RuntimeBlockWeights: BlockWeights =
    ...
    .for_class(DispatchClass::Normal, |weights| {
        weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);
    })

which boils down to NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT=0.75*2*1_000_000_000_000=1.5e+12, combined that with cost of ExtrinsicBaseWeight = 85_795 * WEIGHT_PER_NANOS = 85_795 * 1000 = 85795000 is set to 0.01*CENTS=$0.0001 (by virtue of WeightToFeeCoefficients::polynomial), which means total cost of filling a block using only weight is (1.5e+12/85795000)*$0.0001=$1.74835363. This means that to do this for 24 hours you must pay 14400*$1.74835363 = 25176.2923 US$, which is well above our $10,000 willingness to pay.

Governance: Testnet vs Mainnet

NB: This is the most important section of this entire document

On testnet, the primary purpose of the testnet was to identify, incentivise and train a sufficiently large pool of prospective high value mainnet participants across a range of roles. Just reviewing the current testnet scale shows how massive the system is

  • Council: 5
  • Content WG: 11
  • Membership WG: 1
  • Marketing WG: 12
  • Builders WG: 9
  • Distribution WG: 18
  • Storage WG: 14
  • HR WG: 13
  • Forum WG: 5 Total: 88

Keep in mind that this does not count

  • creators,
  • stakers: validators & nominators
  • bounty workers

who also need to be paid for their contributions. A lot of these numbers have no relationship between the actual output of the groups, and its very clear that monitoring and coordination is not working very well. Under real circumstances, being large is very costly, both in terms of expenditure and coordination, and rotating people in and out is very costly in terms of knowledge transfer. Likewise, relying on large numbers of people with non-full time involvement in the operations is also a very costly way of trying to achieve longer term objectives. Additionally, a very large pool of tokens was being distributed from the genesis supply, which is a one time event, and under normal operational circumstances, the budget available to reward all this activity will need to be sensitive to ongoing market conditions. For all of these reasons, this meant that the testnet had a much larger number of participants, and also much larger throughput of participants, than what one would have if the DAO was being run with the narrow objective of operating and building the system out in the optimal way, subject to real budget constraints at the time. This means that the DAO now is being setup with initial parameters which reflect that the system will need to have

  1. a smaller number of participants across the board.
  2. a greater reliance on people who devote a large share of their time.
  3. a much smaller change, over time, in the set of people involved.

Council & Elections

One of the primary learnings from our past testnets is that there is a significant risk of free-riding and coordination failure within the council. This has been demonstrated across a broad range of values, from 20 down to 5. The virtue of a large council is that it has better safeguards against very bad, or harmful proposals, as those are for proposal types with high stakes, and thus large quorum and threshold requirements, and so a small minority of good actors can be very impactful. The cost of a big council is however also very substantial, as described. Another big concern with large councils is that it becomes much harder for voters to monitor impact of each council member, in order to vote accordingly. Another big change in the role of the council on mainnet will be to be in charge of execution of long term strategies over longer time frames. On testnet, the primary purpose was mainly to just optimize for the highly stable scoring rules that Jsgenesis provided, and to accommodate space for newcomers into the council for training purposes. On mainnet the purpose will much more be to ensure work towards long term goals in an effective way while adapting to shifting constraints. This requires a substantial level of continuity within he council, as the cost of knowledge transfer between exiting and joining members will be very large. For these reasons, the following is being proposed for the initial parameters

  • there should be a very small council, specifically with 3 members.
  • this council should be very stable, from one term to the next.
  • the purpose of council elections is primary as an accountability mechanism to evict councils trying to pass harmful proposals, which will have a large constitutionality.

This is in essence an optimistic governance scheme, where you try to kick out bad actors ex-post, rather than have everyone check and be involved in everything to make sure everything is good ex-ante. For this reason, elections will also active almost continuously, with very long announcing periods, and short idle periods, in order to compress the period of time where the community has no recourse, which the subsequently would waste time by stretching out the total timeline of all proposals with larger constitutionality. Specifically it is being proposed that

  • Announcing Period: 14 days
  • Voting Period: 3 days
  • Reveal Period: 3 days
  • Idle Period: 1 days

Total: 21 days = 3 weeks

Proposals

Proposals largely are put into categories of risk based on their constitutionality, where runtime upgrades are the riskiest for example. Since there is only one spending proposal, it was configured with low limits, in order to not require constitutionality > 1, which would be very impractical for small spends. The council can consider different spending proposals, with different limits, in the future. In general, configurations reflect that

  • discussing and deliberation around proposals should be settled before proposal is created, otherwise voting times get too long, and this impacts council cycle length, which then increases latency of high constitutionality proposals.
  • most proposals allow one council member to not be present for quorum.
  • slashing requires full quorum and threshold.
  • approval threshold is largely set to all
  • vetoing is for mistakes over which there is consensus, i.e. fat fingers or errors detected during gracing, not to resolve contention, that is constitutionality.
  • anything you want to be vetoable need needs reasonable grace period latency.

Budgets & Spending and Inflation

Note: I am just using 10K as a placeholder for some notional average salary, many engineers, product people or other high value contributors may command much more or much less than this depending circumstances. Also people in the same working group or even council need not earn the same fixed reward, this is not realistic or efficient. The DAO should not take these numbers as constraints, just assumptions in a very rough calculation.

Spending, in the form of minting, can be broken down into the following categories with corresponding monthly budget estimates for our current scale:

  1. Council: Here we have 3 people, who are FTE, so lets say 3x$10K=$30K
  2. Spending Proposals: $5K
  3. Content, Membership, Forum, HR, Marketing WGs: These groups have to start very modestly, as there are at this point very limited strategic objectives driven by the DAO that can be used to directly inform resource allocation and priorities, and they all should largely scale with the consumer adoption scale of the platform, which today is in in it's infancy. It may also be advisable that some council members simultaneously do some of these lead roles in the beginning. Simple average of $2K becomes $10K in total.
  4. Gateway WG: This group is different, as it may be worths pending resources on in a way which is out of line with current utlization, precisely to catalyse adoption, by building new verticalised audiences and drawing in creators, and also to help guide builders in how to make it easier to launch and operate gateways. Using an estimate of $30K.
  5. Builders: Similar to gateway group, and builders must support both infrastructure and gateways, so using an estimate of 5 people at $10K, giving total of $50K.
  6. Storage WG: Lets say there are 10K active channels, each one with 10 videos, each of 20min length at 1080p (H264) i.e. 10GB, in total is 10,000*10*10GB = 1PB, which with replication factor 5 is 5PB in total storage. With storage cost of $20K per month per 1PB, including basic bandwidth budget, this equals $100K
  7. Distribution WG: Lets say there are 10,000 DAUs each consuming about 10mins of content per day, this is 10000x5GBx30=1500000GB egress from distributors per month, and at price of $0.02/GB on AWS this works out to $30K
  8. Creators payouts: Lets say there are 1K creators involved, each receiving $500 on average/month, this nets out to $500K (omg!).
  9. Stakers (Validators & Nominators): We use staking configuration where yearly inflation rate I(x) at ideal staking rate x_ideal=50%, is 5%, so I(x_ideal) = I(50%)=3%, and since we are using the standard staking curves this is also the maximum overall inflation rate. Hence, with a market cap of $60M, this amounts to (0.03)*$60M/12=$150K.

Total = $30K + $5K + $10K + $30K + $50K + $100K + $30K + $500K + $150K = $905K

This gives an annual total inflation rate of roughly $905K/60M = 10%. Specifically for the council we want to give them a budget that covers (1)-(8), which is $755K. With BudgetRefillPeriod: BlockNumber = days!(1) this means budget_increment = $755K/30 days = $25K=dollars!(25_000)

State Bloat Pricing

We will use a new universal pricing function for specifying all bloat parameter values, is done in Polkadot and Kusama, as reviewed here. Currently we have one such function, deposit, but it is broken (since there is not fiat scaling) and it is not used. We will replace it with a new function

// common/costs.rs

/// Computes bloat bond for a single mapping.
/// Unlike `deposit` in Dotsama codebase, which is both or no and multiple mappings, this is explicitly for a single mapping.
/// When forced to specify bond values that cover multiple mappings, such as for FRAME pallets, then invoke multiple times, so
/// = compute_single_bloat_bond(..).saturating_add(compute_single_bloat_bond(...))
pub const fn compute_single_bloat_bond(total_byte_size: u32, floor: Option<Balance>) -> Balance

which uses prices below

// lib.rs
parameter_types! {

  // The price applied to
  // We assume that if balances map is protected purely by the `ExistentialDeposit`, then
  // this would be sufficient for any other storage map as well. In fact, the `balances`
  // pallet this is also covering the storage cost of `AccountData` which currently has form
  //
  // pub struct AccountData<Balance> {
  //  pub free: Balance,
  //  pub reserved: Balance,
  //  pub misc_frozen: Balance,
  //  pub fee_frozen: Balance,
  // }
  //
  // This has `sizeof(AccountData<u128>)` = `4*sizeof(u128)` bytes = `512` bytes, hence
  // and implied upper bound for the variable cost would be `ExistentialDeposit/512`,
  // so we use that
  pub const FixedBloatBondPrice: Balance = ExistentialDeposit::get();
  pub const PerByteBloatBondPrice: Balance = ExistentialDeposit::get().saturating_div(512);
}

const_assert!(FixedBloatBondPrice::get() > 0);
const_assert!(PerByteBloatBondPrice::get() > 0);

Also make sure to fix this: Joystream/joystream#4220

Lastly, for many cases where we have bloat bonds, we will be looking to make sure they are big enough to be attractive to cleanup, that is they have to be at least as expensive as the most expensive inclusion fee to remove entity from storage. For this reason, for many of the deposits we ensure that the final deposit is at least as big as this fee, plus an additional bit of profit to make it worth doing. **Unfortunately, this means we need to have estimates of the inclusion fees of such operations built into the configuration of the chain itself, which is hassle to remember to maintain. Ideally, these could be derived by just calling TransactionPayment::query_info in the configuration, but the problem is that this is not a const computation (e.g. it depends on fee multiplier in storage). There could be a way to work around this and sort of replicate a const version of all this logic, but it would be extra work, and it would itself need to be kept in synch with whatever ends up changing in the TransactionPayment pallet. Sadly they did not expose purely functional versions of their fee logic. Unless someone improves upon this, or until someone does, I suggest we explain this in the configuration source itself. We introduce a new bloat bond computer called compute_single_bloat_bond_with_cleanup for such cases. The tool for coming up with these fee bounds has been planned here: Joystream/joystream#4266

parameter_types! {

  /// How much above the cleanup transaction is used as premium on cleanup of storage deposits.
  /// This value is used when setting lower bound for total bloat bonds for creating storage items, so
  /// as to make cleanup profitable, despite inclusion fee of cleanup transaction.
  pub const StorageDepositCleanupProfit: Balance = currency::CENTS;

  /// Remark: Profitable Deposit Cleanup
  /// For a number of the bloat bond deposits in the runtime, it must be ensured that the cleanup transaction
  /// does not make actual cleanup unprofitable, and this is ensured by making sure the initial storage deposit
  /// being sufficiently big, which ultimately requires the runtime to have assumptions about the inclusion fees
  /// of cleanup extrinsics when setting corresponding storage deposits. Ideally, such fees could be derived in the
  /// configuration code, but this is not practical at this time, so it has to be provided manually. Lastly, we add
  /// `StorageDepositCleanupProfit` to this lower bound to make it worth doing.

}

pub const fn compute_single_bloat_bond_with_cleanup(total_byte_size: u32, max_cleanup_inclusion_fee: Balance) -> Balance {
  compute_single_bloat_bond(total_byte_size, Some(max_cleanup_inclusion_fee.saturating_add(StorageDepositCleanupProfit::get())))
}

pub const fn stake_with_cleanup(stake: Balance, max_cleanup_inclusion_fee: Balance) {
  stake.max(max_cleanup_inclusion_fee.saturating_add(StorageDepositCleanupProfit::get()))
}

note, there is another example wehre we need this, not just token... we just skipped it so far.

Utils

const BYTES_IN_MB: u32 = 1024*1024;
const BYTES_IN_GB: u32 = 1024*BYTES_IN_MB;

macro_rules! mega_bytes{
    ($a:expr)=>{
        {
            $a*(1024*1024)
        }
    }
}

macro_rules! hours{
    ($a:expr)=>{
        {
            $a*((60 * 60) / ExpectedBlockTime::get())
        }
    }
}

macro_rules! days{
    ($a:expr)=>{
        {
            $a*hours!(24)
        }
    }
}

macro_rules! dollars{
    ($a:expr)=>{
        {
            $a*currency::DOLLARS
        }
    }
}

macro_rules! cents{
    ($a:expr)=>{
        {
            $a*currency::CENTS
        }
    }
}

macro_rules! monthly_dollars_to_per_block{
    ($a:expr)=>{
        {
          dollars!(a).saturating_div(days!(30))
        }
    }
}

Core Weight values

We are currently relying on some base level weight values generated for Substrate, and we will not be using them (as agreed here), new values will be specified below in places marked as <TO BE DEFINED>.

//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: <TO BE DEFINED> (Y/M/D)
//!
//! SHORT-NAME: `block`, LONG-NAME: `BlockExecution`, RUNTIME: `Development`
//! WARMUPS: `10`, REPEAT: `100`
//! WEIGHT-PATH: `./frame/support/src/weights/`
//! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1`, WEIGHT-ADD: `0`

// Executed Command:
//   ./target/production/substrate
//   benchmark
//   overhead
//   --chain=dev
//   --execution=wasm
//   --wasm-execution=compiled
//   --weight-path=./frame/support/src/weights/
//   --warmup=10
//   --repeat=100
pub const BlockExecutionWeight: Weight = <TO BE DEFINED> * WEIGHT_PER_NANOS;

//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: <TO BE DEFINED> (Y/M/D)
//!
//! SHORT-NAME: `extrinsic`, LONG-NAME: `ExtrinsicBase`, RUNTIME: `Development`
//! WARMUPS: `10`, REPEAT: `100`
//! WEIGHT-PATH: `./frame/support/src/weights/`
//! WEIGHT-METRIC: `Average`, WEIGHT-MUL: `1`, WEIGHT-ADD: `0`

// Executed Command:
//   ./target/production/substrate
//   benchmark
//   overhead
//   --chain=dev
//   --execution=wasm
//   --wasm-execution=compiled
//   --weight-path=./frame/support/src/weights/
//   --warmup=10
//   --repeat=100
pub const ExtrinsicBaseWeight: Weight = <TO BE DEFINED> * WEIGHT_PER_NANOS;

//// no infomation about what machine these were generated on
pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight {
	read: <TO BE DEFINED> * constants::WEIGHT_PER_NANOS,
	write: <TO BE DEFINED> * constants::WEIGHT_PER_NANOS,
};

pallet_balances

No change

Config

parameter_types! {
    // For weight estimation, we assume that the most locks on an individual account will be 50.
    // This number may need to be adjusted in the future if this assumption no longer holds true.
    pub const MaxLocks: u32 = 50;

    /// The maximum number of named reserves that can exist on an account.
    pub const MaxReserves: u32 = 50;

    /// The minimum amount required to keep an account open.
    pub const ExistentialDeposit: Balance = currency::MILLICENTS;
}

GenesisConfig

  • balances
    • Type: Vec<(T::AccountId, T::Balance)>
    • Description: Initial balances.
    • Values: to be specified by Martin.

pallet_vesting

No change

Config

parameter_types! {
    // The minimum amount transferred to call vested_transfer.
    pub const MinVestedTransfer: Balance = 100 * currency::CENTS;
}

GenesisConfig

  • vesting
    • Type: Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, BalanceOf<T>)>
    • Description: Initial vesting schedules applied to accounts.
    • Values: to be specified by Martin.

frame_system

No change

Config

/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers.
/// This is used to limit the maximal weight of a single extrinsic.
const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10);
/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used
/// by  Operational  extrinsics.
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
/// We allow for 2 seconds of compute with a 6 second average block time.
pub const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND;

parameter_types! {
    pub const BlockHashCount: BlockNumber = 2400;
    pub const Version: RuntimeVersion = VERSION;
    pub RuntimeBlockLength: BlockLength =
        BlockLength::max_with_normal_ratio(5 * 1024 * 1024;, NORMAL_DISPATCH_RATIO);
    pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()
        .base_block(BlockExecutionWeight::get())
        .for_class(DispatchClass::all(), |weights| {
            weights.base_extrinsic = ExtrinsicBaseWeight::get();
        })
        .for_class(DispatchClass::Normal, |weights| {
            weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);
        })
        .for_class(DispatchClass::Operational, |weights| {
            weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);
            // Operational transactions have some extra reserved space, so that they
            // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.
            weights.reserved = Some(
                MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT
            );
        })
        .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
        .build_or_panic();
}

const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct());

CallFiter

Frozen in rollout plan

Here we block everything except Sudo and essential validation pallets required for PoA to work. See this issue for more context Joystream/joystream#4255 (comment).

Call::System(method) =>
// All methods except the remark call
{
    !matches!(method, frame_system::Call::<Runtime>::remark { .. })
}
// confirmed that Utility.batch dispatch does not bypass filter.
Call::Utility(_) => true,
Call::Babe(_) => true,
Call::Timestamp(_) => true,
Call::Grandpa(_) => true,
Call::Sudo(_) => true,

Supervised in rollout plan

Here we allow everything except

// TODO: adjust after Carthage
Call::Content(content::Call::<Runtime>::destroy_nft { .. }) => false,
Call::Content(content::Call::<Runtime>::toggle_nft_limits { .. }) => false,
Call::Content(content::Call::<Runtime>::update_curator_group_permissions { .. }) => false,
Call::Content(content::Call::<Runtime>::update_channel_privilege_level { .. }) => false,
Call::Content(content::Call::<Runtime>::update_channel_nft_limit { .. }) => false,
Call::Content(content::Call::<Runtime>::update_global_nft_limit { .. }) => false,
Call::Content(content::Call::<Runtime>::set_channel_paused_features_as_moderator {
    ..
}) => false,
Call::Content(content::Call::<Runtime>::initialize_channel_transfer { .. }) => false,
Call::ProposalsCodex(proposals_codex::Call::<Runtime>::create_proposal {
    general_proposal_parameters: _,
    proposal_details,
}) => !matches!(
    proposal_details,
    proposals_codex::ProposalDetails::UpdateChannelPayouts(..)
        | proposals_codex::ProposalDetails::UpdateGlobalNftLimit(..)
),

Liberated in rollout plan

Here we allow everything as in Supervised, and Sudo pallet will be removed.

GenesisConfig

  • code
    • Type: Vec<u8>
    • Description: Initial runtime WASM blob.
    • Value: TBD. by Mokhtar

substrate_utility

None

pallet_babe

No change

Config

parameter_types! {
    // NOTE: Currently it is not possible to change the epoch duration after the chain has started.
    //       Attempting to do so will brick block production.
    pub const EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS;
    pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK;
    pub const ReportLongevity: u64 =
        BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * EpochDuration::get();
}

GensisConfig

  • authorities
    • Type: Vec<(AuthorityId, BabeAuthorityWeight)>
    • Description: ..
    • Values: ...
  • epoch_config
    • Type: Option<BabeEpochConfiguration>
    • Description: ..
    • Values: ...

pallet_session_historical

None

pallet_grandpa

Config

Nothing

GenesisConfig

  • authorities
    • Description: An initial list of Grandpa authorities with associated weights.
    • Type: AuthorityList
    • Values: TBD. by Mokhtar.

pallet_timestamp

No change

Config

parameter_types! {
    pub const MinimumPeriod: Moment = SLOT_DURATION / 2;
}

GenesisConfig

None

pallet_session

No change

Config

impl_opaque_keys! {
    pub struct SessionKeys {
        pub grandpa: Grandpa,
        pub babe: Babe,
        pub im_online: ImOnline,
        pub authority_discovery: AuthorityDiscovery,
    }
}

GenesisConfig

  • keys
    • Type: Vec<(T::AccountId, T::ValidatorId, T::Keys)>
    • Description: Initial set of validators identified by controller account, validator id and session keys.
    • Values: TBD. by Mokhtar.

pallet_authorship

No change

Config

parameter_types! {
    pub const UncleGenerations: BlockNumber = 0;
}

GenesisConfig

None

pallet_multisig

Config

parameter_types! {

    // A note on parameter mutability for this pallet.
    // ===============================================
    // Note it _appears_ to be safe to have mutable values for both of these base
    // inputs, as they are only used during multisig creation to compute a final deposit,
    // and the final deposit quantity is stored with the multisig, and this value is used when unreserving
    // the multisig, hence any mutated values are irrelevant.

    // A note on use of deposit parameters in this pallet:
    //
    // It is absurdly difficult to disentangle in Polkadot/Kusama configurations becuase of the choice to
    // not use unitary prices, and because actual data
    // sizes of some pallet specific types are incorrect.
    // ===================================================
    //
    // The total deposit for deploying a new multisig with a new call is
    //
    // [T::DepositBase::get() + T::DepositFactor::get() * threshold.into()] +
		// [T::DepositBase::get() + T::DepositFactor::get() * BalanceOf::<T>::from(((data.encoded_len() + 31) / 32) as u32)]
    //
    // where `threshold` is number of signers needed and `data` is serialised multisig `Call` data.
    // The first `DepositBase` is for adding to `Multisigs` map `(AccountIdx[u8; 32] -> Multisig)`,
    // and the second is for `Calls` map `[u8; 32] -> (OpaqueCall<T>, T::AccountId, BalanceOf<T>)`.
    // In both cases, the base is only accounting for fixed portions of these mappings, so they exclude
    // `Multisig::accounts: Vec<AccountId>` and `OpaqueCall<T>`, respectively, as both are variable length.
    //
    // So for adding to `Multisigs` we are paying for
    // `sizeof((AccountIdx[u8; 32] -> fixed_part[Multisig]))`
    //  = `sizeof(AccountId)` + `sizeof([u8; 32])` + `sizeof(fixed_part[Multisig])`
    //  = `sizeof(AccountId)` + `sizeof([u8; 32])` + `sizeof(BlockNumber || Balance || AccountId)`
    //  = `sizeof(AccountId)` + `sizeof([u8; 32])` + `sizeof(BlockNumber)` + `sizeof(Balance)` + `sizeof(AccountId)`
    //  = `32` + `32` + `32` + `128` + `32`
    //  = `256`
    //
    // and for adding to `Calls` we are paying for
    // `sizeof([u8; 32] -> fixed_part[(OpaqueCall<T>, T::AccountId, BalanceOf<T>)])`
    // = `sizeof([u8; 32])` + `sizeof((T::AccountId, BalanceOf<T>))`
    // = `sizeof([u8; 32])` + `sizeof(T::AccountId)`+ `sizeof(BalanceOf<T>))`
    // = `32` + `32`+ `128`
    // = `192`
    //
    // Now not only are these two numbers not the ones found in configuration of Dotsama chains,
    // but they are also not the same! which is needed in order for them to be used they way the arey,
    // namely as a base fee for each mapping.
    //
    // The `DepositFactor` is much simpler, as it just is fixed to `32` bytes, for no obvious reason, and then
    // it's scaled  in use.
    //
    // What a mess.
    //
    // We cannot resolve this cleanly, as we don't want to fork this pallet, so best alternative is just to use the
    // biggest base deposit estimate, namely for `Multisigs` insertion.

    // The fixed portion of the size of a storage item in `Multisigs`.
    pub const MultisigMapItemFixedPortionByteSize: u32 = AccountId::length() + 32 + Multisig::length(); // <-- assuming ::length does fixed portion of encoding? tweak if not.

    // The fixed portion of the size of a storage item in `Calls`.
    pub const CallMapItemBytFixedPortioneSize: u32 = 32 + AccountId::length() + Balance::length();

    // Base fee for fixed byte portion of a map insertion across both major maps: `Multisigs`, `Calls`
    pub const DepositBase: Balance = compute_single_bloat_bond(MultisigMapItemFixedPortionByteSize.max(CallMapItemBytFixedPortioneSize), None);

    // The amount of currency needed per unit threshold when creating a multisig execution.
    // This is held for adding 32 bytes more into a pre-existing storage value.
    // Note: we leave it fixed at 32, as pallet is hard coded to that anyway... what a mess
    pub const DepositFactor: Balance = 32 * PerByteBloatBondPrice::get();

    // Max number of multisig signatories
    pub const MaxSignatories: u16 = 100;
}

GenesisConfig

None

pallet_staking

Config

const fn percentage_to_millionth(percentage: u64) -> u64 {
  1_000_000*(percentage/100)
}

pallet_staking_reward_curve::build! {
    const REWARD_CURVE: PiecewiseLinear<'static> = curve!(

        /// the minimal amount to be rewarded between validators, expressed as a fraction
        /// of total issuance. Known as `I_0` in the literature. Expressed in millionth, must be between 0
        /// and 1_000_000.
        min_inflation: percentage_to_millionth(3),

        /// the maximum amount to be rewarded between validators, expressed as a fraction
        /// of total issuance. This is attained only when `ideal_stake` is achieved. Expressed in
        /// millionth, must be between min_inflation and 1_000_000.
        max_inflation: percentage_to_millionth(3),

        /// the fraction of total issued tokens that should be actively staked behind
        /// validators. Known as `x_ideal` in the literature. Expressed in millionth, must be between
        /// 0_100_000 and 0_900_000.
        ideal_stake: percentage_to_millionth(50),

        /// Known as `decay_rate` in the literature. A co-efficient dictating the strength of
        /// the global incentivization to get the `ideal_stake`. A higher number results in less typical
        /// inflation at the cost of greater volatility for validators. Expressed in millionth, must be
        /// between 0 and 1_000_000.
        falloff: percentage_to_millionth(5),

        /// The maximum number of pieces in the curve. A greater number uses more
        /// resources but results in higher accuracy. Must be between 2 and 1_000.
        max_piece_count: 100,

        /// The maximum error allowed in the generated test. Expressed in millionth,
        /// must be between 0 and 1_000_000.
        test_precision: 0_005_000,
    );
}

parameter_types! {

    /// We are just sticking with this value from Polkadot
    pub const SessionsPerEra: sp_staking::SessionIndex = 6;
    pub const BondingDuration: sp_staking::EraIndex = BONDING_DURATION;
    pub const SlashDeferDuration: sp_staking::EraIndex = BONDING_DURATION - 1;
    pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;

    /// We are just sticking with this value from Polkadot
    pub const MaxNominatorRewardedPerValidator: u32 = 256;

    /// We are just sticking with this value from Polkadot
    pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);

    /// We are just sticking with this value from Polkadot
    pub OffchainRepeat: BlockNumber = 5;

    /// We are just sticking with this value from Polkadot
    pub MaxNominations: u32 = <NposSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
}

pub struct StakingBenchmarkingConfig;
impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig {

    /// We are just sticking with this value from Polkadot
    type MaxNominators = ConstU32<1000>;

    /// We are just sticking with this value from Polkadot
    type MaxValidators = ConstU32<1000>;
}

GenesisConfig

None

pallet_transaction_payment

Config

/// Currently in runtime/src/constants.rs

parameter_types! {
    /// The portion of the `NORMAL_DISPATCH_RATIO` that we adjust the fees with. Blocks filled less
    /// than this will decrease the weight and more will increase.
    pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25);
    /// The adjustment variable of the runtime. Higher values will cause `TargetBlockFullness` to
    /// change the fees more rapidly.
    pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000);
    /// Minimum amount of the multiplier. This value cannot be too low. A test case should ensure
    /// that combined with `AdjustmentVariable`, we can recover from the minimum.
    /// See `multiplier_can_grow_from_zero`.
    pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000_000u128);
}

pub struct WeightToFee;
impl WeightToFeePolynomial for WeightToFee {
    type Balance = Balance;
    fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {

        // TODO: This value will be tweaked after finalized weights
        // as in Polkadot, extrinsic base weight (smallest non-zero weight) is mapped to 1/10 CENT:
        let p = super::currency::CENTS;

        let q = 100 * Balance::from(ExtrinsicBaseWeight::get());
        smallvec![WeightToFeeCoefficient {
            degree: 1,
            negative: false,
            coeff_frac: Perbill::from_rational(p % q, q),
            coeff_integer: p / q,
        }]
    }
}


parameter_types! {

    // TODO: This value will be tweaked after finalized weights

    /// Balance estimated worth one thousandths of a cent.
    pub const MICROCENTS: Balance = MILLICENTS.saturating_div(1_000);
    pub const TransactionByteFee: Balance = currency::MICROCENTS;

    /// This value increases the priority of `Operational` transactions by adding
    /// a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.
    pub const OperationalFeeMultiplier: u8 = 5;
}

impl pallet_transaction_payment::Config for Runtime {
    type OnChargeTransaction = CurrencyAdapter<Balances, DealWithFees>;
    type OperationalFeeMultiplier = OperationalFeeMultiplier;
    type WeightToFee = constants::fees::WeightToFee;
    type LengthToFee = ConstantMultiplier<Balance, TransactionByteFee>;
    type FeeMultiplierUpdate = constants::fees::SlowAdjustingFeeUpdate<Self>;
}

GenesisConfig

None

pallet_election_provider_multi_phase

No change

Config

/// The numbers configured here could always be more than the the maximum limits of staking pallet
/// to ensure election snapshot will not run out of memory. For now, we set them to smaller values
/// since the staking is bounded and the weight pipeline takes hours for this single pallet.
pub struct ElectionProviderBenchmarkConfig;
impl pallet_election_provider_multi_phase::BenchmarkingConfig for ElectionProviderBenchmarkConfig {
    const VOTERS: [u32; 2] = [1000, 2000];
    const TARGETS: [u32; 2] = [500, 1000];
    const ACTIVE_VOTERS: [u32; 2] = [500, 800];
    const DESIRED_TARGETS: [u32; 2] = [200, 400];
    const SNAPSHOT_MAXIMUM_VOTERS: u32 = 1000;
    const MINER_MAXIMUM_VOTERS: u32 = 1000;
    const MAXIMUM_TARGETS: u32 = 300;
}

impl pallet_election_provider_multi_phase::MinerConfig for Runtime {
    type AccountId = AccountId;
    type MaxLength = MinerMaxLength;
    type MaxWeight = MinerMaxWeight;
    type Solution = NposSolution16;
    type MaxVotesPerVoter =
	<<Self as pallet_election_provider_multi_phase::Config>::DataProvider as ElectionDataProvider>::MaxVotesPerVoter;

    // The unsigned submissions have to respect the weight of the submit_unsigned call, thus their
    // weight estimate function is wired to this call's weight.
    fn solution_weight(v: u32, t: u32, a: u32, d: u32) -> Weight {
        <
			<Self as pallet_election_provider_multi_phase::Config>::WeightInfo
			as
			pallet_election_provider_multi_phase::WeightInfo
		>::submit_unsigned(v, t, a, d)
    }
}

impl pallet_election_provider_multi_phase::Config for Runtime {
    type Event = Event;
    type Currency = Balances;
    type EstimateCallFee = TransactionPayment;
    type SignedPhase = SignedPhase;
    type UnsignedPhase = UnsignedPhase;
    type BetterUnsignedThreshold = BetterUnsignedThreshold;
    type BetterSignedThreshold = ();
    type OffchainRepeat = OffchainRepeat;
    type MinerTxPriority = MultiPhaseUnsignedPriority;
    type MinerConfig = Self;
    type SignedMaxSubmissions = ConstU32<10>;
    type SignedRewardBase = SignedRewardBase;
    type SignedDepositBase = SignedDepositBase;
    type SignedDepositByte = SignedDepositByte;
    type SignedMaxRefunds = ConstU32<3>;
    type SignedDepositWeight = ();
    type SignedMaxWeight = MinerMaxWeight;
    type SlashHandler = (); // burn slashes
    type RewardHandler = (); // nothing to do upon rewards
    type DataProvider = Staking;
    type Fallback = onchain::BoundedExecution<OnChainSeqPhragmen>;
    type GovernanceFallback = onchain::BoundedExecution<OnChainSeqPhragmen>;
    type Solver = SequentialPhragmen<
        AccountId,
        pallet_election_provider_multi_phase::SolutionAccuracyOf<Self>,
        OffchainRandomBalancing,
    >;
    type ForceOrigin = EnsureRoot<AccountId>; // EnsureRootOrHalfCouncil;
    type MaxElectableTargets = ConstU16<{ u16::MAX }>;
    type MaxElectingVoters = MaxElectingVoters;
    type BenchmarkingConfig = ElectionProviderBenchmarkConfig;
    type WeightInfo = pallet_election_provider_multi_phase::weights::SubstrateWeight<Self>;
}

GenesisConfig

None

pallet_authority_discovery

No change

Config

impl pallet_authority_discovery::Config for Runtime {
    type MaxAuthorities = MaxAuthorities;
}

GenesisConfig

  • keys
    • Type: Vec<AuthorityId>
    • Description: ..
    • Values: TBD. Mokhtar

pallet_im_online

No change

Config

parameter_types! {
    pub const ImOnlineUnsignedPriority: TransactionPriority = TransactionPriority::max_value();
    /// We prioritize im-online heartbeats over election solution submission.
    pub const StakingUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 2;
    pub const MaxAuthorities: u32 = 100;
    pub const MaxKeys: u32 = 10_000;
    pub const MaxPeerInHeartbeats: u32 = 10_000;
    pub const MaxPeerDataEncodingSize: u32 = 1_000;
}

impl pallet_im_online::Config for Runtime {
    type AuthorityId = ImOnlineId;
    type Event = Event;
    type NextSessionRotation = Babe;
    type ValidatorSet = Historical;
    type ReportUnresponsiveness = Offences;
    type UnsignedPriority = ImOnlineUnsignedPriority;
    type WeightInfo = weights::pallet_im_online::SubstrateWeight<Runtime>;
    type MaxKeys = MaxKeys;
    type MaxPeerInHeartbeats = MaxPeerInHeartbeats;
    type MaxPeerDataEncodingSize = MaxPeerDataEncodingSize;
}

GenesisConfig

  • keys
    • Type: Vec<T::AuthorityId>
    • Descfiption: ...
    • Values: TBD. by Mokhtar

pallet_offences

No change

Config

impl pallet_offences::Config for Runtime {
    type Event = Event;
    type IdentificationTuple = pallet_session::historical::IdentificationTuple<Self>;
    type OnOffenceHandler = Staking;
}

GenesisConfig

None

pallet_randomness_collective_flip

None

pallet_bags_list

No change

Config

parameter_types! {
    pub const BagThresholds: &'static [u64] = &voter_bags::THRESHOLDS;
}

impl pallet_bags_list::Config for Runtime {
    type Event = Event;
    type ScoreProvider = Staking;
    type WeightInfo = pallet_bags_list::weights::SubstrateWeight<Runtime>;
    type BagThresholds = BagThresholds;
    type Score = VoteWeight;
}

GenesisConfig

None

pallet_sudo

No change

Config

Nothing

GenesisConfig

  • key
    • Type: Option<T::AccountId>
    • Description: The AccountId of the sudo key.
      • Value: TBD. by Martin.

project_token

Config

parameter_types! {
    pub const ProjectTokenModuleId: PalletId = PalletId(*b"mo:token"); // module: token
    pub const MaxVestingSchedulesPerAccountPerToken: u8 = 5;

    // Size of mapping in `account_info_by_token_and_member`
    // of type T::TokenId x T::MemberId => AccountDataOf<T>;
    pub const ByteSizeOfAccountInfoByTokenAndMemberMapping: Balance = TokenId::length() + MemberId::length() + AccountDataOf<T>::length();

    // Maximum total fee for cleaning up an account.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxTokenAccountCleanupFee: Balance = <TO BE DEFINED>;
}

GenesisConfig

Everything empty except

  • next_token_id
    • Type: T::TokenId
    • Description: Token Id nonce.
    • Values: 0
  • bloat_bond
    • Type: JoyBalanceOf<T>
    • Description: Bloat Bond value used during account creation.
    • Values: compute_single_bloat_bond_with_cleanup(ByteSizeOfAccountInfoByTokenAndMemberMapping, MaxTokenAccountCleanupFee);
  • min_sale_duration
    • Type: T::BlockNumber
    • Description: Minimum duration of a token sale.
    • Values: days!(1)
  • min_revenue_split_duration
    • Type: T::BlockNumber
    • Description: Minimum revenue split duration constraint.
    • Values: days!(21)
  • min_revenue_split_time_to_start
    • Type: T::BlockNumber
    • Description: Minimum revenue split time to start constraint.
    • Values: 0
  • sale_platform_fee
    • Type: Permill
    • Description: Platform fee (percentage) charged on top of each sale purchase (in JOY) and burned.
    • Values: 2%

council

Config

I think it should be at least council size + extra candidates. I left this comment once when making changes to council size and set the MaxWinnerTargetCount equal to council.

I think the council has that config “number of extra candidates” so if referendum doesn’t produce council + extra, the council election fails.

// Production coucil and elections configuration
#[cfg(not(any(feature = "staging_runtime", feature = "testing_runtime")))]
parameter_types! {

    pub const CouncilSize: u64 = 3;
    pub const MinNumberOfExtraCandidates: u64 = 0;
    pub const MinCandidateStake: Balance = dollars!(10_000);
    pub const AnnouncingPeriodDuration: BlockNumber = days!(14);
    pub const IdlePeriodDuration: BlockNumber = days!(1);
    pub const ElectedMemberRewardPeriod: BlockNumber = days!(1);
    pub const BudgetRefillPeriod: BlockNumber = days!(1);
}

GenesisConfig

Everything is empty or "obvious" default except

  • budget
    • Type: Balance<T>
    • Description: Budget for the council's elected members rewards
    • Values: 0
  • next_reward_payments
    • Type: T::BlockNumber
    • Description: The next block in which the elected council member rewards will be payed.
    • Values: 0
  • next_budget_refill
    • Type: T::BlockNumber
    • Description: The next block in which the budget will be increased.
    • Values: 0
  • budget_increment
    • Type: Balance<T>
    • Description: Amount of balance to be refilled every budget period
    • Values: dollars!(25_000)
  • councilor_reward
    • Type: Balance<T>
    • Description: Councilor reward per block
    • Values: monthly_dollars_to_per_block!(10_000)

referendum

Config

// Production coucil and elections configuration
#[cfg(not(any(feature = "staging_runtime", feature = "testing_runtime")))]
parameter_types! {
    // referendum parameters
    pub const MaxSaltLength: u64 = 32;
    pub const VoteStageDuration: BlockNumber = days!(3);
    pub const RevealStageDuration: BlockNumber = days!(3);
    pub const MinimumVotingStake: Balance = dollars!(10);
    pub const MaxWinnerTargetCount: u64 = CouncilSize::get();
}

GenesisConfig

Nothing

members

Config

parameter_types! {
    pub const DefaultMembershipPrice: Balance = cents!(5);
    pub const ReferralCutMaximumPercent: u8 = 50;
    pub const DefaultInitialInvitationBalance: Balance = dollars!(5);

    // Maximum total fee for disconnecting staking account.
    // NG: I don't even remember if we have this, just adding for completeness, remove later!
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxStakingAccountCleanupFee = <TO BE DEFINED>;
    // We just say that minimum voting stake is lower bound for any stake binding, as good as anything else.
    pub const CandidateStake: Balance = stake_with_cleanup(MinimumVotingStake::get(), MaxStakingAccountCleanupFee);
}

GenesisConfig

Nothing

forum

Assumes this has been fixed: Joystream/joystream#4275

Config

parameter_types! {
    pub const MaxCategoryDepth: u64 = 6;
    pub const MaxDirectSubcategoriesInCategory: u64 = 5;
    pub const MaxModeratorsForCategory: u64 = 10;
    pub const MaxTotalCategories: u64 = 40;

    /// Post cleanup

    // Maximum total fee for cleaning up a post.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxPostCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `post_by_id`.
    pub const PostByIdMapItemByteSize: u32 = sizeof(ThreadId) + sizeof(PostId) + PostOf<T>::length();

    // Bloat bond for map insertion into `post_by_id` of sizeof((T::ThreadId,T::PostId, PostOf<T>))
    pub const PostDeposit: Balance = compute_single_bloat_bond_with_cleanup(PostByIdMapItemByteSize, MaxPostCleanupFee);

    /// Thread cleanup

    // Maximum total fee for cleaning up a thread.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxThreadCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `thread_by_id`
    pub const ThreadByIdMapItemByteSize: u32 = sizeof(CategoryId) + sizeof(ThreadId) + ThreadOf<T>::length();

    // Bloat bond for map insertion into `thread_by_id`
    // We must ensure that bond clears existential deposit bar, because there is a dedicated
    // module account _per thread_.
    pub const ThreadDeposit: Balance = compute_single_bloat_bond_with_cleanup(ThreadByIdMapItemByteSize, MaxThreadCleanupFee).max(ExistentialDeposit::get());

    pub const ForumModuleId: PalletId = PalletId(*b"mo:forum"); // module : forum
    pub const PostLifeTime: BlockNumber = days!(30);
}

GenesisConfig

Everything empty or with obvious default value.

bounty

Config

parameter_types! {
    pub const BountyModuleId: PalletId = PalletId(*b"m:bounty"); // module : bounty
    pub const ClosedContractSizeLimit: u32 = 50;

    /// Work entrant stake

    // Maximum total fee for cleaning up a work entry.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxWorkEntrantCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `entries`
    pub const EntryMapItemByteSize: u32 = sizeof(BountyId) + sizeof(EntryId) + Option<Entry<T>>::length();

    // Bloat bond for map insertion into `entries`
    pub const MinWorkEntrantStake: Balance = compute_single_bloat_bond_with_cleanup(EntryMapItemByteSize, MaxWorkEntrantCleanupFee);

    /// Funder bloat bond

    // Maximum total fee for cleaning up a funding contribution.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxFundingCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `contribution_by_bounty_by_actor`
    pub const ContributionByBountyEntryMapItemByteSize: u32 = sizeof(BountyId) + BountyActor<MemberId<T>>::length() + Contribution<T>::length();

    // Bloat bond for funding bounty
    pub const FunderStateBloatBondAmount: Balance = compute_single_bloat_bond_with_cleanup(ContributionByBountyEntryMapItemByteSize, MaxFundingCleanupFee);

    /// Bounty creator bloat bond

    // Maximum total fee for cleaning up a bounty creation.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxBountyCreatorCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `bounties`
    pub const BountyMapItemByteSize: u32 = sizeof(BountyId) + Bounty<T>::length();

    // Bloat bond for creating bounty
    pub const CreatorStateBloatBondAmount: Balance = compute_single_bloat_bond_with_cleanup(BountyMapItemByteSize, MaxBountyCreatorCleanupFee);
}

GenesisConfig

Nothing

joystream_utility

None

content

Config

parameter_types! {
    pub const MaxNumberOfCuratorsPerGroup: MaxNumber = 10;
    pub const ContentModuleId: PalletId = PalletId(*b"mContent"); // module content
    pub const MaxKeysPerCuratorGroupPermissionsByLevelMap: u8 = 25;
    pub const DefaultGlobalDailyNftLimit: LimitPerPeriod<BlockNumber> = LimitPerPeriod {
        block_number_period: DAYS,
        limit: 100,
    };  // TODO: update
    pub const DefaultGlobalWeeklyNftLimit: LimitPerPeriod<BlockNumber> = LimitPerPeriod {
        block_number_period: WEEKS,
        limit: 400,
    };  // TODO: update
    pub const DefaultChannelDailyNftLimit: LimitPerPeriod<BlockNumber> = LimitPerPeriod {
        block_number_period: DAYS,
        limit: 10,
    };  // TODO: update
    pub const DefaultChannelWeeklyNftLimit: LimitPerPeriod<BlockNumber> = LimitPerPeriod {
        block_number_period: WEEKS,
        limit: 40,
    };  // TODO: update

    // These limits _on limits_ seem a bit silly in retrospect, but anyway, here we are.
    pub const MinimumCashoutAllowedLimit: Balance = dollars!(10);
    pub const MaximumCashoutAllowedLimit: Balance = dollars!(100_000);

    /// Channel cleanup - presumes we do this: https://github.com/Joystream/joystream/issues/4277

    // Maximum total fee for cleaning up a channel.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxChannelCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `channel_by_id`
    pub const ChannelByIdMapItemByteSize: u32 = sizeof(ChannelId) + sizeof(ThreadId) + Channel<T>::length();

    // Bloat bond for map insertion into `channel_by_id`
    // We must ensure that bond clears existential deposit bar, because there is a dedicated
    // module account _per channel_.
    pub const ChannelStateBloatBondValue: Balance = compute_single_bloat_bond_with_cleanup(ChannelByIdMapItemByteSize, MaxChannelCleanupFee).max(ExistentialDeposit::get());

    /// Video cleanup - presumes https://github.com/Joystream/joystream/issues/4277

    // Maximum total fee for cleaning up a video.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxVideoCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `video_by_id`
    pub const VideoByIdMapItemByteSize: u32 = sizeof(VideoId) + Video<T>::length();

    // Bloat bond for map insertion into `video_by_id`
    pub const VideoStateBloatBondValue: Balance = compute_single_bloat_bond_with_cleanup(VideoByIdMapItemByteSize, MaxVideoCleanupFee);
}

GenesisConfig

pub MaxCashoutAllowed get(fn max_cashout_allowed) config(): BalanceOf<T> = dollars!(100_000);

pub MinCashoutAllowed get(fn min_cashout_allowed) config(): BalanceOf<T> = dollars!(10);

pub ChannelCashoutsEnabled get(fn channel_cashouts_enabled) config(): bool = true;

/// Min auction duration
pub MinAuctionDuration get(fn min_auction_duration) config(): T::BlockNumber = hours!(1);

/// Max auction duration
pub MaxAuctionDuration get(fn max_auction_duration) config(): T::BlockNumber = days!(30);

/// Min auction extension period
pub MinAuctionExtensionPeriod
get(fn min_auction_extension_period) config(): T::BlockNumber = 0;

/// Max auction extension period
pub MaxAuctionExtensionPeriod
get(fn max_auction_extension_period) config(): T::BlockNumber = days!(30);

/// Min bid lock duration
pub MinBidLockDuration get(fn min_bid_lock_duration) config(): T::BlockNumber = 0;

/// Max bid lock duration
pub MaxBidLockDuration get(fn max_bid_lock_duration) config(): T::BlockNumber = hours!(1);

/// Min auction staring price
pub MinStartingPrice get(fn min_starting_price) config(): BalanceOf<T> = dollar!(1);

/// Max auction staring price
pub MaxStartingPrice get(fn max_starting_price) config(): BalanceOf<T> = dollar!(500_000);

/// Min creator royalty percentage
pub MinCreatorRoyalty get(fn min_creator_royalty) config() = 0;

/// Max creator royalty percentage
pub MaxCreatorRoyalty get(fn max_creator_royalty) config(): 10%;

/// Min auction bid step
pub MinBidStep get(fn min_bid_step) config(): BalanceOf<T> = dollar!(1);

/// Max auction bid step
pub MaxBidStep get(fn max_bid_step) config(): BalanceOf<T> = dollar!(10_000);

/// Platform fee percentage
pub PlatfromFeePercentage get(fn platform_fee_percentage) config() = 2%

/// Max delta between current block and starts at
pub AuctionStartsAtMaxDelta get(fn auction_starts_at_max_delta) config(): T::BlockNumber = days!(100)

/// Max nft auction whitelist length
pub MaxAuctionWhiteListLength get(fn max_auction_whitelist_length) config(): MaxNumber = 20;

/// NFT limits enabled or not
/// Can be updated in flight by the Council
pub NftLimitsEnabled get(fn nft_limits_enabled) config(): bool = true;

storage

Config

// Storage parameters independent of runtime profile
parameter_types! {
    pub const MaxDistributionBucketFamilyNumber: u64 = 200;
    pub const BlacklistSizeLimit: u64 = 1000;
    pub const MaxNumberOfPendingInvitationsPerDistributionBucket: u64 = 20;
    pub const StorageModuleId: PalletId = PalletId(*b"mstorage"); // module storage
    pub const DistributionBucketsPerBagValueConstraint: storage::DistributionBucketsPerBagValueConstraint =
        storage::DistributionBucketsPerBagValueConstraint {min: 1, max_min_diff: 50};
    pub const MaxDataObjectSize: u64 = 30 * 1024 * 1024 * 1024; // 30 GB
}

// Production storage parameters
#[cfg(not(any(feature = "staging_runtime", feature = "testing_runtime")))]
parameter_types! {
    pub const StorageBucketsPerBagValueConstraint: storage::StorageBucketsPerBagValueConstraint =
        storage::StorageBucketsPerBagValueConstraint {min: 3, max_min_diff: 10};
    pub const DefaultMemberDynamicBagNumberOfStorageBuckets: u64 = 5;
    pub const DefaultChannelDynamicBagNumberOfStorageBuckets: u64 = 5;
}

GenesisConfig

/// Size based pricing of new objects uploaded.
pub DataObjectPerMegabyteFee get (fn data_object_per_mega_byte_fee) config(): BalanceOf<T> = (100*BYTES_IN_GB).saturating_div(cents!(1));

/// The state bloat bond for the data objects (helps preventing the state bloat).

// Maximum total fee for cleaning up a data object, is generic,
// as cleanup may have extra cost from client pallet using it, currently only
// content pallet, but should be reasonably close.
// Read more about this under: `Profitable Deposit Cleanup`
pub const MaxDataObjectCleanupFee: Balance = <TO BE DEFINED>;

// The size of a storage item in `data_object_by_id`
/// 'Data objects for bags' storage double map.
pub const DataObjectsByIdMapItemByteSize: u32 = sizeof(BagId<T>) + sizeof(T::DataObjectId) + DataObjectOf<T>::length();

// Bloat bond for map insertion into `data_object_by_id`
pub DataObjectStateBloatBondValue get (fn data_object_state_bloat_bond_value) config(): BalanceOf<T>; = compute_single_bloat_bond_with_cleanup(DataObjectsByIdMapItemByteSize, MaxDataObjectCleanupFee);

proposals_engine

Config

parameter_types! {
    // Its difficult to select one uniform value here, and since this fee actually
    // is collected regardless of how much was actually staked, it should be kept low for now.
    pub const ProposalCancellationFee: Balance = dollars!(1);
    // Its difficult to select one uniform value here, and since this fee actually
    // is collected regardless of how much was actually staked, it should be kept low for now.
    pub const ProposalRejectionFee: Balance = dollars!(5);
    pub const ProposalTitleMaxLength: u32 = 40;
    pub const ProposalDescriptionMaxLength: u32 = 3000;
    pub const ProposalMaxActiveProposalLimit: u32 = 20;
}

GenesisConfig

Nothing

proposal_discussion

Config

parameter_types! {
    pub const MaxWhiteListSize: u32 = 20;

    /// ProposalsPostDeposit

    // Maximum total fee for cleaning up a proposal post.
    // Read more about this under: `Profitable Deposit Cleanup`
    pub const MaxProposalPostCleanupFee: Balance = <TO BE DEFINED>;

    // The size of a storage item in `PostThreadIdByPostId`
    pub const PostThreadIdByPostIdMapItemByteSize: u32 = sizeof(T::ThreadId) + sizeof(T::PostId) + DiscussionPostOf<T>::length();

    // Bloat bond for map insertion into `PostThreadIdByPostId`
    pub const ProposalsPostDeposit: Balance = compute_single_bloat_bond_with_cleanup(PostThreadIdByPostIdMapItemByteSize, MaxProposalPostCleanupFee);

    // module : proposals_discussion
    pub const ProposalsDiscussionModuleId: PalletId = PalletId(*b"mo:prdis");
    pub const ForumPostLifeTime: BlockNumber = hours!(1);
}

GenesisConfig

Nothing

proposals_codex

Config

parameter_types! {
    // Make sure to stay below block limit of all operational extrinsics.
    pub const RuntimeUpgradeWasmProposalMaxLength: u32 = mega_bytes!(3.5);

    // We are keeping this purposefully small for now becuase we only have one funding request
    // proposal, and if it was to allow big spends, then we would need different parameter settings.
    // This can be introduced later
    pub const FundingRequestProposalMaxAmount: Balance = dollars!(10_000)
    pub const FundingRequestProposalMaxAccounts: u32 = 20;
    pub const SetMaxValidatorCountProposalMaxValidators: u32 = 100;
}

const_assert!(RuntimeUpgradeWasmProposalMaxLength <= RuntimeBlockLength.max.normal)

ProposalParameters

/// Some useful labels
const TWO_OUT_OF_THREE: u32 = 66; // at least 2 in council of 3, which is 2/3 = 66.66..% > 66%
const ONE: u32 = 33; // at least 1 in council of 3, which is 1/3 = 33.33..% > 33%
const ALL: u32 = 100;
const HALF: u32 = 50;
const_assert!(CouncilSize == 3); // This assert is here to remind anyone who tries to touch updated `CouncilSize` that parameters `TWO_OUT_OF_THREE` and `ONE` need to be updated as well. Only the paranoid survive.

//! This module contains default "production" parameters configuration for the runtime codex proposals.

// Proposal parameters for the 'Set Max Validator Count' proposal
pub(crate) fn set_max_validator_count_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(7),
        grace_period: days!(5),
        approval_quorum_percentage: ALL,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(10_000)),
        constitutionality: 3,
    }
}

// Proposal parameters for the 'Runtime Upgrade' proposal
pub(crate) fn runtime_upgrade_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(7),
        grace_period: days!(5),
        approval_quorum_percentage: ALL,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(10_000)),
        constitutionality: 4,
    }
}

// Proposal parameters for the 'Signal' proposal
pub(crate) fn signal_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: ALL,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(1000))
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Funding Request' proposal
pub(crate) fn funding_request_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(10)), // notice this is low, so newcomers can ask
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Create Working Group Lead Opening' proposal
pub(crate) fn create_working_group_lead_opening_proposal(
) -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(100)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Fill Working Group Lead Opening' proposal
pub(crate) fn fill_working_group_lead_opening_proposal() -> ProposalParameters<BlockNumber, Balance>
{

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Update Working Group Budget' proposal
pub(crate) fn update_working_group_budget_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Decrease Working Group Lead Stake' proposal
pub(crate) fn decrease_working_group_lead_stake_proposal(
) -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: ALL,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Slash Working Group Lead' proposal
pub fn slash_working_group_lead_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Set Working Group Lead Reward' proposal
pub(crate) fn set_working_group_lead_reward_proposal() -> ProposalParameters<BlockNumber, Balance> {

  ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Terminate Working Group Lead' proposal
pub(crate) fn terminate_working_group_lead_proposal() -> ProposalParameters<BlockNumber, Balance> {

  ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Cancel Working Group Lead Opening' proposal
pub(crate) fn cancel_working_group_lead_opening_proposal(
) -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Set Membership Price' proposal
pub(crate) fn set_membership_price_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Set Council Budget Increment' proposal
pub(crate) fn set_council_budget_increment_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(5),
        grace_period: days!(5),
        approval_quorum_percentage: ALL,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(2000)),
        constitutionality: 3,
    }
}

// Proposal parameters for the 'Set Councilor Reward' proposal
pub(crate) fn set_councilor_reward_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(2),
        grace_period: days!(3),
        approval_quorum_percentage: ALL,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(2000)),
        constitutionality: 2,
    }
}

// Proposal parameters for the 'Set Initial Invitation Balance' proposal
pub(crate) fn set_initial_invitation_balance_proposal() -> ProposalParameters<BlockNumber, Balance>
{

    ProposalParameters {
        voting_period: days!(2),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(200)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Set Initial Invitation Quota' proposal
// The parameter for this proposal still have to be more carefully reviewed
pub(crate) fn set_membership_lead_invitation_quota_proposal(
) -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Set Referral Cut' proposal
pub(crate) fn set_referral_cut_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Set Initial Invitation Count' proposal
pub(crate) fn set_invitation_count_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(3),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(50)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Veto Proposal' proposal
pub(crate) fn veto_proposal_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(1),
        grace_period: 0,
        approval_quorum_percentage: ALL,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(1000)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Update global NFT limit' proposal
pub(crate) fn update_global_nft_limit_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(2),
        grace_period: hours!(2),
        approval_quorum_percentage: TWO_OUT_OF_THREE
        approval_threshold_percentage: HALF,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(100)),
        constitutionality: 1,
    }
}

// Proposal parameters for the 'Update Channel Payouts' proposal
pub(crate) fn update_channel_payouts_proposal() -> ProposalParameters<BlockNumber, Balance> {

    ProposalParameters {
        voting_period: days!(7),
        grace_period: days!(1),
        approval_quorum_percentage: TWO_OUT_OF_THREE,
        approval_threshold_percentage: ALL,
        slashing_quorum_percentage: ALL,
        slashing_threshold_percentage: ALL,
        required_stake: Some(dollars!(100)),
        constitutionality: 1,
    }
}

GenesisConfig

Nothing

working_group

Configuration

parameter_types! {
    pub const MaxWorkerNumberLimit: u32 = 30;
    pub const MinUnstakingPeriodLimit: u32 = days!(20);

    // Setup all reward payouts to be happening in a cascade of 10 blocks apart.
    pub const ForumWorkingGroupRewardPeriod: u32 = days!(1) + 10;
    pub const StorageWorkingGroupRewardPeriod: u32 = ForumWorkingGroupRewardPeriod + 10;
    pub const ContentWorkingGroupRewardPeriod: u32 = StorageWorkingGroupRewardPeriod + 10;
    pub const MembershipRewardPeriod: u32 = ContentWorkingGroupRewardPeriod + 10;
    pub const GatewayRewardPeriod: u32 = MembershipRewardPeriod + 10;
    pub const OperationsAlphaRewardPeriod: u32 = GatewayRewardPeriod + 10;
    pub const OperationsBetaRewardPeriod: u32 = OperationsAlphaRewardPeriod + 10;
    pub const OperationsGammaRewardPeriod: u32 = OperationsBetaRewardPeriod + 10;
    pub const DistributionRewardPeriod: u32 = OperationsGammaRewardPeriod + 10;

    // This should be more costly than `apply_on_opening` fee
    pub const MinimumApplicationStake: Balance = dollars!(20);

    // This should be more costly than `add_opening` fee
    pub const LeaderOpeningStake: Balance = dollars!(100);
}

// Make sure that one cannot leave before a slashing proposal for lead can go through.
// Will apply to other non-lead workers as well, but that is fine.
const_assert(MinUnstakingPeriodLimit >= slash_working_group_lead_proposal().voting_period + slash_working_group_lead_proposal().grace_period)

GenesisConfig

Nothing

Questions

  1. Is validator reward too low? too high? Rate of returns seems low, but but based on cost of production for the service it seems way too high at stated market cap.
@adovrodion
Copy link

adovrodion commented Sep 22, 2022

First it says $905K per year, but below that it says $755K/30 days. Is this a mistake?
image

@adovrodion
Copy link

adovrodion commented Sep 22, 2022

Will there be a reduced number of participants in the new testnet? (as for Mainnet)

@bedeho
Copy link
Author

bedeho commented Sep 23, 2022

First it says $905K per year, but below that it says $755K/30 days. Is this a mistake?

So there is a mistake here, which is that $905K was per month, not per year, so inflation per year is actually much higher, it's $905K*12/60 = 0.181 = 18.1%

However, the $755K number is different, it's the council monthly budget, and since validator minting does not come out of the council budget this number is lower than $905K.

Will there be a reduced number of participants in the new testnet? (as for Mainnet)

This is not because of the parameters I have chosen, its because of the likely costs involved. The only value which is hard coded to have fewer participants than now is council, which is going from 5 to 3.

@0x2bc
Copy link

0x2bc commented Nov 21, 2022

@bedeho why is for signal_proposal required_stake: Some(dollars!(1000))?
I believe the parameter need to be adjusted. It looks very high. Community members will need to stake 16k Joy or 1000 USD (a lot!) for the proposal which is not even an actionable one. For example, funding proposal requires 166tJoy which is 100x lower.

@traumschule
Copy link

traumschule commented Nov 21, 2022

I had no chance to review the whole document yet but it needs to be noted that assumed worker count for storage and distribution is limited to 30 (set by MaxWorkerNumberLimit).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment