Expand description
Made with Substrate, for Polkadot.
§Multi Asset Bounties Pallet ( pallet-multi-asset-bounties )
§Bounty
A bounty is a reward for completing a specified body of work or achieving a defined set of objectives. The work must be completed for a predefined amount to be paid out. A curator is assigned when the bounty is funded, and is responsible for awarding the bounty once the objectives are met. To support parallel execution and better governance, a bounty can be split into multiple child bounties. Each child bounty represents a smaller task derived from the parent bounty. The parent bounty curator may assign a separate curator to each child bounty at creation time. The curator may be unassigned, resulting in a new curator election. A bounty may be cancelled at any time—unless a payment has already been attempted and is awaiting status confirmation.
NOTE: A parent bounty cannot be closed if it has any active child bounties associated with it.
§Terminology
- Bounty: A reward for a predefined body of work upon completion. A bounty defines the total reward and can be subdivided into multiple child bounties. When referenced in the context of child bounties, it is referred to as parent bounty.
- Curator: An account managing the bounty and assigning a payout address.
- Child Bounty: A subtask or milestone funded by a parent bounty. It may carry its own curator, and reward similar to the parent bounty.
- Curator deposit: The payment in native asset from a candidate willing to curate a funded bounty. The deposit is returned when/if the bounty is completed.
- Bounty value: The total amount in a given asset kind that should be paid to the Beneficiary if the bounty is rewarded.
- Beneficiary: The account/location to which the total or part of the bounty is assigned to.
§Example
- Fund a bounty approved by spend origin of some asset kind with a proposed curator.
#[test]
fn fund_bounty_works() {
ExtBuilder::default().build_and_execute(|| {
// Given
let asset_kind = 1;
let value = 50;
let curator = 4;
let metadata = note_preimage(1);
let _ = Balances::mint_into(&curator, Balances::minimum_balance());
// When
assert_ok!(Bounties::fund_bounty(
RuntimeOrigin::root(),
Box::new(asset_kind),
value,
curator,
metadata
));
// Then
let parent_bounty_id = 0;
let bounty_account =
Bounties::bounty_account(parent_bounty_id, asset_kind).expect("conversion failed");
let payment_id = get_payment_id(parent_bounty_id, None).expect("no payment attempt");
assert_eq!(paid(bounty_account, asset_kind), value);
expect_events(vec![
BountiesEvent::Paid { index: parent_bounty_id, child_index: None, payment_id },
BountiesEvent::BountyCreated { index: parent_bounty_id },
]);
assert_eq!(
pallet_bounties::Bounties::<Test>::get(parent_bounty_id).unwrap(),
Bounty {
asset_kind,
value,
metadata,
status: BountyStatus::FundingAttempted {
curator,
payment_status: PaymentState::Attempted { id: payment_id }
},
}
);
assert_eq!(pallet_bounties::BountyCount::<Test>::get(), 1);
assert!(Preimage::is_requested(&metadata));
assert_eq!(
pallet_bounties::CuratorDeposit::<Test>::get(parent_bounty_id, None::<BountyIndex>),
None
);
});
}- Award a bounty to a beneficiary.
#[test]
fn award_bounty_works() {
ExtBuilder::default().build_and_execute(|| {
// Given: parent bounty status `Active`
let s = create_active_parent_bounty();
// When
assert_ok!(Bounties::award_bounty(
RuntimeOrigin::signed(s.curator),
s.parent_bounty_id,
None,
s.beneficiary
));
// Then
let payment_id = get_payment_id(s.parent_bounty_id, None).expect("no payment attempt");
assert_eq!(
pallet_bounties::Bounties::<Test>::get(s.parent_bounty_id).unwrap(),
Bounty {
asset_kind: s.asset_kind,
value: s.value,
metadata: s.metadata,
status: BountyStatus::PayoutAttempted {
curator: s.curator,
beneficiary: s.beneficiary,
payment_status: PaymentState::Attempted { id: payment_id }
},
}
);
assert_eq!(
pallet_bounties::CuratorDeposit::<Test>::get(s.parent_bounty_id, None::<BountyIndex>)
.unwrap(),
consideration(s.value)
);
expect_events(vec![
BountiesEvent::Paid { index: s.parent_bounty_id, child_index: None, payment_id },
BountiesEvent::BountyAwarded {
index: s.parent_bounty_id,
child_index: None,
beneficiary: s.beneficiary,
},
]);
// Given: child-bounty status `Active`
let s = create_active_child_bounty();
// When
assert_ok!(Bounties::award_bounty(
RuntimeOrigin::signed(s.child_curator),
s.parent_bounty_id,
Some(s.child_bounty_id),
s.child_beneficiary
));
// Then
let payment_id = get_payment_id(s.parent_bounty_id, Some(s.child_bounty_id))
.expect("no payment attempt");
assert_eq!(
pallet_bounties::ChildBounties::<Test>::get(s.parent_bounty_id, s.child_bounty_id)
.unwrap(),
ChildBounty {
parent_bounty: s.parent_bounty_id,
value: s.child_value,
metadata: s.metadata,
status: BountyStatus::PayoutAttempted {
curator: s.child_curator,
beneficiary: s.child_beneficiary,
payment_status: PaymentState::Attempted { id: payment_id }
},
}
);
assert_eq!(
pallet_bounties::CuratorDeposit::<Test>::get(
s.parent_bounty_id,
Some(s.child_bounty_id)
)
.unwrap(),
consideration(s.child_value)
);
expect_events(vec![
BountiesEvent::Paid {
index: s.parent_bounty_id,
child_index: Some(s.child_bounty_id),
payment_id,
},
BountiesEvent::BountyAwarded {
index: s.parent_bounty_id,
child_index: Some(s.child_bounty_id),
beneficiary: s.child_beneficiary,
},
]);
});
}§Pallet API
See the pallet module for more information about the interfaces this pallet exposes,
including its configuration trait, dispatchables, storage items, events and errors.
Re-exports§
pub use weights::WeightInfo;pub use pallet::*;
Modules§
- pallet
- The
palletmodule in each FRAME pallet hosts the most important items needed to construct this pallet. - weights
- Autogenerated weights for
pallet_multi_asset_bounties
Structs§
- Bounty
- A funded bounty.
- Bounty
Source From Pallet Id - Derives a bounty
AccountIdfrom thePalletIdand theBountyIndex, then converts it into the corresponding bountyBeneficiary. - Child
Bounty - A funded child-bounty.
- Child
Bounty Source From Pallet Id - Derives a child-bounty
AccountIdfrom thePalletId, the parent index, and the child index, then converts it into the child-bountyBeneficiary. - Curator
Deposit Amount - Type implementing curator deposit as a percentage of the child-/bounty value.
- Pallet
IdAs Funding Source - Derives the funding
AccountIdfrom thePalletIdand converts it into the bountyBeneficiary, used as the source of bounty funds.
Enums§
- Bounty
Status - The status of a child-/bounty proposal.
- Payment
State - The state of a single payment.
Traits§
- Arguments
Factory - Trait describing factory functions for dispatchables’ parameters.
Type Aliases§
- Account
IdLookup Of - Lookup type for account addresses.
- Beneficiary
Lookup Of - Lookup type for beneficiary addresses.
- Bounty
Index - An index of a bounty. Just a
u32. - Bounty
Of - Convenience alias for
Bounty. - Child
Bounty Of - Convenience alias for
ChildBounty. - Payment
IdOf - The payment identifier type used by the
Config::Paymaster.