adding-pallets
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdding a New Pallet
添加新的Pallet
Contents
目录
Pallet Creation Workflow
Pallet创建流程
1. Create Pallet Structure
1. 创建Pallet结构
bash
undefinedbash
undefinedCreate pallet directory
Create pallet directory
mkdir -p pallets/my-pallet/src
undefinedmkdir -p pallets/my-pallet/src
undefined2. Create Cargo.toml
2. 创建Cargo.toml
toml
undefinedtoml
undefinedpallets/my-pallet/Cargo.toml
pallets/my-pallet/Cargo.toml
[package]
name = "pallet-my-pallet"
version = "0.1.0"
edition = "2021"
description = "Description of the pallet"
[dependencies]
[package]
name = "pallet-my-pallet"
version = "0.1.0"
edition = "2021"
description = "Description of the pallet"
[dependencies]
Parity
Parity
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
Substrate
Substrate
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }
Benchmarking
Benchmarking
frame-benchmarking = { workspace = true, optional = true }
[dev-dependencies]
sp-io = { workspace = true, features = ["std"] }
sp-core = { workspace = true, features = ["std"] }
[features]
default = ["std"]
std = [
"parity-scale-codec/std",
"scale-info/std",
"frame-support/std",
"frame-system/std",
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
]
undefinedframe-benchmarking = { workspace = true, optional = true }
[dev-dependencies]
sp-io = { workspace = true, features = ["std"] }
sp-core = { workspace = true, features = ["std"] }
[features]
default = ["std"]
std = [
"parity-scale-codec/std",
"scale-info/std",
"frame-support/std",
"frame-system/std",
"sp-runtime/std",
"sp-std/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
]
undefined3. Create Pallet Implementation
3. 实现Pallet逻辑
rust
// pallets/my-pallet/src/lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
pub use weights::WeightInfo;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Weight information for extrinsics.
type WeightInfo: WeightInfo;
}
#[pallet::storage]
#[pallet::getter(fn something)]
pub type Something<T: Config> = StorageValue<_, u32, OptionQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Something was done.
SomethingDone { value: u32 },
}
#[pallet::error]
pub enum Error<T> {
/// Value is invalid.
InvalidValue,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::do_something())]
pub fn do_something(origin: OriginFor<T>, value: u32) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(value > 0, Error::<T>::InvalidValue);
Something::<T>::put(value);
Self::deposit_event(Event::SomethingDone { value });
Ok(())
}
}
}rust
// pallets/my-pallet/src/lib.rs
#![cfg_attr(not(feature = "std"), no_std)]
pub use pallet::*;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod weights;
pub use weights::WeightInfo;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Weight information for extrinsics.
type WeightInfo: WeightInfo;
}
#[pallet::storage]
#[pallet::getter(fn something)]
pub type Something<T: Config> = StorageValue<_, u32, OptionQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Something was done.
SomethingDone { value: u32 },
}
#[pallet::error]
pub enum Error<T> {
/// Value is invalid.
InvalidValue,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::do_something())]
pub fn do_something(origin: OriginFor<T>, value: u32) -> DispatchResult {
let who = ensure_signed(origin)?;
ensure!(value > 0, Error::<T>::InvalidValue);
Something::<T>::put(value);
Self::deposit_event(Event::SomethingDone { value });
Ok(())
}
}
}4. Create Weight Trait
4. 创建Weight特性
rust
// pallets/my-pallet/src/weights.rs
use frame_support::weights::Weight;
pub trait WeightInfo {
fn do_something() -> Weight;
}
impl WeightInfo for () {
fn do_something() -> Weight {
Weight::from_parts(10_000, 0)
}
}rust
// pallets/my-pallet/src/weights.rs
use frame_support::weights::Weight;
pub trait WeightInfo {
fn do_something() -> Weight;
}
impl WeightInfo for () {
fn do_something() -> Weight {
Weight::from_parts(10_000, 0)
}
}5. Create Mock Runtime
5. 创建Mock Runtime
rust
// pallets/my-pallet/src/mock.rs
use crate as pallet_my_pallet;
use frame_support::{derive_impl, parameter_types};
use sp_runtime::BuildStorage;
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test {
System: frame_system,
MyPallet: pallet_my_pallet,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type Block = Block;
}
impl pallet_my_pallet::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}
pub fn new_test_ext() -> sp_io::TestExternalities {
frame_system::GenesisConfig::<Test>::default()
.build_storage()
.unwrap()
.into()
}rust
// pallets/my-pallet/src/mock.rs
use crate as pallet_my_pallet;
use frame_support::{derive_impl, parameter_types};
use sp_runtime::BuildStorage;
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test {
System: frame_system,
MyPallet: pallet_my_pallet,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type Block = Block;
}
impl pallet_my_pallet::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}
pub fn new_test_ext() -> sp_io::TestExternalities {
frame_system::GenesisConfig::<Test>::default()
.build_storage()
.unwrap()
.into()
}6. Create Tests
6. 编写测试用例
rust
// pallets/my-pallet/src/tests.rs
use crate::{mock::*, Error, Event};
use frame_support::{assert_noop, assert_ok};
#[test]
fn do_something_works() {
new_test_ext().execute_with(|| {
System::set_block_number(1);
assert_ok!(MyPallet::do_something(RuntimeOrigin::signed(1), 42));
assert_eq!(MyPallet::something(), Some(42));
System::assert_last_event(Event::SomethingDone { value: 42 }.into());
});
}
#[test]
fn do_something_fails_with_zero() {
new_test_ext().execute_with(|| {
assert_noop!(
MyPallet::do_something(RuntimeOrigin::signed(1), 0),
Error::<Test>::InvalidValue
);
});
}rust
// pallets/my-pallet/src/tests.rs
use crate::{mock::*, Error, Event};
use frame_support::{assert_noop, assert_ok};
#[test]
fn do_something_works() {
new_test_ext().execute_with(|| {
System::set_block_number(1);
assert_ok!(MyPallet::do_something(RuntimeOrigin::signed(1), 42));
assert_eq!(MyPallet::something(), Some(42));
System::assert_last_event(Event::SomethingDone { value: 42 }.into());
});
}
#[test]
fn do_something_fails_with_zero() {
new_test_ext().execute_with(|| {
assert_noop!(
MyPallet::do_something(RuntimeOrigin::signed(1), 0),
Error::<Test>::InvalidValue
);
});
}7. Add to Workspace
7. 添加到工作区
toml
undefinedtoml
undefinedCargo.toml (workspace root)
Cargo.toml (workspace root)
[workspace]
members = [
# ... existing members
"pallets/my-pallet",
]
undefined[workspace]
members = [
# ... existing members
"pallets/my-pallet",
]
undefined8. Integrate into Runtime
8. 集成到Runtime
rust
// runtime/moonbase/lib.rs (and moonbeam, moonriver)
// Add the pallet to construct_runtime!
construct_runtime!(
pub enum Runtime {
// ... existing pallets
MyPallet: pallet_my_pallet = 100, // Choose unique index
}
);
// Implement Config for the runtime
impl pallet_my_pallet::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_my_pallet::weights::SubstrateWeight<Runtime>;
}rust
// runtime/moonbase/lib.rs (and moonbeam, moonriver)
// Add the pallet to construct_runtime!
construct_runtime!(
pub enum Runtime {
// ... existing pallets
MyPallet: pallet_my_pallet = 100, // Choose unique index
}
);
// Implement Config for the runtime
impl pallet_my_pallet::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = pallet_my_pallet::weights::SubstrateWeight<Runtime>;
}9. Add Benchmarks (Optional but Recommended)
9. 添加基准测试(可选但推荐)
rust
// pallets/my-pallet/src/benchmarking.rs
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn do_something() {
let caller: T::AccountId = whitelisted_caller();
#[extrinsic_call]
do_something(RawOrigin::Signed(caller), 42);
assert_eq!(Something::<T>::get(), Some(42));
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(),
crate::mock::Test
);
}rust
// pallets/my-pallet/src/benchmarking.rs
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::v2::*;
use frame_system::RawOrigin;
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn do_something() {
let caller: T::AccountId = whitelisted_caller();
#[extrinsic_call]
do_something(RawOrigin::Signed(caller), 42);
assert_eq!(Something::<T>::get(), Some(42));
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(),
crate::mock::Test
);
}10. Run Benchmarks
10. 运行基准测试
bash
undefinedbash
undefinedGenerate weights
Generate weights
./scripts/run-benches-for-runtime.sh moonbase release pallet_my_pallet
undefined./scripts/run-benches-for-runtime.sh moonbase release pallet_my_pallet
undefinedChecklist
检查清单
- Create pallet directory and Cargo.toml
- Implement pallet with Config, Storage, Events, Errors, Calls
- Create weight trait with placeholder weights
- Create mock runtime for testing
- Write comprehensive tests
- Add to workspace Cargo.toml
- Add to all three runtimes (moonbase, moonbeam, moonriver)
- Implement benchmarks
- Generate production weights
- Add any necessary migrations
- Update runtime version if breaking changes
- 创建pallet目录和Cargo.toml
- 实现包含Config、Storage、Events、Errors、Calls的pallet
- 创建带占位权重的Weight特性
- 创建用于测试的Mock Runtime
- 编写全面的测试用例
- 添加到工作区Cargo.toml
- 添加到三个Runtime(moonbase、moonbeam、moonriver)
- 实现基准测试
- 生成生产环境权重
- 添加必要的迁移逻辑
- 若存在破坏性变更则更新Runtime版本
Common Patterns
常见模式
Storage Types
存储类型
- - Single value
StorageValue<_, T> - - Key-value map
StorageMap<_, Blake2_128Concat, K, V> - - Double key map
StorageDoubleMap<_, ..., K1, K2, V> - - Map with item count
CountedStorageMap<_, ..., K, V>
- - 单个值存储
StorageValue<_, T> - - 键值对映射
StorageMap<_, Blake2_128Concat, K, V> - - 双键映射
StorageDoubleMap<_, ..., K1, K2, V> - - 带计数的映射
CountedStorageMap<_, ..., K, V>
Origin Checking
Origin校验
- - Must be signed transaction
ensure_signed(origin)? - - Must be sudo/governance
ensure_root(origin)? - - Custom origin
T::SomeOrigin::ensure_origin(origin)?
- - 必须是签名交易
ensure_signed(origin)? - - 必须是超级管理员/治理权限
ensure_root(origin)? - - 自定义Origin
T::SomeOrigin::ensure_origin(origin)?
Weight Patterns
权重模式
- Use benchmarks for accurate weights
- Consider worst-case scenarios
- Include DB read/write counts
- 使用基准测试获取准确权重
- 考虑最坏场景
- 包含数据库读写计数