Rust区块链开发利器pallet-bounties:Substrate赏金模块实现去中心化激励与任务管理
以下是一个基于pallet-bounties的完整示例实现,包含赏金模块的主要功能:
// 完整赏金模块实现
use frame_support::{
decl_error, decl_event, decl_module, decl_storage, dispatch, ensure,
traits::{Currency, ExistenceRequirement, Get, ReservableCurrency},
weights::Weight,
};
use frame_system::{self as system, ensure_root, ensure_signed};
use sp_runtime::{
traits::{AccountIdConversion, StaticLookup, Zero},
DispatchResult, RuntimeDebug,
};
use sp_std::prelude::*;
// 配置特征
pub trait Trait: system::Trait {
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
type Currency: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
type BountyDepositBase: Get<BalanceOf<Self>>;
type BountyDepositPerByte: Get<BalanceOf<Self>>;
type BountyUpdatePeriod: Get<Self::BlockNumber>;
type BountyCuratorDeposit: Get<BalanceOf<Self>>;
type BountyValueMinimum: Get<BalanceOf<Self>>;
type BountyFee: Get<BalanceOf<Self>>;
}
type BalanceOf<T> =
<<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
type AccountIdLookupOf<T> = <<T as system::Trait>::Lookup as StaticLookup>::Source;
// 存储定义
decl_storage! {
trait Store for Module<T: Trait> as Bounties {
/// 赏金计数器
pub BountyCount get(fn bounty_count): u32;
/// 赏金信息存储
pub Bounties get(fn bounties):
map hasher(blake2_128_concat) u32 => Option<Bounty<T::AccountId, BalanceOf<T>, T::BlockNumber>>;
/// 赏金描述存储
pub BountyDescriptions get(fn bounty_descriptions):
map hasher(blake2_128_concat) u32 => Option<Vec<u8>>;
/// 子赏金计数器
pub ChildBountyCount get(fn child_bounty_count): u32;
}
}
// 事件定义
decl_event!(
pub enum Event<T> where
AccountId = <T as system::Trait>::AccountId,
Balance = BalanceOf<T>,
BlockNumber = <T as system::Trait>::BlockNumber,
{
/// 新赏金提案
BountyProposed(u32),
/// 赏金批准
BountyApproved(u32),
/// 策展人提议
CuratorProposed(u32, AccountId),
/// 策展人接受
CuratorAccepted(u32, AccountId),
/// 赏金奖励
BountyAwarded(u32, AccountId),
/// 赏金领取
BountyClaimed(u32, Balance),
/// 赏金取消
BountyCanceled(u32),
/// 赏金延长
BountyExtended(u32, BlockNumber),
/// 子赏金添加
ChildBountyAdded(u32, u32),
}
);
// 错误定义
decl_error! {
pub enum Error for Module<T: Trait> {
/// 赏金不存在
BountyNotFound,
/// 赏金状态不正确
InvalidBountyStatus,
/// 赏金金额不足
InsufficientBountyValue,
/// 描述过长
DescriptionTooLong,
/// 策展人未指定
CuratorNotProposed,
/// 策展人押金不足
InsufficientCuratorDeposit,
}
}
// 赏金状态枚举
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub enum BountyStatus<AccountId, Balance, BlockNumber> {
/// 已提案
Proposed,
/// 已批准
Approved,
/// 策展人已提议
CuratorProposed(AccountId),
/// 活跃中(已分配策展人)
Active {
curator: AccountId,
update_due: BlockNumber,
},
/// 待支付
PendingPayout {
curator: AccountId,
beneficiary: AccountId,
unlock_at: BlockNumber,
},
}
// 赏金结构体
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)]
pub struct Bounty<AccountId, Balance, BlockNumber> {
/// 提案人账户
pub proposer: AccountId,
/// 赏金价值
pub value: Balance,
/// 策展人费用
pub fee: Balance,
/// 策展人押金
pub curator_deposit: Balance,
/// 提案人押金
pub bond: Balance,
/// 赏金状态
pub status: BountyStatus<AccountId, Balance, BlockNumber>,
}
// 模块实现
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
type Error = Error<T>;
fn deposit_event() = default;
/// 提出新的赏金
#[weight = 10_000]
pub fn propose_bounty(origin, description: Vec<u8>, value: BalanceOf<T>) -> dispatch::DispatchResult {
let proposer = ensure_signed(origin)?;
// 计算所需押金
let deposit = T::BountyDepositBase::get()
+ (T::BountyDepositPerByte::get() * (description.len() as u32).into());
// 验证描述长度
ensure!(description.len() <= 16384, Error::<T>::DescriptionTooLong);
// 验证赏金金额
ensure!(value >= T::BountyValueMinimum::get(), Error::<T>::InsufficientBountyValue);
// 锁定提案人资金
T::Currency::reserve(&proposer, deposit)?;
// 获取新赏金ID
let bounty_id = Self::bounty_count();
BountyCount::put(bounty_id + 1);
// 存储赏金信息
let bounty = Bounty {
proposer: proposer.clone(),
value,
fee: T::BountyFee::get(),
curator_deposit: Zero::zero(),
bond: deposit,
status: BountyStatus::Proposed,
};
<Bounties<T>>::insert(bounty_id, bounty);
<BountyDescriptions<T>>::insert(bounty_id, description);
// 发出事件
Self::deposit_event(RawEvent::BountyProposed(bounty_id));
Ok(())
}
/// 批准赏金
#[weight = 10_000]
pub fn approve_bounty(origin, bounty_id: u32) -> dispatch::DispatchResult {
ensure_root(origin)?;
let mut bounty = Self::bounties(bounty_id).ok_or(Error::<T>::BountyNotFound)?;
ensure!(bounty.status == BountyStatus::Proposed, Error::<T>::InvalidBountyStatus);
bounty.status = BountyStatus::Approved;
<Bounties<T>>::insert(bounty_id, bounty);
Self::deposit_event(RawEvent::BountyApproved(bounty_id));
Ok(())
}
/// 提议策展人
#[weight = 10_000]
pub fn propose_curator(origin, bounty_id: u32, curator: AccountIdLookupOf<T>) -> dispatch::DispatchResult {
ensure_root(origin)?;
let mut bounty = Self::bounties(bounty_id).ok_or(Error::<T>::BountyNotFound)?;
ensure!(bounty.status == BountyStatus::Approved, Error::<T>::InvalidBountyStatus);
let curator = T::Lookup::lookup(curator)?;
bounty.status = BountyStatus::CuratorProposed(curator.clone());
<Bounties<T>>::insert(bounty_id, bounty);
Self::deposit_event(RawEvent::CuratorProposed(bounty_id, curator));
Ok(())
}
/// 接受策展人角色
#[weight = 10_000]
pub fn accept_curator(origin, bounty_id: u32) -> dispatch::DispatchResult {
let signer = ensure_signed(origin)?;
let mut bounty = Self::bounties(bounty_id).ok_or(Error::<T>::BountyNotFound)?;
let curator = match &bounty.status {
BountyStatus::CuratorProposed(curator) if curator == &signer => curator.clone(),
_ => Err(Error::<T>::CuratorNotProposed)?,
};
let deposit = T::BountyCuratorDeposit::get();
T::Currency::reserve(&curator, deposit)?;
bounty.curator_deposit = deposit;
bounty.status = BountyStatus::Active {
curator: curator.clone(),
update_due: <system::Module<T>>::block_number() + T::BountyUpdatePeriod::get(),
};
<Bounties<T>>::insert(bounty_id, bounty);
Self::deposit_event(RawEvent::CuratorAccepted(bounty_id, curator));
Ok(())
}
/// 奖励赏金
#[weight = 10_000]
pub fn award_bounty(origin, bounty_id: u32, beneficiary: AccountIdLookupOf<T>) -> dispatch::DispatchResult {
let signer = ensure_signed(origin)?;
let mut bounty = Self::bounties(bounty_id).ok_or(Error::<T>::BountyNotFound)?;
let curator = match &bounty.status {
BountyStatus::Active { curator, .. } if curator == &signer => curator.clone(),
_ => Err(Error::<T>::InvalidBountyStatus)?,
};
let beneficiary = T::Lookup::lookup(beneficiary)?;
let unlock_at = <system::Module<T>>::block_number() + T::BountyUpdatePeriod::get();
bounty.status = BountyStatus::PendingPayout {
curator: curator.clone(),
beneficiary: beneficiary.clone(),
unlock_at,
};
<Bounties<T>>::insert(bounty_id, bounty);
Self::deposit_event(RawEvent::BountyAwarded(bounty_id, beneficiary));
Ok(())
}
/// 领取赏金
#[weight = 10_000]
pub fn claim_bounty(origin, bounty_id: u32) -> dispatch::DispatchResult {
let _ = ensure_signed(origin)?;
let bounty = Self::bounties(bounty_id).ok_or(Error::<T>::BountyNotFound)?;
let (curator, beneficiary, unlock_at, value, fee, curator_deposit) = match &bounty.status {
BountyStatus::PendingPayout { curator, beneficiary, unlock_at } => {
ensure!(
<system::Module<T>>::block_number() >= *unlock_at,
Error::<T>::InvalidBountyStatus
);
(curator.clone(), beneficiary.clone(), *unlock_at, bounty.value, bounty.fee, bounty.curator_deposit)
},
_ => Err(Error::<T>::InvalidBountyStatus)?,
};
// 发放赏金
T::Currency::transfer(&Self::account_id(), &beneficiary, value - fee, ExistenceRequirement::KeepAlive)?;
// 发放策展人费用并返还押金
T::Currency::transfer(&Self::account_id(), &curator, fee + curator_deposit, ExistenceRequirement::KeepAlive)?;
// 返还提案人押金
T::Currency::unreserve(&bounty.proposer, bounty.bond);
// 清除赏金数据
<Bounties<T>>::remove(bounty_id);
<BountyDescriptions<T>>::remove(bounty_id);
Self::deposit_event(RawEvent::BountyClaimed(bounty_id, value));
Ok(())
}
/// 关闭赏金
#[weight = 10_000]
pub fn close_bounty(origin, bounty_id: u32) -> dispatch::DispatchResult {
ensure_root(origin)?;
let bounty = Self::bounties(bounty_id).ok_or(Error::<T>::BountyNotFound)?;
match &bounty.status {
BountyStatus::Proposed | BountyStatus::Approved | BountyStatus::CuratorProposed(_) => {},
_ => Err(Error::<T>::InvalidBountyStatus)?,
};
// 返还提案人押金
T::Currency::unreserve(&bounty.proposer, bounty.bond);
// 清除赏金数据
<Bounties<T>>::remove(bounty_id);
<BountyDescriptions<T>>::remove(bounty_id);
Self::deposit_event(RawEvent::BountyCanceled(bounty_id));
Ok(())
}
/// 延长赏金有效期
#[weight = 10_000]
pub fn extend_bounty_expiry(origin, bounty_id: u32) -> dispatch::DispatchResult {
let signer = ensure_signed(origin)?;
let mut bounty = Self::bounties(bounty_id).ok_or(Error::<T>::BountyNotFound)?;
match &mut bounty.status {
BountyStatus::Active { curator, update_due } => {
ensure!(curator == &signer, Error::<T>::InvalidBountyStatus);
*update_due = <system::Module<T>>::block_number() + T::BountyUpdatePeriod::get();
},
_ => Err(Error::<T>::InvalidBountyStatus)?,
};
<Bounties<T>>::insert(bounty_id, bounty);
Self::deposit_event(RawEvent::BountyExtended(
bounty_id,
<system::Module<T>>::block_number() + T::BountyUpdatePeriod::get(),
));
Ok(())
}
}
}
// 模块账户ID实现
impl<T: Trait> Module<T> {
/// 获取模块账户ID
pub fn account_id() -> T::AccountId {
T::ModuleId::get().into_account()
}
}
// 运行时配置示例
/*
impl pallet_bounties::Trait for Runtime {
type Event = Event;
type Currency = Balances;
type BountyDepositBase = BountyDepositBase;
type BountyDepositPerByte = BountyDepositPerByte;
type BountyUpdatePeriod = BountyUpdatePeriod;
type BountyCuratorDeposit = BountyCuratorDeposit;
type BountyValueMinimum = BountyValueMinimum;
type BountyFee = BountyFee;
}
*/
这个完整实现包含了pallet-bounties的主要功能:
-
赏金生命周期管理:
propose_bounty
- 创建新赏金提案approve_bounty
- 批准赏金propose_curator
- 提议策展人accept_curator
- 接受策展人角色award_bounty
- 奖励完成的工作claim_bounty
- 领取赏金close_bounty
- 关闭赏金extend_bounty_expiry
- 延长赏金有效期
-
资金管理:
- 处理提案人押金
- 处理策展人押金和费用
- 延迟支付机制
- 资金发放和返还
-
状态管理:
- 跟踪赏金从提案到支付的完整生命周期
- 处理各种状态转换
要使用此模块,需要在运行时配置中设置适当的参数并集成模块。注释中提供了运行时配置的示例。
1 回复
Rust区块链开发利器pallet-bounties:Substrate赏金模块实现去中心化激励与任务管理
介绍
pallet-bounties
是Substrate框架中的一个内置模块,专门设计用于在区块链上实现去中心化的赏金系统。它允许网络参与者创建资金池,为特定任务或问题解决方案提供经济激励,是构建DAO、开源项目协作平台或去中心化任务管理系统的理想工具。
该模块是Polkadot和Kusama等基于Substrate的区块链的核心功能之一,实现了链上赏金的全生命周期管理。
主要功能
- 赏金创建:存入资金并定义任务要求
- 赏金申请:参与者可以申请解决任务
- 赏金审批:通过民主机制或指定管理者审批
- 赏金支付:任务完成后发放奖励
- 赏金取消:在特定条件下取消并返还资金
完整示例代码
以下是基于pallet-bounties
的完整使用示例,展示从创建到领取赏金的完整流程:
// 1. 在runtime中配置bounties模块
use frame_support::parameter_types;
use frame_system::EnsureRoot;
parameter_types! {
pub const BountyDepositBase: u128 = 1 * DOLLARS;
pub const BountyDepositPayoutDelay: u64 = 24 * HOURS;
pub const BountyUpdatePeriod: u64 = 30 * DAYS;
pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);
pub const BountyValueMinimum: u128 = 5 * DOLLARS;
pub const MaximumReasonLength: u32 = 300;
}
impl pallet_bounties::Config for Runtime {
type Event = Event;
type BountyDepositBase = BountyDepositBase;
type BountyDepositPayoutDelay = BountyDepositPayoutDelay;
type BountyUpdatePeriod = BountyUpdatePeriod;
type BountyCuratorDeposit = BountyCuratorDeposit;
type BountyValueMinimum = BountyValueMinimum;
type DataDepositPerByte = ();
type MaximumReasonLength = MaximumReasonLength;
type WeightInfo = ();
type BountyApproveOrigin = EnsureRoot<AccountId>;
type BountyCuratorOrigin = EnsureRoot<AccountId>;
}
// 2. 创建赏金提案
fn create_bounty() -> DispatchResult {
// 账户1创建赏金,金额500,描述"Fix critical bug"
Bounties::propose_bounty(
RuntimeOrigin::signed(1),
500,
b"Fix critical bug in runtime".to_vec()
)
}
// 3. 批准赏金提案
fn approve_bounty(bounty_id: u32) -> DispatchResult {
// 根账户批准ID为bounty_id的赏金
Bounties::approve_bounty(
RuntimeOrigin::root(),
bounty_id
)
}
// 4. 指定赏金管理者
fn assign_curator(bounty_id: u32, curator: AccountId, deposit: u128) -> DispatchResult {
// 根账户指定管理者
Bounties::propose_curator(
RuntimeOrigin::root(),
bounty_id,
curator,
deposit
)
}
// 5. 管理者接受角色
fn accept_curatorship(bounty_id: u32, curator: AccountId) -> DispatchResult {
// 管理者账户接受角色
Bounties::accept_curator(
RuntimeOrigin::signed(curator),
bounty_id
)
}
// 6. 奖励赏金给解决方案提供者
fn award_bounty(bounty_id: u32, curator: AccountId, beneficiary: AccountId) -> DispatchResult {
// 管理者账户奖励赏金给受益人
Bounties::award_bounty(
RuntimeOrigin::signed(curator),
bounty_id,
beneficiary
)
}
// 7. 领取赏金
fn claim_bounty(bounty_id: u32, beneficiary: AccountId) -> DispatchResult {
// 受益人账户领取赏金
Bounties::claim_bounty(
RuntimeOrigin::signed(beneficiary),
bounty_id
)
}
// 完整工作流程示例
fn full_bounty_workflow() -> DispatchResult {
// 1. 创建赏金
create_bounty()?;
// 2. 批准赏金(ID为0)
approve_bounty(0)?;
// 3. 指定账户2为管理者,保证金100
assign_curator(0, 2, 100)?;
// 4. 账户2接受管理者角色
accept_curatorship(0, 2)?;
// 5. 管理者奖励赏金给账户3
award_bounty(0, 2, 3)?;
// 6. 账户3领取赏金
claim_bounty(0, 3)
}
最佳实践
- 设置合理的保证金:防止垃圾赏金提案
- 明确赏金描述:减少争议
- 分阶段支付:对于复杂任务考虑拆分赏金
- 利用管理者角色:选择可信的领域专家作为管理者
pallet-bounties
为区块链项目提供了强大的去中心化协作工具,通过合理配置可以构建出适应各种场景的任务激励系统。