Rust质押解绑加速插件pallet-fast-unstake的使用,实现Substrate链上质押资产快速解绑与赎回
Rust质押解绑加速插件pallet-fast-unstake的使用
安装
在项目目录中运行以下Cargo命令:
cargo add pallet-fast-unstake
或者在Cargo.toml中添加以下行:
pallet-fast-unstake = "41.0.0"
使用示例
以下是一个完整的Substrate链上使用pallet-fast-unstake实现质押资产快速解绑与赎回的示例:
// 在runtime/src/lib.rs中配置pallet-fast-unstake
// 1. 引入必要的依赖
use frame_support::traits::Currency;
use pallet_fast_unstake::{Config, Pallet};
// 2. 配置pallet-fast-unstake
impl Config for Runtime {
type Event = Event;
type Currency = Balances; // 使用Balances作为货币类型
type Staking = Staking; // 连接到Staking pallet
type WeightInfo = ();
}
// 3. 将pallet添加到construct_runtime!宏中
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ...其他pallets...
FastUnstake: pallet_fast_unstake::{Pallet, Call, Storage, Event<T>},
}
);
// 在客户端代码中调用快速解绑
use sp_core::crypto::Ss58Codec;
use substrate_api_client::{Api, ExtrinsicParams};
// 1. 创建API客户端
let api = Api::new("ws://localhost:9944").unwrap();
// 2. 准备账户
let account = sp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
let account_id = account.public().to_ss58check();
// 3. 调用快速解绑
let call = pallet_fast_unstake::Call::<Runtime>::register_fast_unstake {};
let xt = api.xt(Some(account), None).unwrap();
let result = xt.send_and_watch(call);
match result {
Ok(_) => println!("快速解绑请求已提交"),
Err(e) => println!("快速解绑失败: {:?}", e),
}
完整示例代码
// runtime/src/lib.rs 完整配置示例
// 引入必要的依赖
use frame_support::{
construct_runtime, parameter_types,
traits::{Currency, OnFinalize, OnInitialize},
weights::Weight,
};
use frame_system::EnsureRoot;
use pallet_fast_unstake::{Config, Pallet};
use sp_runtime::{
traits::{IdentifyAccount, Verify},
MultiSignature,
};
// 定义Runtime类型
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
pub type BlockNumber = u32;
pub type Signature = MultiSignature;
pub type Index = u32;
// 参数配置
parameter_types! {
pub const BlockHashCount: BlockNumber = 2400;
pub const Version: RuntimeVersion = VERSION;
}
// 实现frame_system配置
impl frame_system::Config for Runtime {
type AccountId = AccountId;
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Index = Index;
type BlockNumber = BlockNumber;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountData = pallet_balances::AccountData<Balance>;
type OnSetCode = ();
type DbWeight = ();
type Version = Version;
type PalletInfo = PalletInfo;
type BlockHashCount = BlockHashCount;
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
// 实现fast-unstake配置
impl Config for Runtime {
type Event = Event;
type Currency = Balances; // 使用Balances作为货币类型
type Staking = Staking; // 连接到Staking pallet
type WeightInfo = ();
type AdminOrigin = EnsureRoot<AccountId>; // 管理员权限
}
// 添加到construct_runtime宏
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system,
Balances: pallet_balances,
Staking: pallet_staking,
FastUnstake: pallet_fast_unstake,
}
);
// ---------------------------------------------------
// 客户端调用示例 client.rs
use sp_core::{crypto::Ss58Codec, sr25519};
use substrate_api_client::{Api, ExtrinsicParams};
fn main() {
// 1. 创建API客户端连接
let api = Api::new("ws://localhost:9944")
.unwrap()
.set_signer(sr25519::Pair::from_string("//Alice", None).unwrap());
// 2. 获取账户信息
let account = api.signer().unwrap().clone();
let account_id = account.public().to_ss58check();
println!("使用账户: {}", account_id);
// 3. 检查当前质押状态
let staking_info = api.get_staking_info(&account_id).unwrap();
println!("当前质押信息: {:?}", staking_info);
// 4. 调用快速解绑
let call = pallet_fast_unstake::Call::<Runtime>::register_fast_unstake {};
let result = api.xt(None).unwrap().send_and_watch(call);
match result {
Ok(_) => println!("✅ 快速解绑请求已提交"),
Err(e) => println!("❌ 快速解绑失败: {:?}", e),
}
// 5. 可选: 取消解绑请求
// let call = pallet_fast_unstake::Call::<Runtime>::deregister {};
// api.send_extrinsic(call).unwrap();
}
功能说明
pallet-fast-unstake提供了以下主要功能:
register_fast_unstake
- 注册快速解绑请求deregister
- 取消快速解绑请求control
- 管理员控制快速解绑队列
注意事项
- 使用前需要确保链上已配置好staking和balances pallet
- 快速解绑可能需要支付额外的费用
- 解绑速度取决于链的具体配置和当前队列状态
- 需要确保账户有足够的资金支付交易费用
- 解绑完成后资金会返回到账户可用余额
该pallet是Parity Technologies维护的Polkadot SDK的一部分,遵循Apache-2.0许可证。
1 回复
Rust质押解绑加速插件pallet-fast-unstake使用指南
简介
pallet-fast-unstake
是Substrate框架中的一个模块,允许用户在链上质押资产时绕过常规的解绑等待期,实现快速解绑与赎回功能。这对于需要快速流动性的质押用户特别有用。
核心功能
- 绕过标准解绑等待期(通常为7-28天)
- 通过支付少量费用实现即时解绑
- 保持网络安全性不受影响
- 与Substrate的staking模块无缝集成
完整示例代码
1. runtime集成示例
// 定义参数类型
parameter_types! {
pub const FastUnstakeDeposit: Balance = 100 * DOLLARS; // 快速解押押金
pub const ErasToCheckPerBlock: u32 = 1; // 每个区块检查的era数量
pub const MaxFastUnstake: Balance = 10_000 * DOLLARS; // 最大快速解押金额
}
// 实现pallet配置
impl pallet_fast_unstake::Config for Runtime {
type RuntimeEvent = RuntimeEvent; // 事件类型
type Currency = Balances; // 货币类型
type Deposit = FastUnstakeDeposit; // 押金金额
type Staking = Staking; // 质押模块
type WeightInfo = (); // 权重信息
type ErasToCheckPerBlock = ErasToCheckPerBlock; // 每个区块检查的era数量
type MaxFastUnstake = MaxFastUnstake; // 最大快速解押金额
}
// 构建runtime
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ... 其他pallet
FastUnstake: pallet_fast_unstake::{Pallet, Call, Storage, Event<T>},
}
);
2. 完整后端调用示例
use frame_support::{dispatch::DispatchResult, ensure};
use frame_system::ensure_signed;
use sp_runtime::traits::AccountIdConversion;
// 快速解绑实现模块
pub mod fast_unstake {
use super::*;
// 检查用户是否符合快速解绑条件
pub fn can_fast_unstake(who: &AccountId) -> bool {
// 1. 检查是否有活跃提名
let nominations = Staking::nominations(who);
if nominations.is_some() {
return false;
}
// 2. 检查质押金额是否在允许范围内
if let Some(ledger) = Staking::ledger(who) {
ledger.active <= MaxFastUnstake::get()
} else {
false
}
}
// 执行快速解绑
pub fn register_fast_unstake(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
// 检查资格
ensure!(Self::can_fast_unstake(&who), Error::<T>::NotEligible);
// 扣除押金
T::Currency::reserve(&who, T::Deposit::get())?;
// 加入快速解绑队列
Queue::<T>::insert(who.clone(), ());
// 触发事件
Self::deposit_event(Event::Registered(who));
Ok(())
}
// 处理快速解绑
pub fn process_fast_unstake(who: AccountId) -> DispatchResult {
// 执行解绑逻辑
Staking::unstake(&who)?;
// 计算并扣除费用
let fee = calculate_fee(&who);
T::Currency::slash_reserved(&who, fee)?;
// 返还剩余押金
let remaining = T::Deposit::get().saturating_sub(fee);
T::Currency::unreserve(&who, remaining);
// 触发事件
Self::deposit_event(Event::Unstaked(who));
Ok(())
}
}
3. 完整前端调用示例
import { ApiPromise, WsProvider } from '@polkadot/api';
async function fastUnstake() {
// 1. 连接节点
const provider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider });
// 2. 检查用户是否符合条件
const accountId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
const nominations = await api.query.staking.nominators(accountId);
const ledger = await api.query.staking.ledger(accountId);
if (nominations.isEmpty && ledger.unwrap().active.lten(api.consts.fastUnstake.maxFastUnstake)) {
// 3. 发送快速解绑交易
const tx = api.tx.fastUnstake.registerFastUnstake();
const unsub = await tx.signAndSend(accountId, ({ status }) => {
if (status.isInBlock) {
console.log(`快速解绑请求已包含在区块 ${status.asInBlock}`);
unsub();
}
});
} else {
console.log('用户不符合快速解绑条件');
}
}
fastUnstake().catch(console.error);
工作原理详解
-
请求阶段:
- 用户调用
register_fast_unstake
方法 - 系统检查用户资格并冻结押金
- 请求加入处理队列
- 用户调用
-
处理阶段:
- 每个区块处理一定数量的请求
- 验证用户质押状态
- 执行即时解绑操作
-
结算阶段:
- 计算并扣除服务费
- 返还剩余押金
- 更新用户账户余额
高级配置选项
parameter_types! {
pub const FastUnstakeBatchSize: u32 = 20; // 每批处理的最大请求数
pub const FastUnstakeFeePercentage: Perbill = Perbill::from_percent(1); // 1%服务费
}
impl pallet_fast_unstake::Config for Runtime {
// ... 其他配置
type BatchSize = FastUnstakeBatchSize;
type FeePercentage = FastUnstakeFeePercentage;
}
测试用例示例
#[test]
fn test_fast_unstake_workflow() {
new_test_ext().execute_with(|| {
// 1. 设置测试账户和质押
let account_id = 1;
let stake_amount = 500 * DOLLARS;
Balances::make_free_balance_be(&account_id, 1000 * DOLLARS);
Staking::bond(Origin::signed(account_id), stake_amount, RewardDestination::Stash);
// 2. 尝试快速解绑
assert_ok!(FastUnstake::register_fast_unstake(Origin::signed(account_id)));
// 3. 模拟区块处理
FastUnstake::on_initialize(1);
// 4. 验证结果
assert!(Staking::ledger(&account_id).is_none());
assert_eq!(Balances::free_balance(&account_id), 1000 * DOLLARS - stake_amount - FastUnstakeDeposit::get());
});
}