创建自定义 Pallet:从零构建你的 Polkadot Runtime 模块
本文介绍了使用Polkadot SDK中的FRAME框架开发自定义区块链模块(Pallet)的完整流程。主要内容包括:1) Pallet的核心组成结构;2) 创建Pallet项目及配置依赖;3) 初始化Pallet基础框架;4) 实现Config Trait、事件、错误类型等核心组件;5) 添加存储项和创世配置;6) 实现可调用函数;7) 将Pallet集成到Runtime并启动测试节点。通过开发
原文作者:PaperMoon团队
FRAME(Framework for Runtime Aggregation of Modular Entities)是 Polkadot SDK 提供的一套模块化区块链开发框架。它通过“Pallet(功能模块)”的方式,让开发者可以灵活组合链上功能。
Pallet 本质上是基于 Rust 编写的 Runtime 模块,用于实现链上的业务逻辑。FRAME 内置了大量常用 Pallet(如余额、治理、共识等),但其真正的优势在于支持开发者编写完全自定义的 Pallet,以满足特定业务需求。
本教程将从零开始,带你实现一个简单的「计数器 Pallet」,用于讲解 Pallet 开发的核心流程与设计模式。
前置条件(Prerequisites)
在开始之前,请确保你已经具备以下环境:
• 已正确安装 Polkadot SDK 相关依赖
• 已在本地初始化 Parachain Template
• 对 FRAME 的基本概念有一定了解
Pallet 的核心组成结构
一个标准 Pallet 通常由以下部分组成:
1. 依赖导入(Imports)
2. 配置 Trait(Config Trait)
3. 事件定义(Events)
4. 错误类型(Errors)
5. 链上存储(Storage)
6. 创世配置(Genesis Config)
7. 可调用函数(Extrinsics)
这些模块共同构成 Pallet 的完整生命周期。
创建 Pallet 项目
1. 进入 Parachain 项目根目录
cd polkadot-sdk-parachain-template
2. 进入 pallets 目录
cd pallets
3. 创建新的 Rust 库项目
cargo new --lib pallet-customcargo
4. 进入项目目录
cd pallet-custom
5. 目录结构应如下所示
pallet-custom/
├── Cargo.toml
└── src/
└── lib.rs
配置依赖(Configure Dependencies)
由于 Parachain Template 使用 Workspace 机制统一管理依赖版本,因此需要使用 workspace 继承方式配置依赖。
打开 pallet-custom/Cargo.toml,替换为以下内容:
[package]
name = "pallet-custom"
description = "A custom counter pallet for demonstration purposes."
version = "0.1.0"
license = "Unlicense"
authors.workspace = true
homepage.workspace = true
repository.workspace = true
edition.workspace = true
publish = false
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { features = ["derive"], workspace = true }
scale-info = { features = ["derive"], workspace = true }
frame = { features = ["experimental", "runtime"], workspace = true }
[features]
default = ["std"]
std = [
"codec/std",
"scale-info/std",
"frame/std",
]
版本管理说明
Workspace 会在根目录 Cargo.toml 中统一定义依赖版本:
[workspace.members]
members = [
"node",
"pallets/*",
"runtime",
]
因此无需在子项目中指定版本号。
初始化 Pallet 结构(Initialize Pallet Structure)
打开 src/lib.rs,删除原有内容,添加以下基础框架:
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
pub use pallet::*;
#[frame::pallet]
pub mod pallet {
use frame::prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: frame_system::Config {
}
#[pallet::storage]
pub type CounterValue<T> = StorageValue<_, u32, ValueQuery>;
#[pallet::call]
impl<T: Config> Pallet<T> {
}
}
该结构定义了:
• Pallet 主体
• Config Trait
• 存储结构
• 可调用接口区域
执行编译验证:
cargo build --package pallet-custom
配置 Config Trait(Configure the Pallet)
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
#[pallet::constant]
type CounterMaxValue: Get<u32>;
}
该配置定义:
• RuntimeEvent:事件系统绑定
• CounterMaxValue:计数器最大值常量
定义事件(Define Events)
添加事件模块:
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
CounterValueSet { new_value: u32 },
CounterIncremented {
new_value: u32,
who: T::AccountId,
amount: u32,
},
CounterDecremented {
new_value: u32,
who: T::AccountId,
amount: u32,
},
}
事件用于通知链下系统状态变更。
定义错误类型(Define Errors)
添加错误模块:
#[pallet::error]
pub enum Error<T> {
NoneValue,
Overflow,
Underflow,
CounterMaxValueExceeded,
}
错误信息会被写入 Runtime Metadata,便于前端解析。
添加存储项(Add Storage Items)
补充用户交互记录:
#[pallet::storage]
pub type CounterValue<T> = StorageValue<_, u32, ValueQuery>;
#[pallet::storage]
pub type UserInteractions<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, u32, ValueQuery>;
功能说明:
• CounterValue:全局计数器
• UserInteractions:用户操作次数统计
配置创世状态(Configure Genesis)
1. 导入 Vec
use alloc::vec::Vec;
2. 添加创世配置
#[pallet::genesis_config]
#[derive(DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub initial_counter_value: u32,
pub initial_user_interactions: Vec<(T::AccountId, u32)>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
CounterValue::<T>::put(self.initial_counter_value);
for (account, count) in &self.initial_user_interactions {
UserInteractions::<T>::insert(account, count);
}
}
}
Genesis 配置用于初始化链的起始状态。
实现可调用函数(Dispatchable Functions)
替换 #[pallet::call] 模块:
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(0)]
pub fn set_counter_value(
origin: OriginFor<T>,
new_value: u32,
) -> DispatchResult {
ensure_root(origin)?;
ensure!(
new_value <= T::CounterMaxValue::get(),
Error::<T>::CounterMaxValueExceeded
);
CounterValue::<T>::put(new_value);
Self::deposit_event(Event::CounterValueSet { new_value });
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight(0)]
pub fn increment(
origin: OriginFor<T>,
amount: u32,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let current = CounterValue::<T>::get();
let new_value =
current.checked_add(amount)
.ok_or(Error::<T>::Overflow)?;
ensure!(
new_value <= T::CounterMaxValue::get(),
Error::<T>::CounterMaxValueExceeded
);
CounterValue::<T>::put(new_value);
UserInteractions::<T>::mutate(&who, |c| {
*c = c.saturating_add(1);
});
Self::deposit_event(
Event::CounterIncremented {
new_value,
who,
amount,
}
);
Ok(())
}
#[pallet::call_index(2)]
#[pallet::weight(0)]
pub fn decrement(
origin: OriginFor<T>,
amount: u32,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let current = CounterValue::<T>::get();
let new_value =
current.checked_sub(amount)
.ok_or(Error::<T>::Underflow)?;
CounterValue::<T>::put(new_value);
UserInteractions::<T>::mutate(&who, |c| {
*c = c.saturating_add(1);
});
Self::deposit_event(
Event::CounterDecremented {
new_value,
who,
amount,
}
);
Ok(())
}
}
|
方法 |
权限 |
功能 |
|---|---|---|
|
set_counter_value |
Root |
设置计数器 |
|
increment |
Signed |
增加 |
|
decrement |
Signed |
减少 |
编译验证(Verify Compilation)
cargo build --package pallet-custom
集成到 Runtime(Add to Runtime)
1. 添加依赖
runtime/Cargo.toml:
pallet-custom = { path = "../pallets/pallet-custom", default-features = false }
features:
"pallet-custom/std",
2. 实现 Config
runtime/src/configs/mod.rs:
impl pallet_custom::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type CounterMaxValue = ConstU32<1000>;
}
3. 注册 Pallet
runtime/src/lib.rs:
#[runtime::pallet_index(51)]
pub type CustomPallet = pallet_custom;
注意:索引必须唯一。
生成链配置并启动节点
生成 Chain Spec
chain-spec-builder create -t development \
--relay-chain paseo \
--para-id 1000 \
--runtime ./target/release/wbuild/parachain-template-runtime/parachain_template_runtime.compact.compressed.wasm \
named-preset development
启动节点
polkadot-omni-node --chain ./chain_spec.json --dev
交互测试(Interact with Your Pallet)
打开 Polkadot.js Apps:
• 地址:ws://127.0.0.1:9944
• 进入 Developer → Extrinsics
• 选择 customPallet
可调用接口:
• increment
• decrement
• setCounterValue
核心总结(Key Takeaways)
通过本教程,你已经完成:
• 自定义 Pallet 架构设计
• Runtime 配置绑定
• 链上存储设计
• 事件与错误系统
• 创世状态初始化
• Extrinsic 访问控制
• Runtime 集成与部署
这些能力构成了 Polkadot Runtime 开发的基础框架,为后续开发复杂业务模块打下坚实基础。
原文链接:https://docs.polkadot.com/parachains/customize-runtime/pallet-development/create-a-pallet/
更多推荐


所有评论(0)