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提供了以下主要功能:

  1. register_fast_unstake - 注册快速解绑请求
  2. deregister - 取消快速解绑请求
  3. control - 管理员控制快速解绑队列

注意事项

  1. 使用前需要确保链上已配置好staking和balances pallet
  2. 快速解绑可能需要支付额外的费用
  3. 解绑速度取决于链的具体配置和当前队列状态
  4. 需要确保账户有足够的资金支付交易费用
  5. 解绑完成后资金会返回到账户可用余额

该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);

工作原理详解

  1. 请求阶段

    • 用户调用register_fast_unstake方法
    • 系统检查用户资格并冻结押金
    • 请求加入处理队列
  2. 处理阶段

    • 每个区块处理一定数量的请求
    • 验证用户质押状态
    • 执行即时解绑操作
  3. 结算阶段

    • 计算并扣除服务费
    • 返还剩余押金
    • 更新用户账户余额

高级配置选项

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());
    });
}
回到顶部