referrerpolicy=no-referrer-when-downgrade

Crate pallet_multi_asset_bounties

Source
Expand description

Made with Substrate, for Polkadot.

github - 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

  1. 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
		);
	});
}
  1. 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 pallet module 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.
BountySourceFromPalletId
Derives a bounty AccountId from the PalletId and the BountyIndex, then converts it into the corresponding bounty Beneficiary.
ChildBounty
A funded child-bounty.
ChildBountySourceFromPalletId
Derives a child-bounty AccountId from the PalletId, the parent index, and the child index, then converts it into the child-bounty Beneficiary.
CuratorDepositAmount
Type implementing curator deposit as a percentage of the child-/bounty value.
PalletIdAsFundingSource
Derives the funding AccountId from the PalletId and converts it into the bounty Beneficiary, used as the source of bounty funds.

Enums§

BountyStatus
The status of a child-/bounty proposal.
PaymentState
The state of a single payment.

Traits§

ArgumentsFactory
Trait describing factory functions for dispatchables’ parameters.

Type Aliases§

AccountIdLookupOf
Lookup type for account addresses.
BeneficiaryLookupOf
Lookup type for beneficiary addresses.
BountyIndex
An index of a bounty. Just a u32.
BountyOf
Convenience alias for Bounty.
ChildBountyOf
Convenience alias for ChildBounty.
PaymentIdOf
The payment identifier type used by the Config::Paymaster.