Skip to content

Instantly share code, notes, and snippets.

@bernardoaraujor
Created May 4, 2023 21:00
Show Gist options
  • Save bernardoaraujor/6960ad289fd59cba1d01084972255ec9 to your computer and use it in GitHub Desktop.
Save bernardoaraujor/6960ad289fd59cba1d01084972255ec9 to your computer and use it in GitHub Desktop.
// Copyright 2019-2022 PureStake Inc.
// This file is part of Moonbeam.
// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
//! The Moonbase Runtime.
//!
//! Primary features of this runtime include:
//! * Ethereum compatibility
//! * Moonbase tokenomics
#![cfg_attr(not(feature = "std"), no_std)]
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
#![recursion_limit = "256"]
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use cumulus_pallet_parachain_system::{RelayChainStateProof, RelaychainBlockNumberProvider};
use cumulus_primitives_core::relay_chain;
use fp_rpc::TransactionStatus;
use account::AccountId20;
// Re-export required by get! macro.
pub use frame_support::traits::Get;
use frame_support::{
construct_runtime,
dispatch::{DispatchClass, GetDispatchInfo},
ensure,
pallet_prelude::DispatchResult,
parameter_types,
traits::{
ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, Contains,
Currency as CurrencyT, EitherOfDiverse, EqualPrivilegeOnly, FindAuthor, Imbalance,
InstanceFilter, OffchainWorker, OnFinalize, OnIdle, OnInitialize, OnRuntimeUpgrade,
OnUnbalanced,
},
weights::{
constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND},
ConstantMultiplier, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients,
WeightToFeePolynomial,
},
PalletId,
};
#[cfg(feature = "std")]
pub use fp_evm::GenesisAccount;
use frame_system::{EnsureRoot, EnsureSigned};
pub use moonbeam_core_primitives::{
AccountId, AccountIndex, Address, AssetId, Balance, BlockNumber, DigestItem, Hash, Header,
Index, Signature,
};
use moonbeam_rpc_primitives_txpool::TxPoolResponse;
pub use pallet_author_slot_filter::EligibilityValue;
use pallet_balances::NegativeImbalance;
use pallet_ethereum::Call::transact;
use pallet_ethereum::Transaction as EthereumTransaction;
use pallet_evm::{
Account as EVMAccount, EVMCurrencyAdapter, EnsureAddressNever, EnsureAddressRoot,
FeeCalculator, GasWeightMapping, OnChargeEVMTransaction as OnChargeEVMTransactionT, Runner,
};
pub use pallet_parachain_staking::{InflationInfo, Range};
use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment};
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_api::impl_runtime_apis;
use sp_core::{OpaqueMetadata, H160, H256, U256};
use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys,
traits::{
BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, IdentityLookup,
PostDispatchInfoOf, UniqueSaturatedInto, Zero,
},
transaction_validity::{
InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
},
ApplyExtrinsicResult, FixedPointNumber, Perbill, Permill, Perquintill,
};
use sp_std::{
convert::{From, Into},
prelude::*,
};
#[cfg(feature = "std")]
use sp_version::NativeVersion;
use sp_version::RuntimeVersion;
use nimbus_primitives::CanAuthor;
mod precompiles;
pub use precompiles::{
MoonbasePrecompiles, PrecompileName, FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
LOCAL_ASSET_PRECOMPILE_ADDRESS_PREFIX,
};
use smallvec::smallvec;
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
pub type Precompiles = MoonbasePrecompiles<Runtime>;
pub mod asset_config;
pub mod governance;
pub mod xcm_config;
use governance::councils::*;
/// UNIT, the native token, uses 18 decimals of precision.
pub mod currency {
use super::Balance;
// Provide a common factor between runtimes based on a supply of 10_000_000 tokens.
pub const SUPPLY_FACTOR: Balance = 1;
pub const WEI: Balance = 1;
pub const KILOWEI: Balance = 1_000;
pub const MEGAWEI: Balance = 1_000_000;
pub const GIGAWEI: Balance = 1_000_000_000;
pub const MICROUNIT: Balance = 1_000_000_000_000;
pub const MILLIUNIT: Balance = 1_000_000_000_000_000;
pub const UNIT: Balance = 1_000_000_000_000_000_000;
pub const KILOUNIT: Balance = 1_000_000_000_000_000_000_000;
pub const TRANSACTION_BYTE_FEE: Balance = 1 * GIGAWEI * SUPPLY_FACTOR;
pub const STORAGE_BYTE_FEE: Balance = 100 * MICROUNIT * SUPPLY_FACTOR;
pub const WEIGHT_FEE: Balance = 50 * KILOWEI * SUPPLY_FACTOR;
pub const fn deposit(items: u32, bytes: u32) -> Balance {
items as Balance * 1 * UNIT * SUPPLY_FACTOR + (bytes as Balance) * STORAGE_BYTE_FEE
}
}
/// Maximum weight per block
pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_ref_time(WEIGHT_REF_TIME_PER_SECOND)
.saturating_div(2)
.set_proof_size(cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64);
pub const MILLISECS_PER_BLOCK: u64 = 12000;
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
pub const HOURS: BlockNumber = MINUTES * 60;
pub const DAYS: BlockNumber = HOURS * 24;
pub const WEEKS: BlockNumber = DAYS * 7;
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
/// to even the core data structures.
pub mod opaque {
use super::*;
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
impl_opaque_keys! {
pub struct SessionKeys {
pub nimbus: AuthorInherent,
pub vrf: session_keys_primitives::VrfSessionKey,
}
}
}
/// This runtime version.
/// The spec_version is composed of 2x2 digits. The first 2 digits represent major changes
/// that can't be skipped, such as data migration upgrades. The last 2 digits represent minor
/// changes which can be skipped.
#[sp_version::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("moonbase"),
impl_name: create_runtime_str!("moonbase"),
authoring_version: 4,
spec_version: 2400,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 2,
state_version: 0,
};
/// The version information used to identify this runtime when compiled natively.
#[cfg(feature = "std")]
pub fn native_version() -> NativeVersion {
NativeVersion {
runtime_version: VERSION,
can_author_with: Default::default(),
}
}
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
pub const NORMAL_WEIGHT: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_mul(3).saturating_div(4);
// Here we assume Ethereum's base fee of 21000 gas and convert to weight, but we
// subtract roughly the cost of a balance transfer from it (about 1/3 the cost)
// and some cost to account for per-byte-fee.
// TODO: we should use benchmarking's overhead feature to measure this
pub const EXTRINSIC_BASE_WEIGHT: Weight = Weight::from_ref_time(10000 * WEIGHT_PER_GAS);
pub struct RuntimeBlockWeights;
impl Get<frame_system::limits::BlockWeights> for RuntimeBlockWeights {
fn get() -> frame_system::limits::BlockWeights {
frame_system::limits::BlockWeights::builder()
.for_class(DispatchClass::Normal, |weights| {
weights.base_extrinsic = EXTRINSIC_BASE_WEIGHT;
weights.max_total = NORMAL_WEIGHT.into();
})
.for_class(DispatchClass::Operational, |weights| {
weights.max_total = MAXIMUM_BLOCK_WEIGHT.into();
weights.reserved = (MAXIMUM_BLOCK_WEIGHT - NORMAL_WEIGHT).into();
})
.avg_block_initialization(Perbill::from_percent(10))
.build()
.expect("Provided BlockWeight definitions are valid, qed")
}
}
parameter_types! {
pub const Version: RuntimeVersion = VERSION;
/// TODO: this is left here so that `impl_runtime_apis_plus_common` will find the same type for
/// `BlockWeights` in all runtimes. It can probably be removed once the custom
/// `RuntimeBlockWeights` has been pushed to each runtime.
pub BlockWeights: frame_system::limits::BlockWeights = RuntimeBlockWeights::get();
/// We allow for 5 MB blocks.
pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
}
impl frame_system::Config for Runtime {
/// The identifier used to distinguish between accounts.
type AccountId = AccountId;
/// The aggregated dispatch type that is available for extrinsics.
type RuntimeCall = RuntimeCall;
/// The lookup mechanism to get account ID from whatever is passed in dispatchers.
type Lookup = IdentityLookup<AccountId>;
/// The index type for storing how many extrinsics an account has signed.
type Index = Index;
/// The index type for blocks.
type BlockNumber = BlockNumber;
/// The type for hashing blocks and tries.
type Hash = Hash;
/// The hashing algorithm used.
type Hashing = BlakeTwo256;
/// The header type.
type Header = generic::Header<BlockNumber, BlakeTwo256>;
/// The ubiquitous event type.
type RuntimeEvent = RuntimeEvent;
/// The ubiquitous origin type.
type RuntimeOrigin = RuntimeOrigin;
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
type BlockHashCount = ConstU32<256>;
/// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok.
type BlockWeights = RuntimeBlockWeights;
/// Maximum size of all encoded transactions (in bytes) that are allowed in one block.
type BlockLength = BlockLength;
/// Runtime version.
type Version = Version;
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type DbWeight = RocksDbWeight;
type BaseCallFilter = MaintenanceMode;
type SystemWeightInfo = ();
/// This is used as an identifier of the chain. 42 is the generic substrate prefix.
type SS58Prefix = ConstU16<1287>;
type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
impl pallet_utility::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type PalletsOrigin = OriginCaller;
type WeightInfo = pallet_utility::weights::SubstrateWeight<Runtime>;
}
impl pallet_timestamp::Config for Runtime {
/// A timestamp: milliseconds since the unix epoch.
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = ConstU64<6000>;
type WeightInfo = pallet_timestamp::weights::SubstrateWeight<Runtime>;
}
impl pallet_balances::Config for Runtime {
type MaxReserves = ConstU32<50>;
type ReserveIdentifier = [u8; 4];
type MaxLocks = ConstU32<50>;
/// The type for recording an account's balance.
type Balance = Balance;
/// The ubiquitous event type.
type RuntimeEvent = RuntimeEvent;
type DustRemoval = ();
type ExistentialDeposit = ConstU128<0>;
type AccountStore = System;
type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
}
pub struct DealWithFees<R>(sp_std::marker::PhantomData<R>);
impl<R> OnUnbalanced<NegativeImbalance<R>> for DealWithFees<R>
where
R: pallet_balances::Config + pallet_treasury::Config,
pallet_treasury::Pallet<R>: OnUnbalanced<NegativeImbalance<R>>,
{
// this seems to be called for substrate-based transactions
fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance<R>>) {
if let Some(fees) = fees_then_tips.next() {
// for fees, 80% are burned, 20% to the treasury
let (_, to_treasury) = fees.ration(80, 20);
// Balances pallet automatically burns dropped Negative Imbalances by decreasing
// total_supply accordingly
<pallet_treasury::Pallet<R> as OnUnbalanced<_>>::on_unbalanced(to_treasury);
}
}
// this is called from pallet_evm for Ethereum-based transactions
// (technically, it calls on_unbalanced, which calls this when non-zero)
fn on_nonzero_unbalanced(amount: NegativeImbalance<R>) {
// Balances pallet automatically burns dropped Negative Imbalances by decreasing
// total_supply accordingly
let (_, to_treasury) = amount.ration(80, 20);
<pallet_treasury::Pallet<R> as OnUnbalanced<_>>::on_unbalanced(to_treasury);
}
}
pub struct LengthToFee;
impl WeightToFeePolynomial for LengthToFee {
type Balance = Balance;
fn polynomial() -> WeightToFeeCoefficients<Self::Balance> {
smallvec![
WeightToFeeCoefficient {
degree: 1,
coeff_frac: Perbill::zero(),
coeff_integer: currency::TRANSACTION_BYTE_FEE,
negative: false,
},
WeightToFeeCoefficient {
degree: 3,
coeff_frac: Perbill::zero(),
coeff_integer: 1 * currency::SUPPLY_FACTOR,
negative: false,
},
]
}
}
impl pallet_transaction_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnChargeTransaction = CurrencyAdapter<Balances, DealWithFees<Runtime>>;
type OperationalFeeMultiplier = ConstU8<5>;
type WeightToFee = ConstantMultiplier<Balance, ConstU128<{ currency::WEIGHT_FEE }>>;
type LengthToFee = LengthToFee;
type FeeMultiplierUpdate = FastAdjustingFeeUpdate<Runtime>;
}
impl pallet_sudo::Config for Runtime {
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
}
impl pallet_ethereum_chain_id::Config for Runtime {}
/// Current approximation of the gas/s consumption considering
/// EVM execution over compiled WASM (on 4.4Ghz CPU).
/// Given the 500ms Weight, from which 75% only are used for transactions,
/// the total EVM execution gas limit is: GAS_PER_SECOND * 0.500 * 0.75 ~= 15_000_000.
pub const GAS_PER_SECOND: u64 = 40_000_000;
/// Approximate ratio of the amount of Weight per Gas.
/// u64 works for approximations because Weight is a very small unit compared to gas.
pub const WEIGHT_PER_GAS: u64 = WEIGHT_REF_TIME_PER_SECOND / GAS_PER_SECOND;
parameter_types! {
pub BlockGasLimit: U256
= U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS);
/// 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. This fast multiplier responds by doubling/halving in
/// approximately one hour at extreme block congestion levels.
pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(4, 1_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` in integration_tests.rs.
pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 10);
/// Maximum multiplier. We pick a value that is expensive but not impossibly so; it should act
/// as a safety net.
pub MaximumMultiplier: Multiplier = Multiplier::from(100_000u128);
pub PrecompilesValue: MoonbasePrecompiles<Runtime> = MoonbasePrecompiles::<_>::new();
pub WeightPerGas: Weight = Weight::from_ref_time(WEIGHT_PER_GAS);
}
pub struct TransactionPaymentAsGasPrice;
impl FeeCalculator for TransactionPaymentAsGasPrice {
fn min_gas_price() -> (U256, Weight) {
// TODO: transaction-payment differs from EIP-1559 in that its tip and length fees are not
// scaled by the multiplier, which means its multiplier will be overstated when
// applied to an ethereum transaction
// note: transaction-payment uses both a congestion modifier (next_fee_multiplier, which is
// updated once per block in on_finalize) and a 'WeightToFee' implementation. Our
// runtime implements this as a 'ConstantModifier', so we can get away with a simple
// multiplication here.
// It is imperative that `saturating_mul_int` be performed as late as possible in the
// expression since it involves fixed point multiplication with a division by a fixed
// divisor. This leads to truncation and subsequent precision loss if performed too early.
// This can lead to min_gas_price being same across blocks even if the multiplier changes.
// There's still some precision loss when the final `gas_price` (used_gas * min_gas_price)
// is computed in frontier, but that's currently unavoidable.
let min_gas_price = TransactionPayment::next_fee_multiplier()
.saturating_mul_int(currency::WEIGHT_FEE.saturating_mul(WEIGHT_PER_GAS as u128));
(
min_gas_price.into(),
<Runtime as frame_system::Config>::DbWeight::get().reads(1),
)
}
}
/// A "Fast" TargetedFeeAdjustment. Parameters chosen based on model described here:
/// https://research.web3.foundation/en/latest/polkadot/overview/2-token-economics.html#-1.-fast-adjusting-mechanism // editorconfig-checker-disable-line
///
/// The adjustment algorithm boils down to:
///
/// diff = (previous_block_weight - target) / maximum_block_weight
/// next_multiplier = prev_multiplier * (1 + (v * diff) + ((v * diff)^2 / 2))
/// assert(next_multiplier > min)
/// where: v is AdjustmentVariable
/// target is TargetBlockFullness
/// min is MinimumMultiplier
pub type FastAdjustingFeeUpdate<R> = TargetedFeeAdjustment<
R,
TargetBlockFullness,
AdjustmentVariable,
MinimumMultiplier,
MaximumMultiplier,
>;
/// The author inherent provides an AccountId, but pallet evm needs an H160.
/// This simple adapter makes the conversion for any types T, U such that T: Into<U>
pub struct FindAuthorAdapter<T, U, Inner>(sp_std::marker::PhantomData<(T, U, Inner)>);
impl<T, U, Inner> FindAuthor<U> for FindAuthorAdapter<T, U, Inner>
where
T: Into<U>,
Inner: FindAuthor<T>,
{
fn find_author<'a, I>(digests: I) -> Option<U>
where
I: 'a + IntoIterator<Item = (sp_runtime::ConsensusEngineId, &'a [u8])>,
{
Inner::find_author(digests).map(Into::into)
}
}
moonbeam_runtime_common::impl_on_charge_evm_transaction!();
impl pallet_evm::Config for Runtime {
type FeeCalculator = TransactionPaymentAsGasPrice;
type GasWeightMapping = pallet_evm::FixedGasWeightMapping<Self>;
type WeightPerGas = WeightPerGas;
type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;
type CallOrigin = EnsureAddressRoot<AccountId>;
type WithdrawOrigin = EnsureAddressNever<AccountId>;
type AddressMapping = moonbeam_runtime_common::IntoAddressMapping;
type Currency = Balances;
type RuntimeEvent = RuntimeEvent;
type Runner = pallet_evm::runner::stack::Runner<Self>;
type PrecompilesType = MoonbasePrecompiles<Self>;
type PrecompilesValue = PrecompilesValue;
type ChainId = EthereumChainId;
type OnChargeTransaction = OnChargeEVMTransaction<DealWithFees<Runtime>>;
type BlockGasLimit = BlockGasLimit;
type FindAuthor = FindAuthorAdapter<AccountId20, H160, AuthorInherent>;
type OnCreate = ();
}
parameter_types! {
pub MaximumSchedulerWeight: Weight = NORMAL_DISPATCH_RATIO * RuntimeBlockWeights::get().max_block;
pub const NoPreimagePostponement: Option<u32> = Some(10);
}
impl pallet_scheduler::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type RuntimeOrigin = RuntimeOrigin;
type PalletsOrigin = OriginCaller;
type RuntimeCall = RuntimeCall;
type MaximumWeight = MaximumSchedulerWeight;
type ScheduleOrigin = EnsureRoot<AccountId>;
type MaxScheduledPerBlock = ConstU32<50>;
type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>;
type OriginPrivilegeCmp = EqualPrivilegeOnly;
type Preimages = Preimage;
}
impl pallet_preimage::Config for Runtime {
type WeightInfo = pallet_preimage::weights::SubstrateWeight<Runtime>;
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type ManagerOrigin = EnsureRoot<AccountId>;
type BaseDeposit = ConstU128<{ 5 * currency::UNIT * currency::SUPPLY_FACTOR }>;
type ByteDeposit = ConstU128<{ currency::STORAGE_BYTE_FEE }>;
}
parameter_types! {
pub const ProposalBond: Permill = Permill::from_percent(5);
pub const TreasuryId: PalletId = PalletId(*b"pc/trsry");
}
type TreasuryApproveOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
pallet_collective::EnsureProportionAtLeast<AccountId, TreasuryCouncilInstance, 3, 5>,
>;
type TreasuryRejectOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
pallet_collective::EnsureProportionMoreThan<AccountId, TreasuryCouncilInstance, 1, 2>,
>;
impl pallet_treasury::Config for Runtime {
type PalletId = TreasuryId;
type Currency = Balances;
// At least three-fifths majority of the council is required (or root) to approve a proposal
type ApproveOrigin = TreasuryApproveOrigin;
// More than half of the council is required (or root) to reject a proposal
type RejectOrigin = TreasuryRejectOrigin;
type RuntimeEvent = RuntimeEvent;
// If spending proposal rejected, transfer proposer bond to treasury
type OnSlash = Treasury;
type ProposalBond = ProposalBond;
type ProposalBondMinimum = ConstU128<{ 1 * currency::UNIT * currency::SUPPLY_FACTOR }>;
type SpendPeriod = ConstU32<{ 6 * DAYS }>;
type Burn = ();
type BurnDestination = ();
type MaxApprovals = ConstU32<100>;
type WeightInfo = pallet_treasury::weights::SubstrateWeight<Runtime>;
type SpendFunds = ();
type ProposalBondMaximum = ();
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<Balance>; // Same as Polkadot
}
type IdentityForceOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
EitherOfDiverse<
pallet_collective::EnsureProportionMoreThan<AccountId, CouncilInstance, 1, 2>,
governance::custom_origins::GeneralAdmin,
>,
>;
type IdentityRegistrarOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
EitherOfDiverse<
pallet_collective::EnsureProportionMoreThan<AccountId, CouncilInstance, 1, 2>,
governance::custom_origins::GeneralAdmin,
>,
>;
impl pallet_identity::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
// Add one item in storage and take 258 bytes
type BasicDeposit = ConstU128<{ currency::deposit(1, 258) }>;
// Not add any item to the storage but takes 66 bytes
type FieldDeposit = ConstU128<{ currency::deposit(0, 66) }>;
// Add one item in storage and take 53 bytes
type SubAccountDeposit = ConstU128<{ currency::deposit(1, 53) }>;
type MaxSubAccounts = ConstU32<100>;
type MaxAdditionalFields = ConstU32<100>;
type MaxRegistrars = ConstU32<20>;
type Slashed = Treasury;
type ForceOrigin = IdentityForceOrigin;
type RegistrarOrigin = IdentityRegistrarOrigin;
type WeightInfo = pallet_identity::weights::SubstrateWeight<Runtime>;
}
pub struct TransactionConverter;
impl fp_rpc::ConvertTransaction<UncheckedExtrinsic> for TransactionConverter {
fn convert_transaction(&self, transaction: pallet_ethereum::Transaction) -> UncheckedExtrinsic {
UncheckedExtrinsic::new_unsigned(
pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),
)
}
}
impl fp_rpc::ConvertTransaction<opaque::UncheckedExtrinsic> for TransactionConverter {
fn convert_transaction(
&self,
transaction: pallet_ethereum::Transaction,
) -> opaque::UncheckedExtrinsic {
let extrinsic = UncheckedExtrinsic::new_unsigned(
pallet_ethereum::Call::<Runtime>::transact { transaction }.into(),
);
let encoded = extrinsic.encode();
opaque::UncheckedExtrinsic::decode(&mut &encoded[..])
.expect("Encoded extrinsic is always valid")
}
}
impl pallet_ethereum::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type StateRoot = pallet_ethereum::IntermediateStateRoot<Self>;
}
pub struct EthereumXcmEnsureProxy;
impl xcm_primitives::EnsureProxy<AccountId> for EthereumXcmEnsureProxy {
fn ensure_ok(delegator: AccountId, delegatee: AccountId) -> Result<(), &'static str> {
// The EVM implicitely contains an Any proxy, so we only allow for "Any" proxies
let def: pallet_proxy::ProxyDefinition<AccountId, ProxyType, BlockNumber> =
pallet_proxy::Pallet::<Runtime>::find_proxy(
&delegator,
&delegatee,
Some(ProxyType::Any),
)
.map_err(|_| "proxy error: expected `ProxyType::Any`")?;
// We only allow to use it for delay zero proxies, as the call will immediatly be executed
ensure!(def.delay.is_zero(), "proxy delay is Non-zero`");
Ok(())
}
}
impl pallet_ethereum_xcm::Config for Runtime {
type InvalidEvmTransactionError = pallet_ethereum::InvalidTransactionWrapper;
type ValidatedTransaction = pallet_ethereum::ValidatedTransaction<Self>;
type XcmEthereumOrigin = pallet_ethereum_xcm::EnsureXcmEthereumTransaction;
type ReservedXcmpWeight = ReservedXcmpWeight;
type EnsureProxy = EthereumXcmEnsureProxy;
type ControllerOrigin = EnsureRoot<AccountId>;
}
parameter_types! {
pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4);
pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4);
}
impl cumulus_pallet_parachain_system::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type OnSystemEvent = ();
type SelfParaId = ParachainInfo;
type DmpMessageHandler = MaintenanceMode;
type ReservedDmpWeight = ReservedDmpWeight;
type OutboundXcmpMessageSource = XcmpQueue;
type XcmpMessageHandler = XcmpQueue;
type ReservedXcmpWeight = ReservedXcmpWeight;
type CheckAssociatedRelayNumber = cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases;
}
impl parachain_info::Config for Runtime {}
pub struct OnNewRound;
impl pallet_parachain_staking::OnNewRound for OnNewRound {
fn on_new_round(round_index: pallet_parachain_staking::RoundIndex) -> Weight {
MoonbeamOrbiters::on_new_round(round_index)
}
}
pub struct PayoutCollatorOrOrbiterReward;
impl pallet_parachain_staking::PayoutCollatorReward<Runtime> for PayoutCollatorOrOrbiterReward {
fn payout_collator_reward(
for_round: pallet_parachain_staking::RoundIndex,
collator_id: AccountId,
amount: Balance,
) -> Weight {
let extra_weight = if MoonbeamOrbiters::is_orbiter(for_round, collator_id) {
MoonbeamOrbiters::distribute_rewards(for_round, collator_id, amount)
} else {
ParachainStaking::mint_collator_reward(for_round, collator_id, amount)
};
<Runtime as frame_system::Config>::DbWeight::get()
.reads(1)
.saturating_add(extra_weight)
}
}
type MonetaryGovernanceOrigin =
EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
impl pallet_parachain_staking::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type MonetaryGovernanceOrigin = MonetaryGovernanceOrigin;
/// Minimum round length is 2 minutes (10 * 12 second block times)
type MinBlocksPerRound = ConstU32<10>;
/// Rounds before the collator leaving the candidates request can be executed
type LeaveCandidatesDelay = ConstU32<2>;
/// Rounds before the candidate bond increase/decrease can be executed
type CandidateBondLessDelay = ConstU32<2>;
/// Rounds before the delegator exit can be executed
type LeaveDelegatorsDelay = ConstU32<2>;
/// Rounds before the delegator revocation can be executed
type RevokeDelegationDelay = ConstU32<2>;
/// Rounds before the delegator bond increase/decrease can be executed
type DelegationBondLessDelay = ConstU32<2>;
/// Rounds before the reward is paid
type RewardPaymentDelay = ConstU32<2>;
/// Minimum collators selected per round, default at genesis and minimum forever after
type MinSelectedCandidates = ConstU32<8>;
/// Maximum top delegations per candidate
type MaxTopDelegationsPerCandidate = ConstU32<300>;
/// Maximum bottom delegations per candidate
type MaxBottomDelegationsPerCandidate = ConstU32<50>;
/// Maximum delegations per delegator
type MaxDelegationsPerDelegator = ConstU32<100>;
/// Minimum stake required to be reserved to be a candidate
type MinCandidateStk = ConstU128<{ 500 * currency::UNIT * currency::SUPPLY_FACTOR }>;
/// Minimum stake required to be reserved to be a delegator
type MinDelegation = ConstU128<{ 1 * currency::UNIT * currency::SUPPLY_FACTOR }>;
/// Minimum stake required to be reserved to be a delegator
type MinDelegatorStk = ConstU128<{ 1 * currency::UNIT * currency::SUPPLY_FACTOR }>;
type BlockAuthor = AuthorInherent;
type OnCollatorPayout = ();
type PayoutCollatorReward = PayoutCollatorOrOrbiterReward;
type OnNewRound = OnNewRound;
type WeightInfo = pallet_parachain_staking::weights::SubstrateWeight<Runtime>;
}
impl pallet_author_inherent::Config for Runtime {
type SlotBeacon = RelaychainBlockNumberProvider<Self>;
type AccountLookup = MoonbeamOrbiters;
type CanAuthor = AuthorFilter;
type WeightInfo = pallet_author_inherent::weights::SubstrateWeight<Runtime>;
}
impl pallet_author_slot_filter::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type RandomnessSource = Randomness;
type PotentialAuthors = ParachainStaking;
type WeightInfo = pallet_author_slot_filter::weights::SubstrateWeight<Runtime>;
}
parameter_types! {
pub const InitializationPayment: Perbill = Perbill::from_percent(30);
pub const RelaySignaturesThreshold: Perbill = Perbill::from_percent(100);
pub const SignatureNetworkIdentifier: &'static [u8] = b"moonbase-";
}
impl pallet_crowdloan_rewards::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Initialized = ConstBool<false>;
type InitializationPayment = InitializationPayment;
type MaxInitContributors = ConstU32<500>;
// TODO to be revisited
type MinimumReward = ConstU128<0>;
type RewardCurrency = Balances;
type RelayChainAccountId = [u8; 32];
type RewardAddressAssociateOrigin = EnsureSigned<Self::AccountId>;
type RewardAddressChangeOrigin = EnsureSigned<Self::AccountId>;
type RewardAddressRelayVoteThreshold = RelaySignaturesThreshold;
type SignatureNetworkIdentifier = SignatureNetworkIdentifier;
type VestingBlockNumber = cumulus_primitives_core::relay_chain::BlockNumber;
type VestingBlockProvider =
cumulus_pallet_parachain_system::RelaychainBlockNumberProvider<Self>;
type WeightInfo = pallet_crowdloan_rewards::weights::SubstrateWeight<Runtime>;
}
// This is a simple session key manager. It should probably either work with, or be replaced
// entirely by pallet sessions
impl pallet_author_mapping::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type DepositCurrency = Balances;
type DepositAmount = ConstU128<{ 100 * currency::UNIT * currency::SUPPLY_FACTOR }>;
type Keys = session_keys_primitives::VrfId;
type WeightInfo = pallet_author_mapping::weights::SubstrateWeight<Runtime>;
}
/// The type used to represent the kinds of proxying allowed.
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, MaxEncodedLen, TypeInfo,
)]
pub enum ProxyType {
/// All calls can be proxied. This is the trivial/most permissive filter.
Any = 0,
/// Only extrinsics that do not transfer funds.
NonTransfer = 1,
/// Only extrinsics related to governance (democracy and collectives).
Governance = 2,
/// Only extrinsics related to staking.
Staking = 3,
/// Allow to veto an announced proxy call.
CancelProxy = 4,
/// Allow extrinsic related to Balances.
Balances = 5,
/// Allow extrinsic related to AuthorMapping.
AuthorMapping = 6,
/// Allow extrinsic related to IdentityJudgement.
IdentityJudgement = 7,
}
impl Default for ProxyType {
fn default() -> Self {
Self::Any
}
}
fn is_governance_precompile(precompile_name: &precompiles::PrecompileName) -> bool {
matches!(
precompile_name,
PrecompileName::DemocracyPrecompile
| PrecompileName::CouncilInstance
| PrecompileName::TechCommitteeInstance
| PrecompileName::TreasuryCouncilInstance
| PrecompileName::ReferendaPrecompile
| PrecompileName::ConvictionVotingPrecompile
| PrecompileName::PreimagePrecompile
| PrecompileName::OpenTechCommitteeInstance,
)
}
// Be careful: Each time this filter is modified, the substrate filter must also be modified
// consistently.
impl pallet_evm_precompile_proxy::EvmProxyCallFilter for ProxyType {
fn is_evm_proxy_call_allowed(
&self,
call: &pallet_evm_precompile_proxy::EvmSubCall,
recipient_has_code: bool,
) -> bool {
use pallet_evm::PrecompileSet as _;
match self {
ProxyType::Any => true,
ProxyType::NonTransfer => {
call.value == U256::zero()
&& match PrecompileName::from_address(call.to.0) {
Some(
PrecompileName::AuthorMappingPrecompile
| PrecompileName::ParachainStakingPrecompile,
) => true,
Some(ref precompile) if is_governance_precompile(precompile) => true,
_ => false,
}
}
ProxyType::Governance => {
call.value == U256::zero()
&& matches!(
PrecompileName::from_address(call.to.0),
Some(ref precompile) if is_governance_precompile(precompile)
)
}
ProxyType::Staking => {
call.value == U256::zero()
&& matches!(
PrecompileName::from_address(call.to.0),
Some(
PrecompileName::AuthorMappingPrecompile
| PrecompileName::ParachainStakingPrecompile
)
)
}
// The proxy precompile does not contain method cancel_proxy
ProxyType::CancelProxy => false,
ProxyType::Balances => {
// Allow only "simple" accounts as recipient (no code nor precompile).
// Note: Checking the presence of the code is not enough because some precompiles
// have no code.
!recipient_has_code && !PrecompilesValue::get().is_precompile(call.to.0)
}
ProxyType::AuthorMapping => {
call.value == U256::zero()
&& matches!(
PrecompileName::from_address(call.to.0),
Some(PrecompileName::AuthorMappingPrecompile)
)
}
// There is no identity precompile
ProxyType::IdentityJudgement => false,
}
}
}
// Be careful: Each time this filter is modified, the EVM filter must also be modified consistently.
impl InstanceFilter<RuntimeCall> for ProxyType {
fn filter(&self, c: &RuntimeCall) -> bool {
match self {
ProxyType::Any => true,
ProxyType::NonTransfer => {
matches!(
c,
RuntimeCall::System(..)
| RuntimeCall::ParachainSystem(..)
| RuntimeCall::Timestamp(..)
| RuntimeCall::ParachainStaking(..)
| RuntimeCall::Democracy(..)
| RuntimeCall::Referenda(..)
| RuntimeCall::Preimage(..)
| RuntimeCall::ConvictionVoting(..)
| RuntimeCall::CouncilCollective(..)
| RuntimeCall::TreasuryCouncilCollective(..)
| RuntimeCall::TechCommitteeCollective(..)
| RuntimeCall::OpenTechCommitteeCollective(..)
| RuntimeCall::Identity(..)
| RuntimeCall::Utility(..)
| RuntimeCall::Proxy(..) | RuntimeCall::AuthorMapping(..)
| RuntimeCall::CrowdloanRewards(
pallet_crowdloan_rewards::Call::claim { .. }
)
)
}
ProxyType::Governance => matches!(
c,
RuntimeCall::Democracy(..)
| RuntimeCall::Referenda(..)
| RuntimeCall::Preimage(..)
| RuntimeCall::ConvictionVoting(..)
| RuntimeCall::CouncilCollective(..)
| RuntimeCall::TreasuryCouncilCollective(..)
| RuntimeCall::TechCommitteeCollective(..)
| RuntimeCall::OpenTechCommitteeCollective(..)
| RuntimeCall::Utility(..)
),
ProxyType::Staking => matches!(
c,
RuntimeCall::ParachainStaking(..)
| RuntimeCall::Utility(..)
| RuntimeCall::AuthorMapping(..)
| RuntimeCall::MoonbeamOrbiters(..)
),
ProxyType::CancelProxy => matches!(
c,
RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. })
),
ProxyType::Balances => {
matches!(c, RuntimeCall::Balances(..) | RuntimeCall::Utility(..))
}
ProxyType::AuthorMapping => matches!(c, RuntimeCall::AuthorMapping(..)),
ProxyType::IdentityJudgement => matches!(
c,
RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. })
| RuntimeCall::Utility(..)
),
}
}
fn is_superset(&self, o: &Self) -> bool {
match (self, o) {
(x, y) if x == y => true,
(ProxyType::Any, _) => true,
(_, ProxyType::Any) => false,
_ => false,
}
}
}
impl pallet_proxy::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type Currency = Balances;
type ProxyType = ProxyType;
// One storage item; key size 32, value size 8
type ProxyDepositBase = ConstU128<{ currency::deposit(1, 8) }>;
// Additional storage item size of 21 bytes (20 bytes AccountId + 1 byte sizeof(ProxyType)).
type ProxyDepositFactor = ConstU128<{ currency::deposit(0, 21) }>;
type MaxProxies = ConstU32<32>;
type WeightInfo = pallet_proxy::weights::SubstrateWeight<Runtime>;
type MaxPending = ConstU32<32>;
type CallHasher = BlakeTwo256;
type AnnouncementDepositBase = ConstU128<{ currency::deposit(1, 8) }>;
// Additional storage item size of 56 bytes:
// - 20 bytes AccountId
// - 32 bytes Hasher (Blake2256)
// - 4 bytes BlockNumber (u32)
type AnnouncementDepositFactor = ConstU128<{ currency::deposit(0, 56) }>;
}
impl pallet_migrations::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
// TODO wire up our correct list of migrations here. Maybe this shouldn't be in
// `moonbeam_runtime_common`.
type MigrationsList = (
moonbeam_runtime_common::migrations::CommonMigrations<
Runtime,
CouncilCollective,
TechCommitteeCollective,
>,
moonbeam_runtime_common::migrations::ReferendaMigrations<
Runtime,
CouncilCollective,
TechCommitteeCollective,
>,
);
type XcmExecutionManager = XcmExecutionManager;
type WeightInfo = pallet_migrations::weights::SubstrateWeight<Runtime>;
}
/// Maintenance mode Call filter
pub struct MaintenanceFilter;
impl Contains<RuntimeCall> for MaintenanceFilter {
fn contains(c: &RuntimeCall) -> bool {
match c {
RuntimeCall::Assets(_) => false,
RuntimeCall::LocalAssets(_) => false,
RuntimeCall::Balances(_) => false,
RuntimeCall::CrowdloanRewards(_) => false,
RuntimeCall::Ethereum(_) => false,
RuntimeCall::EVM(_) => false,
RuntimeCall::Identity(_) => false,
RuntimeCall::XTokens(_) => false,
RuntimeCall::ParachainStaking(_) => false,
RuntimeCall::MoonbeamOrbiters(_) => false,
RuntimeCall::PolkadotXcm(_) => false,
RuntimeCall::Treasury(_) => false,
RuntimeCall::XcmTransactor(_) => false,
RuntimeCall::EthereumXcm(_) => false,
_ => true,
}
}
}
/// Normal Call Filter
/// We dont allow to create nor mint assets, this for now is disabled
/// We only allow transfers. For now creation of assets will go through
/// asset-manager, while minting/burning only happens through xcm messages
/// This can change in the future
pub struct NormalFilter;
impl Contains<RuntimeCall> for NormalFilter {
fn contains(c: &RuntimeCall) -> bool {
match c {
RuntimeCall::Assets(method) => match method {
pallet_assets::Call::transfer { .. } => true,
pallet_assets::Call::transfer_keep_alive { .. } => true,
pallet_assets::Call::approve_transfer { .. } => true,
pallet_assets::Call::transfer_approved { .. } => true,
pallet_assets::Call::cancel_approval { .. } => true,
pallet_assets::Call::destroy_accounts { .. } => true,
pallet_assets::Call::destroy_approvals { .. } => true,
pallet_assets::Call::finish_destroy { .. } => true,
_ => false,
},
// We want to disable create, as we dont want users to be choosing the
// assetId of their choice
// We also disable destroy, as we want to route destroy through the
// asset-manager, which guarantees the removal both at the EVM and
// substrate side of things
RuntimeCall::LocalAssets(method) => match method {
pallet_assets::Call::create { .. } => false,
pallet_assets::Call::start_destroy { .. } => false,
_ => true,
},
// We filter anonymous proxy as they make "reserve" inconsistent
// See: https://github.com/paritytech/substrate/blob/37cca710eed3dadd4ed5364c7686608f5175cce1/frame/proxy/src/lib.rs#L270 // editorconfig-checker-disable-line
RuntimeCall::Proxy(method) => match method {
pallet_proxy::Call::create_pure { .. } => false,
pallet_proxy::Call::kill_pure { .. } => false,
_ => true,
},
// Filtering the EVM prevents possible re-entrancy from the precompiles which could
// lead to unexpected scenarios.
// See https://github.com/PureStake/sr-moonbeam/issues/30
// Note: It is also assumed that EVM calls are only allowed through `Origin::Root` so
// this can be seen as an additional security
RuntimeCall::EVM(_) => false,
_ => true,
}
}
}
use cumulus_primitives_core::{relay_chain::BlockNumber as RelayBlockNumber, DmpMessageHandler};
pub struct XcmExecutionManager;
impl xcm_primitives::PauseXcmExecution for XcmExecutionManager {
fn suspend_xcm_execution() -> DispatchResult {
XcmpQueue::suspend_xcm_execution(RuntimeOrigin::root())
}
fn resume_xcm_execution() -> DispatchResult {
XcmpQueue::resume_xcm_execution(RuntimeOrigin::root())
}
}
pub struct NormalDmpHandler;
impl DmpMessageHandler for NormalDmpHandler {
// This implementation makes messages be queued
// Since the limit is 0, messages are queued for next iteration
fn handle_dmp_messages(
iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
limit: Weight,
) -> Weight {
(if Migrations::should_pause_xcm() {
DmpQueue::handle_dmp_messages(iter, Weight::zero())
} else {
DmpQueue::handle_dmp_messages(iter, limit)
}) + <Runtime as frame_system::Config>::DbWeight::get().reads(1)
}
}
pub struct MaintenanceDmpHandler;
impl DmpMessageHandler for MaintenanceDmpHandler {
// This implementation makes messages be queued
// Since the limit is 0, messages are queued for next iteration
fn handle_dmp_messages(
iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
_limit: Weight,
) -> Weight {
DmpQueue::handle_dmp_messages(iter, Weight::zero())
}
}
/// The hooks we wnat to run in Maintenance Mode
pub struct MaintenanceHooks;
impl OnInitialize<BlockNumber> for MaintenanceHooks {
fn on_initialize(n: BlockNumber) -> Weight {
AllPalletsWithSystem::on_initialize(n)
}
}
// return 0
// For some reason using empty tuple () isnt working
// There exist only two pallets that use onIdle and these are xcmp and dmp queues
// For some reason putting an empty tumple does not work (transaction never finishes)
// We use an empty onIdle, if on the future we want one of the pallets to execute it
// we need to provide it here
impl OnIdle<BlockNumber> for MaintenanceHooks {
fn on_idle(_n: BlockNumber, _max_weight: Weight) -> Weight {
Weight::zero()
}
}
impl OnRuntimeUpgrade for MaintenanceHooks {
fn on_runtime_upgrade() -> Weight {
AllPalletsWithSystem::on_runtime_upgrade()
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, &'static str> {
AllPalletsWithSystem::pre_upgrade()
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> {
AllPalletsWithSystem::post_upgrade(state)
}
}
impl OnFinalize<BlockNumber> for MaintenanceHooks {
fn on_finalize(n: BlockNumber) {
AllPalletsWithSystem::on_finalize(n)
}
}
impl OffchainWorker<BlockNumber> for MaintenanceHooks {
fn offchain_worker(n: BlockNumber) {
AllPalletsWithSystem::offchain_worker(n)
}
}
impl pallet_maintenance_mode::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type NormalCallFilter = NormalFilter;
type MaintenanceCallFilter = MaintenanceFilter;
type MaintenanceOrigin =
pallet_collective::EnsureProportionAtLeast<AccountId, TechCommitteeInstance, 2, 3>;
type XcmExecutionManager = XcmExecutionManager;
type NormalDmpHandler = NormalDmpHandler;
type MaintenanceDmpHandler = MaintenanceDmpHandler;
// We use AllPalletsWithSystem because we dont want to change the hooks in normal
// operation
type NormalExecutiveHooks = AllPalletsWithSystem;
type MaintenanceExecutiveHooks = MaintenanceHooks;
}
impl pallet_proxy_genesis_companion::Config for Runtime {
type ProxyType = ProxyType;
}
parameter_types! {
pub OrbiterReserveIdentifier: [u8; 4] = [b'o', b'r', b'b', b'i'];
}
type AddCollatorOrigin =
EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
type DelCollatorOrigin =
EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
impl pallet_moonbeam_orbiters::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type AccountLookup = AuthorMapping;
type AddCollatorOrigin = AddCollatorOrigin;
type Currency = Balances;
type DelCollatorOrigin = DelCollatorOrigin;
/// Maximum number of orbiters per collator
type MaxPoolSize = ConstU32<8>;
/// Maximum number of round to keep on storage
type MaxRoundArchive = ConstU32<4>;
type OrbiterReserveIdentifier = OrbiterReserveIdentifier;
type RotatePeriod = ConstU32<3>;
/// Round index type.
type RoundIndex = pallet_parachain_staking::RoundIndex;
type WeightInfo = pallet_moonbeam_orbiters::weights::SubstrateWeight<Runtime>;
}
/// Only callable after `set_validation_data` is called which forms this proof the same way
fn relay_chain_state_proof() -> RelayChainStateProof {
let relay_storage_root = ParachainSystem::validation_data()
.expect("set in `set_validation_data`")
.relay_parent_storage_root;
let relay_chain_state =
ParachainSystem::relay_state_proof().expect("set in `set_validation_data`");
RelayChainStateProof::new(ParachainInfo::get(), relay_storage_root, relay_chain_state)
.expect("Invalid relay chain state proof, already constructed in `set_validation_data`")
}
pub struct BabeDataGetter;
impl pallet_randomness::GetBabeData<u64, Option<Hash>> for BabeDataGetter {
// Tolerate panic here because only ever called in inherent (so can be omitted)
fn get_epoch_index() -> u64 {
if cfg!(feature = "runtime-benchmarks") {
// storage reads as per actual reads
let _relay_storage_root = ParachainSystem::validation_data();
let _relay_chain_state = ParachainSystem::relay_state_proof();
const BENCHMARKING_NEW_EPOCH: u64 = 10u64;
return BENCHMARKING_NEW_EPOCH;
}
relay_chain_state_proof()
.read_optional_entry(relay_chain::well_known_keys::EPOCH_INDEX)
.ok()
.flatten()
.expect("expected to be able to read epoch index from relay chain state proof")
}
fn get_epoch_randomness() -> Option<Hash> {
if cfg!(feature = "runtime-benchmarks") {
// storage reads as per actual reads
let _relay_storage_root = ParachainSystem::validation_data();
let _relay_chain_state = ParachainSystem::relay_state_proof();
let benchmarking_babe_output = Hash::default();
return Some(benchmarking_babe_output);
}
relay_chain_state_proof()
.read_optional_entry(relay_chain::well_known_keys::ONE_EPOCH_AGO_RANDOMNESS)
.ok()
.flatten()
}
}
impl pallet_randomness::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type AddressMapping = moonbeam_runtime_common::IntoAddressMapping;
type Currency = Balances;
type BabeDataGetter = BabeDataGetter;
type VrfKeyLookup = AuthorMapping;
type Deposit = ConstU128<{ 1 * currency::UNIT * currency::SUPPLY_FACTOR }>;
type MaxRandomWords = ConstU8<100>;
type MinBlockDelay = ConstU32<2>;
type MaxBlockDelay = ConstU32<2_000>;
type BlockExpirationDelay = ConstU32<10_000>;
type EpochExpirationDelay = ConstU64<10_000>;
}
impl pallet_root_testing::Config for Runtime {}
parameter_types! {
// One storage item; key size is 32; value is size 4+4+16+20 bytes = 44 bytes.
pub const DepositBase: Balance = currency::deposit(1, 76);
// Additional storage item size of 32 bytes.
pub const DepositFactor: Balance = currency::deposit(0, 20);
pub const MaxSignatories: u32 = 100;
}
impl pallet_multisig::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type RuntimeCall = RuntimeCall;
type Currency = Balances;
type DepositBase = DepositBase;
type DepositFactor = DepositFactor;
type MaxSignatories = MaxSignatories;
type WeightInfo = pallet_multisig::weights::SubstrateWeight<Runtime>;
}
construct_runtime! {
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Pallet, Call, Storage, Config, Event<T>} = 0,
Utility: pallet_utility::{Pallet, Call, Event} = 1,
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 2,
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>} = 3,
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>} = 4,
// Previously 5: pallet_randomness_collective_flip
ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Storage, Inherent, Event<T>} = 6,
TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Config, Event<T>} = 7,
ParachainInfo: parachain_info::{Pallet, Storage, Config} = 8,
EthereumChainId: pallet_ethereum_chain_id::{Pallet, Storage, Config} = 9,
EVM: pallet_evm::{Pallet, Config, Call, Storage, Event<T>} = 10,
Ethereum: pallet_ethereum::{Pallet, Call, Storage, Event, Origin, Config} = 11,
ParachainStaking: pallet_parachain_staking::{Pallet, Call, Storage, Event<T>, Config<T>} = 12,
Scheduler: pallet_scheduler::{Pallet, Storage, Event<T>, Call} = 13,
Democracy: pallet_democracy::{Pallet, Storage, Config<T>, Event<T>, Call} = 14,
CouncilCollective:
pallet_collective::<Instance1>::{Pallet, Call, Storage, Event<T>, Origin<T>, Config<T>} = 15,
TechCommitteeCollective:
pallet_collective::<Instance2>::{Pallet, Call, Storage, Event<T>, Origin<T>, Config<T>} = 16,
Treasury: pallet_treasury::{Pallet, Storage, Config, Event<T>, Call} = 17,
AuthorInherent: pallet_author_inherent::{Pallet, Call, Storage, Inherent} = 18,
AuthorFilter: pallet_author_slot_filter::{Pallet, Call, Storage, Event, Config} = 19,
CrowdloanRewards: pallet_crowdloan_rewards::{Pallet, Call, Config<T>, Storage, Event<T>} = 20,
AuthorMapping: pallet_author_mapping::{Pallet, Call, Config<T>, Storage, Event<T>} = 21,
Proxy: pallet_proxy::{Pallet, Call, Storage, Event<T>} = 22,
MaintenanceMode: pallet_maintenance_mode::{Pallet, Call, Config, Storage, Event} = 23,
Identity: pallet_identity::{Pallet, Call, Storage, Event<T>} = 24,
XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event<T>} = 25,
CumulusXcm: cumulus_pallet_xcm::{Pallet, Event<T>, Origin} = 26,
DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event<T>} = 27,
PolkadotXcm: pallet_xcm::{Pallet, Call, Storage, Event<T>, Origin, Config} = 28,
Assets: pallet_assets::{Pallet, Call, Storage, Event<T>} = 29,
XTokens: orml_xtokens::{Pallet, Call, Storage, Event<T>} = 30,
AssetManager: pallet_asset_manager::{Pallet, Call, Storage, Event<T>} = 31,
Migrations: pallet_migrations::{Pallet, Call, Storage, Config, Event<T>} = 32,
XcmTransactor: pallet_xcm_transactor::{Pallet, Call, Storage, Event<T>} = 33,
ProxyGenesisCompanion: pallet_proxy_genesis_companion::{Pallet, Config<T>} = 34,
LocalAssets: pallet_assets::<Instance1>::{Pallet, Call, Storage, Event<T>} = 36,
MoonbeamOrbiters: pallet_moonbeam_orbiters::{Pallet, Call, Storage, Event<T>} = 37,
EthereumXcm: pallet_ethereum_xcm::{Pallet, Call, Storage, Origin} = 38,
Randomness: pallet_randomness::{Pallet, Call, Storage, Event<T>, Inherent} = 39,
TreasuryCouncilCollective:
pallet_collective::<Instance3>::{Pallet, Call, Storage, Event<T>, Origin<T>, Config<T>} = 40,
ConvictionVoting: pallet_conviction_voting::{Pallet, Call, Storage, Event<T>} = 41,
Referenda: pallet_referenda::{Pallet, Call, Storage, Event<T>} = 42,
Origins: governance::custom_origins::{Origin} = 43,
Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>} = 44,
Whitelist: pallet_whitelist::{Pallet, Call, Storage, Event<T>} = 45,
OpenTechCommitteeCollective:
pallet_collective::<Instance4>::{Pallet, Call, Storage, Event<T>, Origin<T>, Config<T>} = 46,
RootTesting: pallet_root_testing::{Pallet, Call, Storage} = 47,
Erc20XcmBridge: pallet_erc20_xcm_bridge::{Pallet} = 48,
Multisig: pallet_multisig::{Pallet, Call, Storage, Event<T>} = 49,
}
}
/// Block type as expected by this runtime.
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// A Block signed with a Justification
pub type SignedBlock = generic::SignedBlock<Block>;
/// BlockId type as expected by this runtime.
pub type BlockId = generic::BlockId<Block>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtra = (
frame_system::CheckNonZeroSender<Runtime>,
frame_system::CheckSpecVersion<Runtime>,
frame_system::CheckTxVersion<Runtime>,
frame_system::CheckGenesis<Runtime>,
frame_system::CheckEra<Runtime>,
frame_system::CheckNonce<Runtime>,
frame_system::CheckWeight<Runtime>,
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic =
fp_self_contained::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
/// Extrinsic type that has already been checked.
pub type CheckedExtrinsic =
fp_self_contained::CheckedExtrinsic<AccountId, RuntimeCall, SignedExtra, H160>;
/// Executive: handles dispatch to the various pallets.
pub type Executive = frame_executive::Executive<
Runtime,
Block,
frame_system::ChainContext<Runtime>,
Runtime,
pallet_maintenance_mode::ExecutiveHooks<Runtime>,
>;
// All of our runtimes share most of their Runtime API implementations.
// We use a macro to implement this common part and add runtime-specific additional implementations.
// This macro expands to :
// ```
// impl_runtime_apis! {
// // All impl blocks shared between all runtimes.
//
// // Specific impls provided to the `impl_runtime_apis_plus_common!` macro.
// }
// ```
moonbeam_runtime_common::impl_runtime_apis_plus_common! {
impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(
source: TransactionSource,
xt: <Block as BlockT>::Extrinsic,
block_hash: <Block as BlockT>::Hash,
) -> TransactionValidity {
// Filtered calls should not enter the tx pool as they'll fail if inserted.
// If this call is not allowed, we return early.
if !<Runtime as frame_system::Config>::BaseCallFilter::contains(&xt.0.function) {
return InvalidTransaction::Call.into();
}
// This runtime uses Substrate's pallet transaction payment. This
// makes the chain feel like a standard Substrate chain when submitting
// frame transactions and using Substrate ecosystem tools. It has the downside that
// transaction are not prioritized by gas_price. The following code reprioritizes
// transactions to overcome this.
//
// A more elegant, ethereum-first solution is
// a pallet that replaces pallet transaction payment, and allows users
// to directly specify a gas price rather than computing an effective one.
// #HopefullySomeday
// First we pass the transactions to the standard FRAME executive. This calculates all the
// necessary tags, longevity and other properties that we will leave unchanged.
// This also assigns some priority that we don't care about and will overwrite next.
let mut intermediate_valid = Executive::validate_transaction(source, xt.clone(), block_hash)?;
let dispatch_info = xt.get_dispatch_info();
// If this is a pallet ethereum transaction, then its priority is already set
// according to effective priority fee from pallet ethereum. If it is any other kind of
// transaction, we modify its priority. The goal is to arrive at a similar metric used
// by pallet ethereum, which means we derive a fee-per-gas from the txn's tip and
// weight.
Ok(match &xt.0.function {
RuntimeCall::Ethereum(transact { .. }) => intermediate_valid,
_ if dispatch_info.class != DispatchClass::Normal => intermediate_valid,
_ => {
let tip = match xt.0.signature {
None => 0,
Some((_, _, ref signed_extra)) => {
// Yuck, this depends on the index of charge transaction in Signed Extra
let charge_transaction = &signed_extra.7;
charge_transaction.tip()
}
};
let effective_gas =
<Runtime as pallet_evm::Config>::GasWeightMapping::weight_to_gas(
dispatch_info.weight
);
let tip_per_gas = if effective_gas > 0 {
tip.saturating_div(effective_gas as u128)
} else {
0
};
// Overwrite the original prioritization with this ethereum one
intermediate_valid.priority = tip_per_gas as u64;
intermediate_valid
}
})
}
}
}
// Check the timestamp and parachain inherents
struct CheckInherents;
impl cumulus_pallet_parachain_system::CheckInherents<Block> for CheckInherents {
fn check_inherents(
block: &Block,
relay_state_proof: &RelayChainStateProof,
) -> sp_inherents::CheckInherentsResult {
let relay_chain_slot = relay_state_proof
.read_slot()
.expect("Could not read the relay chain slot from the proof");
let inherent_data =
cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration(
relay_chain_slot,
sp_std::time::Duration::from_secs(6),
)
.create_inherent_data()
.expect("Could not create the timestamp inherent data");
inherent_data.check_extrinsics(&block)
}
}
// Nimbus's Executive wrapper allows relay validators to verify the seal digest
cumulus_pallet_parachain_system::register_validate_block!(
Runtime = Runtime,
BlockExecutor = pallet_author_inherent::BlockExecutor::<Runtime, Executive>,
CheckInherents = CheckInherents,
);
moonbeam_runtime_common::impl_self_contained_call!();
// Shorthand for a Get field of a pallet Config.
#[macro_export]
macro_rules! get {
($pallet:ident, $name:ident, $type:ty) => {
<<$crate::Runtime as $pallet::Config>::$name as $crate::Get<$type>>::get()
};
}
#[cfg(test)]
mod tests {
use super::{currency::*, *};
#[test]
// Helps us to identify a Pallet Call in case it exceeds the 1kb limit.
// Hint: this should be a rare case. If that happens, one or more of the dispatchable arguments
// need to be Boxed.
fn call_max_size() {
const CALL_ALIGN: u32 = 1024;
assert!(
std::mem::size_of::<pallet_ethereum_chain_id::Call<Runtime>>() <= CALL_ALIGN as usize
);
assert!(std::mem::size_of::<pallet_evm::Call<Runtime>>() <= CALL_ALIGN as usize);
assert!(std::mem::size_of::<pallet_ethereum::Call<Runtime>>() <= CALL_ALIGN as usize);
assert!(
std::mem::size_of::<pallet_parachain_staking::Call<Runtime>>() <= CALL_ALIGN as usize
);
assert!(
std::mem::size_of::<pallet_author_inherent::Call<Runtime>>() <= CALL_ALIGN as usize
);
assert!(
std::mem::size_of::<pallet_author_slot_filter::Call<Runtime>>() <= CALL_ALIGN as usize
);
assert!(
std::mem::size_of::<pallet_crowdloan_rewards::Call<Runtime>>() <= CALL_ALIGN as usize
);
assert!(std::mem::size_of::<pallet_author_mapping::Call<Runtime>>() <= CALL_ALIGN as usize);
assert!(
std::mem::size_of::<pallet_maintenance_mode::Call<Runtime>>() <= CALL_ALIGN as usize
);
assert!(std::mem::size_of::<orml_xtokens::Call<Runtime>>() <= CALL_ALIGN as usize);
assert!(std::mem::size_of::<pallet_asset_manager::Call<Runtime>>() <= CALL_ALIGN as usize);
assert!(std::mem::size_of::<pallet_migrations::Call<Runtime>>() <= CALL_ALIGN as usize);
assert!(std::mem::size_of::<pallet_xcm_transactor::Call<Runtime>>() <= CALL_ALIGN as usize);
assert!(
std::mem::size_of::<pallet_proxy_genesis_companion::Call<Runtime>>()
<= CALL_ALIGN as usize
);
}
#[test]
fn currency_constants_are_correct() {
assert_eq!(SUPPLY_FACTOR, 1);
// txn fees
assert_eq!(TRANSACTION_BYTE_FEE, Balance::from(1 * GIGAWEI));
assert_eq!(
get!(pallet_transaction_payment, OperationalFeeMultiplier, u8),
5_u8
);
assert_eq!(STORAGE_BYTE_FEE, Balance::from(100 * MICROUNIT));
// democracy minimums
assert_eq!(
get!(pallet_democracy, MinimumDeposit, u128),
Balance::from(4 * UNIT)
);
assert_eq!(
get!(pallet_preimage, ByteDeposit, u128),
Balance::from(100 * MICROUNIT)
);
assert_eq!(
get!(pallet_treasury, ProposalBondMinimum, u128),
Balance::from(1 * UNIT)
);
// pallet_identity deposits
assert_eq!(
get!(pallet_identity, BasicDeposit, u128),
Balance::from(1 * UNIT + 25800 * MICROUNIT)
);
assert_eq!(
get!(pallet_identity, FieldDeposit, u128),
Balance::from(6600 * MICROUNIT)
);
assert_eq!(
get!(pallet_identity, SubAccountDeposit, u128),
Balance::from(1 * UNIT + 5300 * MICROUNIT)
);
// staking minimums
assert_eq!(
get!(pallet_parachain_staking, MinCandidateStk, u128),
Balance::from(500 * UNIT)
);
assert_eq!(
get!(pallet_parachain_staking, MinDelegation, u128),
Balance::from(1 * UNIT)
);
assert_eq!(
get!(pallet_parachain_staking, MinDelegatorStk, u128),
Balance::from(1 * UNIT)
);
// crowdloan min reward
assert_eq!(
get!(pallet_crowdloan_rewards, MinimumReward, u128),
Balance::from(0u128)
);
// deposit for AuthorMapping
assert_eq!(
get!(pallet_author_mapping, DepositAmount, u128),
Balance::from(100 * UNIT)
);
// proxy deposits
assert_eq!(
get!(pallet_proxy, ProxyDepositBase, u128),
Balance::from(1 * UNIT + 800 * MICROUNIT)
);
assert_eq!(
get!(pallet_proxy, ProxyDepositFactor, u128),
Balance::from(2100 * MICROUNIT)
);
assert_eq!(
get!(pallet_proxy, AnnouncementDepositBase, u128),
Balance::from(1 * UNIT + 800 * MICROUNIT)
);
assert_eq!(
get!(pallet_proxy, AnnouncementDepositFactor, u128),
Balance::from(5600 * MICROUNIT)
);
}
#[test]
// Required migration is
// pallet_parachain_staking::migrations::IncreaseMaxTopDelegationsPerCandidate
// Purpose of this test is to remind of required migration if constant is ever changed
fn updating_maximum_delegators_per_candidate_requires_configuring_required_migration() {
assert_eq!(
get!(pallet_parachain_staking, MaxTopDelegationsPerCandidate, u32),
300
);
assert_eq!(
get!(
pallet_parachain_staking,
MaxBottomDelegationsPerCandidate,
u32
),
50
);
}
#[test]
fn test_proxy_type_can_be_decoded_from_valid_values() {
let test_cases = vec![
// (input, expected)
(0u8, ProxyType::Any),
(1, ProxyType::NonTransfer),
(2, ProxyType::Governance),
(3, ProxyType::Staking),
(4, ProxyType::CancelProxy),
(5, ProxyType::Balances),
(6, ProxyType::AuthorMapping),
(7, ProxyType::IdentityJudgement),
];
for (input, expected) in test_cases {
let actual = ProxyType::decode(&mut input.to_le_bytes().as_slice());
assert_eq!(
Ok(expected),
actual,
"failed decoding ProxyType for value '{}'",
input
);
}
}
#[test]
fn configured_base_extrinsic_weight_is_evm_compatible() {
let min_ethereum_transaction_weight = WeightPerGas::get() * 21_000;
let base_extrinsic = <Runtime as frame_system::Config>::BlockWeights::get()
.get(frame_support::dispatch::DispatchClass::Normal)
.base_extrinsic;
assert!(base_extrinsic.ref_time() <= min_ethereum_transaction_weight.ref_time());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment