Rust质押池管理库pallet-nomination-pools的使用,实现高效安全的权益质押与委托功能

Rust质押池管理库pallet-nomination-pools的使用,实现高效安全的权益质押与委托功能

安装

在项目目录中运行以下Cargo命令:

cargo add pallet-nomination-pools

或者在Cargo.toml中添加以下行:

pallet-nomination-pools = "40.0.0"

使用示例

以下是一个使用pallet-nomination-pools实现质押池管理的完整示例:

use frame_support::traits::{Currency, OnUnbalanced};
use pallet_nomination_pools::{self as pools, PoolId};
use sp_runtime::traits::{AccountIdConversion, Saturating};
use frame_system::Config as SystemConfig;

// 定义你的运行时配置
pub trait Config: pools::Config + SystemConfig {
    // 添加你的运行时特定配置
}

// 创建质押池
fn create_pool<T: Config>(
    depositor: T::AccountId,
    amount: BalanceOf<T>,
    root: T::AccountId,
    nominator: T::AccountId,
    state_toggler: T::AccountId,
) -> Result<PoolId, pools::Error<T>> {
    // 检查余额是否足够
    let min_create_bond = pools::MinCreateBond::<T>::get();
    ensure!(amount >= min_create_bond, pools::Error::<T>::InsufficientBond);
    
    // 创建质押池
    pools::Pallet::<T>::create(
        depositor,
        amount,
        root,
        nominator,
        state_toggler,
    )
}

// 加入质押池
fn join_pool<T: Config>(
    who: T::AccountId,
    pool_id: PoolId,
    amount: BalanceOf<T>,
) -> Result<(), pools::Error<T>> {
    // 检查池是否存在
    ensure!(pools::Pallet::<T>::pool_exists(pool_id), pools::Error::<T>::PoolNotFound);
    
    // 加入池
    pools::Pallet::<T>::join(
        who,
        pool_id,
        amount,
    )
}

// 提名验证人
fn nominate<T: Config>(
    pool_id: PoolId,
    validators: Vec<T::AccountId>,
) -> Result<(), pools::Error<T>> {
    // 获取池账户
    let pool_account = pools::Pallet::<T>::create_pool_account(pool_id);
    
    // 提名验证人
    pools::Pallet::<T>::nominate(
        pool_account,
        validators,
    )
}

// 提取奖励
fn claim_payout<T: Config>(
    member_account: T::AccountId,
) -> Result<(), pools::Error<T>> {
    // 提取奖励
    pools::Pallet::<T>::claim_payout(member_account)
}

// 解质押
fn unbond<T: Config>(
    member_account: T::AccountId,
    unbond_points: BalanceOf<T>,
) -> Result<(), pools::Error<T>> {
    // 解质押
    pools::Pallet::<T>::unbond(
        member_account,
        unbond_points,
    )
}

完整示例代码

use frame_support::{
    assert_ok,
    traits::{Currency, OnUnbalanced},
};
use frame_system::Config as SystemConfig;
use pallet_nomination_pools::{self as pools, BondExtra, PoolId};
use sp_core::H256;
use sp_runtime::{
    testing::Header,
    traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, Saturating},
    Perbill,
};

// 定义测试环境类型
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;

// 配置测试运行时
frame_support::construct_runtime!(
    pub enum Test where
        Block = Block,
        NodeBlock = Block,
        UncheckedExtrinsic = UncheckedExtrinsic,
    {
        System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
        Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
        Staking: pallet_staking::{Pallet, Call, Storage, Config<T>, Event<T>},
        NominationPools: pallet_nomination_pools::{
            Pallet, Call, Storage, Event<T>, Config<T>
        },
    }
);

// 实现系统配置
impl frame_system::Config for Test {
    type BaseCallFilter = frame_support::traits::Everything;
    type BlockWeights = ();
    type BlockLength = ();
    type DbWeight = ();
    type Origin = Origin;
    type Index = u64;
    type BlockNumber = u64;
    type Hash = H256;
    type Call = Call;
    type Hashing = BlakeTwo256;
    type AccountId = u64;
    type Lookup = IdentityLookup<Self::AccountId>;
    type Header = Header;
    type Event = Event;
    type BlockHashCount = ();
    type Version = ();
    type PalletInfo = PalletInfo;
    type AccountData = pallet_balances::AccountData<u64>;
    type OnNewAccount = ();
    type OnKilledAccount = ();
    type SystemWeightInfo = ();
    type SS58Prefix = ();
    type OnSetCode = ();
    type MaxConsumers = frame_support::traits::ConstU32<16>;
}

// 实现余额配置
impl pallet_balances::Config for Test {
    type Balance = u64;
    type DustRemoval = ();
    type Event = Event;
    type ExistentialDeposit = frame_support::traits::ConstU64<1>;
    type AccountStore = System;
    type WeightInfo = ();
    type MaxLocks = ();
    type MaxReserves = ();
    type ReserveIdentifier = [u8; 8];
}

// 实现质押配置
impl pallet_staking::Config for Test {
    type Currency = Balances;
    type UnixTime = ();
    type CurrencyToVote = ();
    type RewardRemainder = ();
    type Event = Event;
    type Slash = ();
    type Reward = ();
    type SessionsPerEra = ();
    type SlashDeferDuration = ();
    type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
    type BondingDuration = frame_support::traits::ConstU32<3>;
    type SessionInterface = ();
    type EraPayout = ();
    type NextNewSession = ();
    type MaxNominatorRewardedPerValidator = frame_support::traits::ConstU32<64>;
    type OffendingValidatorsThreshold = ();
    type WeightInfo = ();
}

// 实现质押池配置
impl pallet_nomination_pools::Config for Test {
    type Event = Event;
    type WeightInfo = ();
    type Currency = Balances;
    type Stake = Staking;
    type PostUnbondingPoolsWindow = frame_support::traits::ConstU32<10>;
    type MaxMetadataLen = frame_support::traits::ConstU32<256>;
    type MaxPools = frame_support::traits::ConstU32<10>;
    type MaxPoolMembers = frame_support::traits::ConstU32<100>;
    type MaxPoolMembersPerPool = frame_support::traits::ConstU32<50>;
    type MinerReward = ();
    type BalanceToU256 = ();
    type U256ToBalance = ();
}

// 测试创建和加入质押池
#[test]
fn test_pool_creation_and_joining() {
    // 初始化测试环境
    let mut ext = sp_io::TestExternalities::new_empty();
    ext.execute_with(|| {
        // 创建初始账户
        let root = 1;
        let depositor = 2;
        let member = 3;
        
        // 设置初始余额
        Balances::make_free_balance_be(&depositor, 1000);
        Balances::make_free_balance_be(&member, 500);
        
        // 创建质押池
        assert_ok!(NominationPools::create(
            Origin::signed(depositor),
            100,  // 质押金额
            root, // 根账户
            root, // 提名账户
            root  // 状态切换账户
        ));
        
        // 获取创建的池ID
        let pool_id = 1;
        
        // 验证池是否存在
        assert!(NominationPools::pool_exists(pool_id));
        
        // 加入质押池
        assert_ok!(NominationPools::join(
            Origin::signed(member),
            pool_id,
            200  // 质押金额
        ));
        
        // 验证成员是否已加入池
        assert!(NominationPools::is_pool_member(&member).is_some());
    });
}

// 测试提名验证人
#[test]
fn test_nominating_validators() {
    let mut ext = sp_io::TestExternalities::new_empty();
    ext.execute_with(|| {
        // 创建初始账户
        let root = 1;
        let depositor = 2;
        let validator = 10;
        
        // 设置初始余额
        Balances::make_free_balance_be(&depositor, 1000);
        
        // 创建质押池
        assert_ok!(NominationPools::create(
            Origin::signed(depositor),
            100,
            root,
            root,
            root
        ));
        
        let pool_id = 1;
        let pool_account = NominationPools::create_pool_account(pool_id);
        
        // 提名验证人
        assert_ok!(NominationPools::nominate(
            Origin::signed(root),
            pool_id,
            vec![validator]
        ));
        
        // 验证提名是否成功
        let nominations = Staking::nominations(pool_account);
        assert!(nominations.targets.contains(&validator));
    });
}

// 测试解质押和提取奖励
#[test]
fn test_unbonding_and_claiming() {
    let mut ext = sp_io::TestExternalities::new_empty();
    ext.execute_with(|| {
        // 创建初始账户
        let root = 1;
        let depositor = 2;
        let member = 3;
        
        // 设置初始余额
        Balances::make_free_balance_be(&depositor, 1000);
        Balances::make_free_balance_be(&member, 500);
        
        // 创建质押池
        assert_ok!(NominationPools::create(
            Origin::signed(depositor),
            100,
            root,
            root,
            root
        ));
        
        let pool_id = 1;
        
        // 加入质押池
        assert_ok!(NominationPools::join(
            Origin::signed(member),
            pool_id,
            200
        ));
        
        // 解质押部分金额
        assert_ok!(NominationPools::unbond(
            Origin::signed(member),
            pool_id,
            50
        ));
        
        // 模拟奖励分配
        
        // 提取奖励
        assert_ok!(NominationPools::claim_payout(
            Origin::signed(member)
        ));
    });
}

功能说明

  1. 创建质押池:允许用户创建新的质押池,成为池管理员
  2. 加入质押池:其他用户可以将资金存入池中参与质押
  3. 提名验证人:池管理员可以提名验证人集合
  4. 奖励分配:自动分配质押奖励给池成员
  5. 解质押:成员可以解质押他们的资金

安全特性

  1. 资金隔离:每个池的资金存储在单独的账户中
  2. 角色分离:池管理权限可以分配给不同的角色
  3. 最小质押限制:防止小额质押导致的系统过载
  4. 解质押冷却期:防止频繁进出质押池

1 回复

Rust质押池管理库pallet-nomination-pools的使用指南

概述

pallet-nomination-pools是Substrate框架中的一个模块,用于实现高效安全的权益质押(PoS)与委托功能。它允许用户将他们的代币集中在一起,作为一个集体进行质押,同时保持对各自资金的所有权。

主要功能

  1. 创建和管理质押池
  2. 允许用户加入现有池进行委托质押
  3. 自动分配质押奖励
  4. 提供安全的资金提取机制
  5. 支持池成员的动态变化

使用方法

1. 创建质押池

use frame_support::traits::Currency;
use pallet_nomination_pools::{PoolId, Config};

// 创建新池的参数
let amount_to_bond: BalanceOf<T> = 1000u32.into(); // 初始绑定金额
let root: T::AccountId = account("root", 0, 0); // 池管理员
let nominator: T::AccountId = account("nominator", 0, 0); // 提名人账户
let bouncer: T::AccountId = account("bouncer", 0, 0); // 资金提取管理者

// 创建新池
let create_pool_result = Pools::create(
    origin,
    amount_to_bond,
    root.clone(),
    nominator.clone(),
    bouncer.clone(),
);

2. 加入现有质押池

use pallet_nomination_pools::{PoolId, BondExtra};

// 加入池并绑定资金
let pool_id: PoolId = 1; // 要加入的池ID
let amount: BalanceOf<T> = 500u32.into(); // 要绑定的金额

// 加入池
let join_result = Pools::join(
    origin,
    amount,
    pool_id,
);

// 或者使用BondExtra增加绑定
let bond_extra_result = Pools::bond_extra(
    origin,
    BondExtra::FreeBalance(amount),
);

3. 提取奖励

use pallet_nomination-pools::{PoolId, ClaimPermission};

// 设置奖励领取权限
let set_claim_permission_result = Pools::set_claim_permission(
    origin,
    ClaimPermission::Permissionless,
);

// 领取奖励
let claim_result = Pools::claim_payout(
    origin,
);

4. 解绑和提取资金

use pallet_nomination_pools::{PoolId, UnbondPoolMembers};

// 解绑部分资金
let unbond_amount: BalanceOf<T> = 200u32.into();
let unbond_result = Pools::unbond(
    origin,
    member_account,
    unbond_amount,
);

// 完全解绑并提取资金
let withdraw_result = Pools::withdraw_unbonded(
    origin,
    member_account,
    num_slashing_spans,
);

示例:完整的质押池生命周期

use frame_support::{assert_ok, traits::Currency};
use pallet_nomination_pools::{
    BondExtra, ClaimPermission, Config, PoolId, PoolState,
    UnbondPoolMembers,
};

// 1. 创建池
assert_ok!(Pools::create(
    RuntimeOrigin::signed(root),
    1000u32.into(),
    root.clone(),
    nominator.clone(),
    bouncer.clone(),
));

// 2. 成员加入池
assert_ok!(Pools::join(
    RuntimeOrigin::signed(member1),
    500u32.into(),
    1, // 池ID
));

// 3. 绑定额外资金
assert_ok!(Pools::bond_extra(
    RuntimeOrigin::signed(member1),
    BondExtra::FreeBalance(300u32.into()),
));

// 4. 设置奖励领取权限
assert_ok!(Pools::set_claim_permission(
    RuntimeOrigin::signed(member1),
    ClaimPermission::Permissionless,
));

// 5. 模拟获得奖励后领取
assert_ok!(Pools::claim_payout(
    RuntimeOrigin::signed(member1),
));

// 6. 解绑部分资金
assert_ok!(Pools::unbond(
    RuntimeOrigin::signed(member1),
    member1.clone(),
    200u32.into(),
));

// 7. 提取解绑的资金
assert_ok!(Pools::withdraw_unbonded(
    RuntimeOrigin::signed(member1),
    member1.clone(),
    0,
));

完整示例DEMO

下面是一个更完整的质押池使用示例,展示了从创建到解绑的完整生命周期:

use frame_support::{assert_ok, traits::Currency};
use frame_system::RawOrigin;
use pallet_nomination_pools::{
    BondExtra, Call, ClaimPermission, Config, Error, Event, PoolId,
    PoolState,
};
use sp_runtime::traits::StaticLookup;

// 定义测试环境
type Test = crate::mock::Test;

// 测试账户
fn account(name: &'static str, index: u32, seed: u32) -> <Test as frame_system::Config>::AccountId {
    let entropy = format!("{}{}{}", name, index, seed);
    <Test as frame_system::Config>::Hashing::hash(entropy.as_bytes())
        .into()
}

#[test]
fn test_full_pool_lifecycle() {
    // 初始化测试环境
    crate::mock::new_test_ext().execute_with(|| {
        // 1. 准备账户
        let root = account("root", 0, 0);
        let nominator = account("nominator", 0, 0);
        let bouncer = account("bouncer", 0, 0);
        let member1 = account("member1", 0, 0);
        let member2 = account("member2", 0, 0);
        
        // 2. 创建质押池
        assert_ok!(Call::<Test>::create {
            amount: 1000,
            root: root.clone(),
            nominator: nominator.clone(),
            bouncer: bouncer.clone(),
        }.dispatch(RawOrigin::Signed(root.clone()).into()));
        
        // 3. 成员加入池
        assert_ok!(Call::<Test>::join {
            amount: 500,
            pool_id: 1,
        }.dispatch(RawOrigin::Signed(member1.clone()).into()));
        
        // 4. 另一个成员加入
        assert_ok!(Call::<Test>::join {
            amount: 700,
            pool_id: 1,
        }.dispatch(RawOrigin::Signed(member2.clone()).into()));
        
        // 5. 成员1增加绑定
        assert_ok!(Call::<Test>::bond_extra {
            extra: BondExtra::FreeBalance(300),
        }.dispatch(RawOrigin::Signed(member1.clone()).into()));
        
        // 6. 设置奖励领取权限
        assert_ok!(Call::<Test>::set_claim_permission {
            permission: ClaimPermission::Permissionless,
        }.dispatch(RawOrigin::Signed(member1.clone()).into()));
        
        // 7. 模拟奖励分配后领取奖励
        assert_ok!(Call::<Test>::claim_payout {}.dispatch(RawOrigin::Signed(member1.clone()).into()));
        
        // 8. 解绑部分资金
        assert_ok!(Call::<Test>::unbond {
            member_account: member1.clone(),
            points: 200,
        }.dispatch(RawOrigin::Signed(member1.clone()).into()));
        
        // 9. 提取解绑的资金
        assert_ok!(Call::<Test>::withdraw_unbonded {
            member_account: member1.clone(),
            num_slashing_spans: 0,
        }.dispatch(RawOrigin::Signed(member1.clone()).into()));
        
        // 10. 验证最终状态
        let pool = Pools::<Test>::pool(1).unwrap();
        assert_eq!(pool.state, PoolState::Open);
    });
}

最佳实践

  1. 池管理:确保选择可信的池管理员和提名人
  2. 安全:定期检查池的状态和奖励分配
  3. 解绑周期:注意解绑期(通常需要等待多个era才能提取资金)
  4. 费用:了解池可能收取的佣金或费用结构
  5. 监控:监控池的性能和活跃度

注意事项

  • 质押操作通常需要多个区块确认
  • 解绑资金有冷却期,不能立即提取
  • 参与质押可能会使资金暂时无法流动
  • 质押奖励会根据网络规则和池表现而变化

通过pallet-nomination-pools,用户可以安全高效地参与权益质押,而无需自己运行验证节点,同时享受集体质押的好处。

回到顶部