Rust区块链薪资管理模块pallet-salary的使用,pallet-salary提供去中心化薪资发放与财务追踪功能
Rust区块链薪资管理模块pallet-salary的使用
pallet-salary是一个Substrate模块,用于根据等级定期向集体成员支付薪资。
安装
在项目目录中运行以下Cargo命令:
cargo add pallet-salary
或者在Cargo.toml中添加以下行:
pallet-salary = "26.0.0"
示例代码
以下是一个完整的pallet-salary使用示例:
// 导入必要的模块
use frame_support::{parameter_types, traits::Currency};
use pallet_salary as salary;
use sp_runtime::Perbill;
// 定义货币类型
pub type Balance = u128;
pub type AccountId = u64;
parameter_types! {
pub const SalaryPeriod: u64 = 30 * 24 * 60 * 60; // 30天(秒)
pub const SalaryBudget: Balance = 1_000_000; // 总预算
pub const SalaryPayoutFraction: Perbill = Perbill::from_percent(100); // 支付比例
}
// 配置薪资模块
impl salary::Config for Runtime {
type Event = Event;
type WeightInfo = ();
type Currency = Balances; // 使用balances模块作为货币
type Paymaster = Treasury; // 使用国库作为支付者
type Members = Membership; // 成员资格模块
type Salary = FixedSalary; // 固定薪资类型
type Period = SalaryPeriod;
type Budget = SalaryBudget;
type PayoutFraction = SalaryPayoutFraction;
}
// 固定薪资实现
pub struct FixedSalary;
impl salary::Salary<AccountId, Balance> for FixedSalary {
fn get_salary(who: &AccountId, rank: u16) -> Balance {
match rank {
1 => 10_000, // 等级1薪资
2 => 20_000, // 等级2薪资
3 => 30_000, // 等级3薪资
_ => 5_000, // 默认薪资
}
}
}
// 在runtime中集成模块
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ...其他模块
Salary: salary::{Module, Call, Storage, Event<T>},
}
);
// 使用示例 - 注册成员
fn register_member(account: AccountId, rank: u16) {
// 注册成员到成员资格模块
Membership::add_member(account);
// 设置成员等级
Salary::set_rank(Origin::signed(account), rank);
}
// 使用示例 - 发放薪资
fn payout_salaries() {
// 通常由链上调度器定期调用
Salary::payout_salaries(Origin::none());
}
功能说明
- 定期支付:根据设定的周期(如30天)自动发放薪资
- 等级薪资:不同等级成员获得不同数额的薪资
- 预算控制:设置总预算限制,防止超额支付
- 财务追踪:所有薪资支付记录在链上可查
完整示例代码
//! 完整的pallet-salary实现示例
use frame_support::{
construct_runtime, parameter_types,
traits::{Currency, OnInitialize},
weights::Weight,
};
use frame_system::EnsureRoot;
use pallet_balances as balances;
use pallet_salary as salary;
use pallet_treasury as treasury;
use sp_core::H256;
use sp_runtime::{
testing::Header,
traits::{BlakeTwo256, IdentityLookup},
Perbill,
};
// 类型别名
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>;
type Block = frame_system::mocking::MockBlock<Runtime>;
// 构建测试运行时
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Balances: balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Treasury: treasury::{Pallet, Call, Storage, Config, Event<T>},
Salary: salary::{Pallet, Call, Storage, Event<T>},
}
);
// 系统配置
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const SS58Prefix: u8 = 42;
}
impl frame_system::Config for Runtime {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = balances::AccountData<u128>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}
// 余额模块配置
parameter_types! {
pub const ExistentialDeposit: u128 = 1;
pub const MaxLocks: u32 = 50;
pub const MaxReserves: u32 = 50;
}
impl balances::Config for Runtime {
type Balance = u128;
type DustRemoval = ();
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
type MaxLocks = MaxLocks;
type MaxReserves = MaxReserves;
type ReserveIdentifier = [u8; 8];
type RuntimeHoldReason = ();
type RuntimeFreezeReason = ();
type FreezeIdentifier = ();
type MaxHolds = ();
type MaxFreezes = ();
}
// 国库模块配置
parameter_types! {
pub const ProposalBond: Permill = Permill::from_percent(5);
pub const ProposalBondMinimum: u128 = 1;
pub const SpendPeriod: u64 = 2;
pub const Burn: Permill = Permill::from_percent(50);
pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
pub const MaxApprovals: u32 = 100;
}
impl treasury::Config for Runtime {
type PalletId = TreasuryPalletId;
type Currency = Balances;
type ApproveOrigin = EnsureRoot<u64>;
type RejectOrigin = EnsureRoot<u64>;
type RuntimeEvent = RuntimeEvent;
type OnSlash = ();
type ProposalBond = ProposalBond;
type ProposalBondMinimum = ProposalBondMinimum;
type SpendPeriod = SpendPeriod;
type Burn = Burn;
type BurnDestination = ();
type SpendFunds = ();
type WeightInfo = ();
type MaxApprovals = MaxApprovals;
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u128>;
}
// 薪资模块配置
parameter_types! {
pub const SalaryPeriod: u64 = 30 * 24 * 60 * 60; // 30天(秒)
pub const SalaryBudget: u128 = 1_000_000; // 总预算
pub const SalaryPayoutFraction: Perbill = Perbill::from_percent(100); // 支付比例
}
// 简单的成员资格模块模拟
pub struct Membership;
impl salary::Membership<Runtime> for Membership {
fn is_member(who: &u64) -> bool {
// 实际应用中应从存储中查询
*who == 1 || *who == 2 || *who == 3
}
fn add_member(who: &u64) {
// 实际应用中应更新存储
println!("Added member: {}", who);
}
}
// 固定薪资实现
pub struct FixedSalary;
impl salary::Salary<u64, u128> for FixedSalary {
fn get_salary(who: &u64, rank: u16) -> u128 {
match rank {
1 => 10_000, // 等级1薪资
2 => 20_000, // 等级2薪资
3 => 30_000, // 等级3薪资
_ => 5_000, // 默认薪资
}
}
}
impl salary::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Currency = Balances;
type Paymaster = Treasury;
type Members = Membership;
type Salary = FixedSalary;
type Period = SalaryPeriod;
type Budget = SalaryBudget;
type PayoutFraction = SalaryPayoutFraction;
}
// 测试示例
#[test]
fn test_salary_payout() {
// 初始化测试环境
let mut t = frame_system::GenesisConfig::default()
.build_storage::<Runtime>()
.unwrap();
balances::GenesisConfig::<Runtime> {
balances: vec![
// 给国库初始资金
(1, 100_000_000),
],
}
.assimilate_storage(&mut t)
.unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| {
// 初始化区块
System::set_block_number(1);
// 注册成员
Membership::add_member(&2); // 账户2
Salary::set_rank(RuntimeOrigin::signed(2), 2).unwrap(); // 设置为等级2
// 模拟时间流逝到薪资周期结束
for _ in 0..SalaryPeriod::get() {
System::set_block_number(System::block_number() + 1);
Salary::on_initialize(System::block_number());
}
// 检查薪资是否发放
assert_eq!(Balances::free_balance(&2), 20_000);
});
}
功能说明
- 定期支付:根据设定的周期(如30天)自动发放薪资
- 等级薪资:不同等级成员获得不同数额的薪资
- 预算控制:设置总预算限制,防止超额支付
- 财务追踪:所有薪资支付记录在链上可查
1 回复
Rust区块链薪资管理模块pallet-salary使用指南
模块介绍
pallet-salary是Substrate框架中的一个功能模块,专门为区块链系统提供去中心化的薪资管理和财务追踪功能。它允许组织或DAO在区块链上透明地管理成员薪资发放、预算分配和财务记录。
主要功能
- 去中心化薪资发放
- 薪资周期管理
- 薪资预算分配与追踪
- 薪资支付记录
- 多币种支持
使用方法
1. 添加依赖
在项目的runtime/Cargo.toml
中添加pallet-salary依赖:
[dependencies.pallet-salary]
default-features = false
git = 'https://github.com/substrate-developer-hub/substrate-node-template'
branch = 'latest'
2. 配置runtime
在runtime/src/lib.rs
中配置pallet-salary:
impl pallet_salary::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Currency = Balances;
type Paymaster = Treasury; // 使用国库作为支付账户
type WeightInfo = ();
}
3. 初始化薪资系统
// 设置薪资周期(例如每月一次)
Salary::set_pay_period(30 * DAYS);
// 设置薪资预算
Salary::set_budget(1_000_000);
使用示例
1. 添加员工并设置薪资
// 添加员工并设置月薪
Salary::add_employee(employee_account, 5000);
// 更新员工薪资
Salary::update_salary(employee_account, 6000);
2. 发放薪资
// 管理员触发薪资发放
Salary::pay_salaries(origin);
3. 查询功能
// 查询员工薪资
let salary = Salary::employee_salary(employee_account);
// 查询当前薪资周期剩余预算
let remaining_budget = Salary::remaining_budget();
// 查询员工支付历史
let payment_history = Salary::payment_history(employee_account);
高级功能
多币种薪资
// 使用多币种支持
Salary::add_employee_with_currency(employee_account, 5000, CurrencyId::USD);
// 发放特定币种薪资
Salary::pay_salary_in_currency(employee_account, CurrencyId::USD);
薪资自动化
// 设置自动支付
Salary::enable_auto_pay(true);
// 设置自动支付阈值(当预算充足时自动支付)
Salary::set_auto_pay_threshold(800_000);
完整示例代码
//! 完整的薪资管理模块使用示例
use frame_support::{decl_module, dispatch::DispatchResult};
use frame_system::ensure_root;
use sp_runtime::traits::AccountIdConversion;
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type Currency: Currency<Self::AccountId>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::storage]
#[pallet::getter(fn something)]
pub type Something<T> = StorageValue<_, u32>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
SalaryPaid(T::AccountId, u128),
EmployeeAdded(T::AccountId, u128),
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// 添加新员工
#[pallet::weight(10_000)]
pub fn add_employee(origin: OriginFor<T>, employee: T::AccountId, salary: u128) -> DispatchResult {
ensure_root(origin)?;
// 实际实现中这里会调用pallet-salary的add_employee
Self::deposit_event(Event::EmployeeAdded(employee, salary));
Ok(())
}
/// 发放薪资
#[pallet::weight(10_000)]
pub fn pay_salary(origin: OriginFor<T>, employee: T::AccountId) -> DispatchResult {
ensure_root(origin)?;
// 实际实现中这里会调用pallet-salary的pay_salary
Self::deposit_event(Event::SalaryPaid(employee, 5000)); // 示例固定金额
Ok(())
}
}
}
// 运行时配置
pub struct Runtime;
impl frame_system::Config for Runtime {
type AccountId = u64;
type RuntimeCall = ();
type RuntimeEvent = ();
type RuntimeOrigin = ();
type BlockNumber = u32;
}
impl pallet::Config for Runtime {
type RuntimeEvent = ();
type Currency = ();
}
最佳实践
- 定期检查薪资预算,避免资金不足
- 为敏感操作设置多重签名
- 利用区块链事件系统监听薪资支付事件
- 结合pallet-treasury实现更复杂的资金管理
注意事项
- 确保支付账户有足够余额
- 薪资周期设置应考虑区块链出块时间
- 大规模员工系统可能需要优化存储结构
pallet-salary为区块链组织提供了透明、不可篡改的薪资管理解决方案,特别适合DAO、开源项目和其他去中心化组织的财务管理需求。