1#![cfg(feature = "runtime-benchmarks")]
21
22use super::*;
23use crate as pallet_bounties;
24use crate::Pallet as Bounties;
25
26use alloc::{borrow::Cow, vec};
27use frame_benchmarking::{v2::*, BenchmarkError};
28use frame_support::assert_ok;
29use frame_system::RawOrigin;
30use sp_core::crypto::FromEntropy;
31
32pub trait ArgumentsFactory<AssetKind, Beneficiary, Balance> {
34 fn create_asset_kind(seed: u32) -> AssetKind;
36
37 fn create_beneficiary(seed: [u8; 32]) -> Beneficiary;
39}
40
41impl<AssetKind, Beneficiary, Balance> ArgumentsFactory<AssetKind, Beneficiary, Balance> for ()
43where
44 AssetKind: FromEntropy,
45 Beneficiary: FromEntropy,
46{
47 fn create_asset_kind(seed: u32) -> AssetKind {
48 AssetKind::from_entropy(&mut seed.encode().as_slice()).unwrap()
49 }
50
51 fn create_beneficiary(seed: [u8; 32]) -> Beneficiary {
52 Beneficiary::from_entropy(&mut seed.as_slice()).unwrap()
53 }
54}
55
56#[derive(Clone)]
57struct BenchmarkBounty<T: Config<I>, I: 'static> {
58 parent_bounty_id: BountyIndex,
60 child_bounty_id: BountyIndex,
62 curator: T::AccountId,
64 child_curator: T::AccountId,
66 asset_kind: T::AssetKind,
68 value: T::Balance,
70 child_value: T::Balance,
72 beneficiary: T::Beneficiary,
74 metadata: T::Hash,
76}
77
78const SEED: u32 = 0;
79
80fn assert_last_event<T: Config<I>, I: 'static>(
81 generic_event: <T as frame_system::Config>::RuntimeEvent,
82) {
83 frame_system::Pallet::<T>::assert_last_event(generic_event.into());
84}
85
86fn assert_has_event<T: Config<I>, I: 'static>(
87 generic_event: <T as frame_system::Config>::RuntimeEvent,
88) {
89 frame_system::Pallet::<T>::assert_has_event(generic_event.into());
90}
91
92pub fn get_payment_id<T: Config<I>, I: 'static>(
93 parent_bounty_id: BountyIndex,
94 child_bounty_id: Option<BountyIndex>,
95) -> Option<PaymentIdOf<T, I>> {
96 let bounty = Bounties::<T, I>::get_bounty_details(parent_bounty_id, child_bounty_id)
97 .expect("no bounty found");
98
99 match bounty.3 {
100 BountyStatus::FundingAttempted {
101 payment_status: PaymentState::Attempted { id }, ..
102 } => Some(id),
103 BountyStatus::RefundAttempted {
104 payment_status: PaymentState::Attempted { id }, ..
105 } => Some(id),
106 BountyStatus::PayoutAttempted {
107 payment_status: PaymentState::Attempted { id }, ..
108 } => Some(id),
109 _ => None,
110 }
111}
112
113fn setup_bounty<T: Config<I>, I: 'static>() -> Result<BenchmarkBounty<T, I>, BenchmarkError> {
115 let asset_kind = <T as Config<I>>::BenchmarkHelper::create_asset_kind(SEED);
116 let min_native_value = T::BountyValueMinimum::get();
117 T::BalanceConverter::ensure_successful(asset_kind.clone());
118 let value = T::BalanceConverter::to_asset_balance(min_native_value, asset_kind.clone())
119 .map_err(|_| BenchmarkError::Stop("Failed to convert balance"))?;
120 let child_value = value / 2u32.into(); let curator = account("curator", 0, SEED);
122 let child_curator = account("child-curator", 1, SEED);
123 let beneficiary =
124 <T as Config<I>>::BenchmarkHelper::create_beneficiary([(SEED).try_into().unwrap(); 32]);
125 let metadata = T::Preimages::note(Cow::from(vec![5, 6])).unwrap();
126
127 Ok(BenchmarkBounty::<T, I> {
128 parent_bounty_id: 0,
129 child_bounty_id: 0,
130 curator,
131 child_curator,
132 asset_kind,
133 value,
134 child_value,
135 beneficiary,
136 metadata,
137 })
138}
139
140fn create_parent_bounty<T: Config<I>, I: 'static>() -> Result<BenchmarkBounty<T, I>, BenchmarkError>
141{
142 let mut s = setup_bounty::<T, I>()?;
143
144 let spend_origin = T::SpendOrigin::try_successful_origin().map_err(|_| {
145 BenchmarkError::Stop("SpendOrigin has no successful origin required for the benchmark")
146 })?;
147 let funding_source_account =
148 Bounties::<T, I>::funding_source_account(s.asset_kind.clone()).expect("conversion failed");
149 let parent_bounty_account =
150 Bounties::<T, I>::bounty_account(s.parent_bounty_id, s.asset_kind.clone())
151 .expect("conversion failed");
152 let curator_lookup = T::Lookup::unlookup(s.curator.clone());
153 <T as pallet_bounties::Config<I>>::Paymaster::ensure_successful(
154 &funding_source_account,
155 &parent_bounty_account,
156 s.asset_kind.clone(),
157 s.value,
158 );
159
160 Bounties::<T, I>::fund_bounty(
161 spend_origin,
162 Box::new(s.asset_kind.clone()),
163 s.value,
164 curator_lookup,
165 s.metadata,
166 )?;
167
168 s.parent_bounty_id = pallet_bounties::BountyCount::<T, I>::get() - 1;
169
170 Ok(s)
171}
172
173fn create_funded_bounty<T: Config<I>, I: 'static>() -> Result<BenchmarkBounty<T, I>, BenchmarkError>
174{
175 let s = create_parent_bounty::<T, I>()?;
176
177 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, None).expect("no payment attempt");
178 <T as pallet::Config<I>>::Paymaster::ensure_concluded(payment_id);
179
180 let caller = account("caller", 0, SEED);
181 Bounties::<T, I>::check_status(RawOrigin::Signed(caller).into(), s.parent_bounty_id, None)?;
182
183 Ok(s)
184}
185
186fn create_active_parent_bounty<T: Config<I>, I: 'static>(
187) -> Result<BenchmarkBounty<T, I>, BenchmarkError> {
188 let s = create_funded_bounty::<T, I>()?;
189 let curator = s.curator.clone();
190 <T as pallet_bounties::Config<I>>::Consideration::ensure_successful(&curator, s.value);
191
192 Bounties::<T, I>::accept_curator(RawOrigin::Signed(curator).into(), s.parent_bounty_id, None)?;
193
194 Ok(s)
195}
196
197fn create_child_bounty<T: Config<I>, I: 'static>() -> Result<BenchmarkBounty<T, I>, BenchmarkError>
198{
199 let mut s = create_active_parent_bounty::<T, I>()?;
200 let child_curator_lookup = T::Lookup::unlookup(s.child_curator.clone());
201
202 Bounties::<T, I>::fund_child_bounty(
203 RawOrigin::Signed(s.curator.clone()).into(),
204 s.parent_bounty_id,
205 s.child_value,
206 s.metadata,
207 Some(child_curator_lookup),
208 )?;
209 s.child_bounty_id =
210 pallet_bounties::TotalChildBountiesPerParent::<T, I>::get(s.parent_bounty_id) - 1;
211
212 Ok(s)
213}
214
215fn create_funded_child_bounty<T: Config<I>, I: 'static>(
216) -> Result<BenchmarkBounty<T, I>, BenchmarkError> {
217 let s = create_child_bounty::<T, I>()?;
218 let caller = account("caller", 0, SEED);
219
220 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
221 .expect("no payment attempt");
222 <T as pallet::Config<I>>::Paymaster::ensure_concluded(payment_id);
223 Bounties::<T, I>::check_status(
224 RawOrigin::Signed(caller).into(),
225 s.parent_bounty_id,
226 Some(s.child_bounty_id),
227 )?;
228
229 Ok(s)
230}
231
232fn create_active_child_bounty<T: Config<I>, I: 'static>(
233) -> Result<BenchmarkBounty<T, I>, BenchmarkError> {
234 let s = create_funded_child_bounty::<T, I>()?;
235 let caller = s.child_curator.clone();
236 <T as pallet_bounties::Config<I>>::Consideration::ensure_successful(&caller, s.child_value);
237
238 Bounties::<T, I>::accept_curator(
239 RawOrigin::Signed(caller).into(),
240 s.parent_bounty_id,
241 Some(s.child_bounty_id),
242 )?;
243
244 Ok(s)
245}
246
247fn create_awarded_child_bounty<T: Config<I>, I: 'static>(
248) -> Result<BenchmarkBounty<T, I>, BenchmarkError> {
249 let s = create_active_child_bounty::<T, I>()?;
250 let caller = s.child_curator.clone();
251 let beneficiary_lookup = T::BeneficiaryLookup::unlookup(s.beneficiary.clone());
252
253 Bounties::<T, I>::award_bounty(
254 RawOrigin::Signed(caller).into(),
255 s.parent_bounty_id,
256 Some(s.child_bounty_id),
257 beneficiary_lookup,
258 )?;
259
260 Ok(s)
261}
262
263pub fn set_status<T: Config<I>, I: 'static>(
264 parent_bounty_id: BountyIndex,
265 child_bounty_id: Option<BountyIndex>,
266 new_payment_status: PaymentState<PaymentIdOf<T, I>>,
267) -> Result<(), BenchmarkError> {
268 let bounty =
269 pallet_bounties::Pallet::<T, I>::get_bounty_details(parent_bounty_id, child_bounty_id)
270 .expect("no bounty");
271
272 let new_status = match bounty.3 {
273 BountyStatus::FundingAttempted { curator, .. } =>
274 BountyStatus::FundingAttempted { payment_status: new_payment_status, curator },
275 BountyStatus::RefundAttempted { curator, .. } =>
276 BountyStatus::RefundAttempted { payment_status: new_payment_status, curator },
277 BountyStatus::PayoutAttempted { curator, beneficiary, .. } =>
278 BountyStatus::PayoutAttempted {
279 payment_status: new_payment_status,
280 curator,
281 beneficiary,
282 },
283 _ => return Err(BenchmarkError::Stop("unexpected bounty status")),
284 };
285
286 let _ = pallet_bounties::Pallet::<T, I>::update_bounty_status(
287 parent_bounty_id,
288 child_bounty_id,
289 new_status,
290 );
291
292 Ok(())
293}
294
295#[instance_benchmarks]
296mod benchmarks {
297 use super::*;
298
299 #[benchmark]
302 fn fund_bounty() -> Result<(), BenchmarkError> {
303 let s = setup_bounty::<T, I>()?;
304
305 let spend_origin =
306 T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
307 let curator_lookup = T::Lookup::unlookup(s.curator.clone());
308 let funding_source_account = Bounties::<T, I>::funding_source_account(s.asset_kind.clone())
309 .expect("conversion failed");
310 let parent_bounty_account =
311 Bounties::<T, I>::bounty_account(s.parent_bounty_id, s.asset_kind.clone())
312 .expect("conversion failed");
313 <T as pallet_bounties::Config<I>>::Paymaster::ensure_successful(
314 &funding_source_account,
315 &parent_bounty_account,
316 s.asset_kind.clone(),
317 s.value,
318 );
319
320 #[extrinsic_call]
321 _(spend_origin, Box::new(s.asset_kind), s.value, curator_lookup, s.metadata);
322
323 let parent_bounty_id = BountyCount::<T, I>::get() - 1;
324 assert_last_event::<T, I>(Event::BountyCreated { index: parent_bounty_id }.into());
325 let payment_id =
326 get_payment_id::<T, I>(parent_bounty_id, None).expect("no payment attempt");
327 assert_has_event::<T, I>(
328 Event::Paid { index: s.parent_bounty_id, child_index: None, payment_id }.into(),
329 );
330 assert_ne!(
331 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
332 PaymentStatus::Failure
333 );
334
335 Ok(())
336 }
337
338 #[benchmark]
339 fn fund_child_bounty() -> Result<(), BenchmarkError> {
340 let s = create_active_parent_bounty::<T, I>()?;
341 let child_curator_lookup = T::Lookup::unlookup(s.child_curator.clone());
342
343 #[extrinsic_call]
344 _(
345 RawOrigin::Signed(s.curator),
346 s.parent_bounty_id,
347 s.child_value,
348 s.metadata,
349 Some(child_curator_lookup),
350 );
351
352 let child_bounty_id =
353 pallet_bounties::TotalChildBountiesPerParent::<T, I>::get(s.parent_bounty_id) - 1;
354 assert_last_event::<T, I>(
355 Event::ChildBountyCreated { index: s.parent_bounty_id, child_index: child_bounty_id }
356 .into(),
357 );
358 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(child_bounty_id))
359 .expect("no payment attempt");
360 assert_has_event::<T, I>(
361 Event::Paid {
362 index: s.parent_bounty_id,
363 child_index: Some(child_bounty_id),
364 payment_id,
365 }
366 .into(),
367 );
368 assert_ne!(
369 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
370 PaymentStatus::Failure
371 );
372
373 Ok(())
374 }
375
376 #[benchmark]
379 fn propose_curator_parent_bounty() -> Result<(), BenchmarkError> {
380 let s = create_funded_bounty::<T, I>()?;
381
382 let spend_origin =
383 T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
384 Bounties::<T, I>::unassign_curator(
385 RawOrigin::Signed(s.curator.clone()).into(),
386 s.parent_bounty_id,
387 None,
388 )?;
389 let curator_lookup = T::Lookup::unlookup(s.curator.clone());
390
391 #[block]
392 {
393 assert_ok!(Bounties::<T, I>::propose_curator(
394 spend_origin,
395 s.parent_bounty_id,
396 None,
397 curator_lookup,
398 ));
399 }
400
401 assert_last_event::<T, I>(
402 Event::CuratorProposed {
403 index: s.parent_bounty_id,
404 child_index: None,
405 curator: s.curator,
406 }
407 .into(),
408 );
409
410 Ok(())
411 }
412
413 #[benchmark]
414 fn propose_curator_child_bounty() -> Result<(), BenchmarkError> {
415 let s = create_funded_child_bounty::<T, I>()?;
416 let child_curator_lookup = T::Lookup::unlookup(s.child_curator.clone());
417
418 Bounties::<T, I>::unassign_curator(
419 RawOrigin::Signed(s.curator.clone()).into(),
420 s.parent_bounty_id,
421 Some(s.child_bounty_id),
422 )?;
423
424 #[block]
425 {
426 assert_ok!(Bounties::<T, I>::propose_curator(
427 RawOrigin::Signed(s.curator).into(),
428 s.parent_bounty_id,
429 Some(s.child_bounty_id),
430 child_curator_lookup,
431 ));
432 }
433
434 assert_last_event::<T, I>(
435 Event::CuratorProposed {
436 index: s.parent_bounty_id,
437 child_index: Some(s.child_bounty_id),
438 curator: s.child_curator,
439 }
440 .into(),
441 );
442
443 Ok(())
444 }
445
446 #[benchmark]
447 fn accept_curator() -> Result<(), BenchmarkError> {
448 let s = create_funded_child_bounty::<T, I>()?;
449 let caller = s.child_curator.clone();
450
451 <T as pallet_bounties::Config<I>>::Consideration::ensure_successful(&caller, s.child_value);
452
453 #[block]
454 {
455 assert_ok!(Bounties::<T, I>::accept_curator(
456 RawOrigin::Signed(caller).into(),
457 s.parent_bounty_id,
458 Some(s.child_bounty_id),
459 ));
460 }
461
462 assert_last_event::<T, I>(
463 Event::BountyBecameActive {
464 index: s.parent_bounty_id,
465 child_index: Some(s.child_bounty_id),
466 curator: s.child_curator,
467 }
468 .into(),
469 );
470
471 Ok(())
472 }
473
474 #[benchmark]
475 fn unassign_curator() -> Result<(), BenchmarkError> {
476 let s = create_active_child_bounty::<T, I>()?;
477
478 #[extrinsic_call]
479 _(RawOrigin::Signed(s.curator), s.parent_bounty_id, Some(s.child_bounty_id));
480
481 assert_last_event::<T, I>(
482 Event::CuratorUnassigned {
483 index: s.parent_bounty_id,
484 child_index: Some(s.child_bounty_id),
485 }
486 .into(),
487 );
488
489 Ok(())
490 }
491
492 #[benchmark]
493 fn award_bounty() -> Result<(), BenchmarkError> {
494 let s = create_active_child_bounty::<T, I>()?;
495 let beneficiary_lookup = T::BeneficiaryLookup::unlookup(s.beneficiary.clone());
496
497 #[extrinsic_call]
498 _(
499 RawOrigin::Signed(s.child_curator),
500 s.parent_bounty_id,
501 Some(s.child_bounty_id),
502 beneficiary_lookup,
503 );
504
505 assert_last_event::<T, I>(
506 Event::BountyAwarded {
507 index: s.parent_bounty_id,
508 child_index: Some(s.child_bounty_id),
509 beneficiary: s.beneficiary,
510 }
511 .into(),
512 );
513
514 Ok(())
515 }
516
517 #[benchmark]
518 fn close_parent_bounty() -> Result<(), BenchmarkError> {
519 let s = create_active_parent_bounty::<T, I>()?;
520
521 let reject_origin =
522 T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
523
524 #[block]
525 {
526 assert_ok!(Bounties::<T, I>::close_bounty(
527 reject_origin.clone(),
528 s.parent_bounty_id,
529 None
530 ));
531 }
532
533 assert_last_event::<T, I>(
534 Event::BountyCanceled { index: s.parent_bounty_id, child_index: None }.into(),
535 );
536 let payment_id =
537 get_payment_id::<T, I>(s.parent_bounty_id, None).expect("no payment attempt");
538 assert_has_event::<T, I>(
539 Event::Paid { index: s.parent_bounty_id, child_index: None, payment_id }.into(),
540 );
541 assert_ne!(
542 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
543 PaymentStatus::Failure
544 );
545 assert!(Bounties::<T, I>::close_bounty(reject_origin, s.parent_bounty_id, None).is_err());
546
547 Ok(())
548 }
549
550 #[benchmark]
551 fn close_child_bounty() -> Result<(), BenchmarkError> {
552 let s = create_active_child_bounty::<T, I>()?;
553
554 let reject_origin =
555 T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
556
557 #[block]
558 {
559 assert_ok!(Bounties::<T, I>::close_bounty(
560 reject_origin.clone(),
561 s.parent_bounty_id,
562 Some(s.child_bounty_id),
563 ));
564 }
565
566 assert_last_event::<T, I>(
567 Event::BountyCanceled {
568 index: s.parent_bounty_id,
569 child_index: Some(s.child_bounty_id),
570 }
571 .into(),
572 );
573 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
574 .expect("no payment attempt");
575 assert_has_event::<T, I>(
576 Event::Paid {
577 index: s.parent_bounty_id,
578 child_index: Some(s.child_bounty_id),
579 payment_id,
580 }
581 .into(),
582 );
583 assert_ne!(
584 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
585 PaymentStatus::Failure
586 );
587 assert!(Bounties::<T, I>::close_bounty(
588 reject_origin,
589 s.parent_bounty_id,
590 Some(s.child_bounty_id)
591 )
592 .is_err());
593
594 Ok(())
595 }
596
597 #[benchmark]
598 fn check_status_funding() -> Result<(), BenchmarkError> {
599 let s = create_child_bounty::<T, I>()?;
600 let caller = s.curator.clone();
601
602 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
603 .expect("no payment attempt");
604 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
605
606 #[block]
607 {
608 assert_ok!(Bounties::<T, I>::check_status(
609 RawOrigin::Signed(caller).into(),
610 s.parent_bounty_id,
611 Some(s.child_bounty_id),
612 ));
613 }
614
615 assert_last_event::<T, I>(
616 Event::BountyFundingProcessed {
617 index: s.parent_bounty_id,
618 child_index: Some(s.child_bounty_id),
619 }
620 .into(),
621 );
622 let child_bounty =
623 pallet_bounties::ChildBounties::<T, I>::get(s.parent_bounty_id, s.child_bounty_id)
624 .expect("no bounty");
625 assert!(matches!(child_bounty.status, BountyStatus::Funded { .. }));
626
627 Ok(())
628 }
629
630 #[benchmark]
631 fn check_status_refund() -> Result<(), BenchmarkError> {
632 let s = create_active_child_bounty::<T, I>()?;
633 let caller = s.curator.clone();
634
635 Bounties::<T, I>::close_bounty(
636 RawOrigin::Signed(caller.clone()).into(),
637 s.parent_bounty_id,
638 Some(s.child_bounty_id),
639 )?;
640 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
641 .expect("no payment attempt");
642 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
643
644 #[block]
645 {
646 assert_ok!(Bounties::<T, I>::check_status(
647 RawOrigin::Signed(caller).into(),
648 s.parent_bounty_id,
649 Some(s.child_bounty_id),
650 ));
651 }
652
653 assert_has_event::<T, I>(
654 Event::BountyRefundProcessed {
655 index: s.parent_bounty_id,
656 child_index: Some(s.child_bounty_id),
657 }
658 .into(),
659 );
660 assert_eq!(
661 pallet_bounties::ChildBounties::<T, I>::get(s.parent_bounty_id, s.child_bounty_id),
662 None
663 );
664
665 Ok(())
666 }
667
668 #[benchmark]
669 fn check_status_payout() -> Result<(), BenchmarkError> {
670 let s = create_awarded_child_bounty::<T, I>()?;
671 let caller = s.child_curator.clone();
672
673 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
674 .expect("no payment attempt");
675 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
676
677 #[block]
678 {
679 assert_ok!(Bounties::<T, I>::check_status(
680 RawOrigin::Signed(caller).into(),
681 s.parent_bounty_id,
682 Some(s.child_bounty_id),
683 ));
684 }
685
686 assert_has_event::<T, I>(
687 Event::BountyPayoutProcessed {
688 index: s.parent_bounty_id,
689 child_index: Some(s.child_bounty_id),
690 asset_kind: s.asset_kind,
691 value: s.child_value,
692 beneficiary: s.beneficiary,
693 }
694 .into(),
695 );
696 assert_eq!(
697 pallet_bounties::ChildBounties::<T, I>::get(s.parent_bounty_id, s.child_bounty_id),
698 None
699 );
700
701 Ok(())
702 }
703
704 #[benchmark]
705 fn retry_payment_funding() -> Result<(), BenchmarkError> {
706 let s = create_child_bounty::<T, I>()?;
707 let caller = s.curator.clone();
708
709 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
710 .expect("no payment attempt");
711 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
712 set_status::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id), PaymentState::Failed)?;
713
714 #[block]
715 {
716 assert_ok!(Bounties::<T, I>::retry_payment(
717 RawOrigin::Signed(caller.clone()).into(),
718 s.parent_bounty_id,
719 Some(s.child_bounty_id),
720 ));
721 }
722
723 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
724 .expect("no payment attempt");
725 assert_last_event::<T, I>(
726 Event::Paid {
727 index: s.parent_bounty_id,
728 child_index: Some(s.child_bounty_id),
729 payment_id,
730 }
731 .into(),
732 );
733 assert_ne!(
734 <T as pallet::Config<I>>::Paymaster::check_payment(payment_id),
735 PaymentStatus::Failure
736 );
737 assert!(Bounties::<T, I>::retry_payment(
738 RawOrigin::Signed(caller).into(),
739 s.parent_bounty_id,
740 Some(s.child_bounty_id),
741 )
742 .is_err());
743
744 Ok(())
745 }
746
747 #[benchmark]
748 fn retry_payment_refund() -> Result<(), BenchmarkError> {
749 let s = create_active_child_bounty::<T, I>()?;
750 let caller = s.curator.clone();
751
752 let new_status = BountyStatus::RefundAttempted {
753 payment_status: PaymentState::Failed,
754 curator: Some(s.child_curator),
755 };
756 let _ = pallet_bounties::Pallet::<T, I>::update_bounty_status(
757 s.parent_bounty_id,
758 Some(s.child_bounty_id),
759 new_status,
760 );
761
762 #[block]
763 {
764 assert_ok!(Bounties::<T, I>::retry_payment(
765 RawOrigin::Signed(caller.clone()).into(),
766 s.parent_bounty_id,
767 Some(s.child_bounty_id),
768 ));
769 }
770
771 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
772 .expect("no payment attempt");
773 assert_last_event::<T, I>(
774 Event::Paid {
775 index: s.parent_bounty_id,
776 child_index: Some(s.child_bounty_id),
777 payment_id,
778 }
779 .into(),
780 );
781 assert_ne!(
782 <T as pallet::Config<I>>::Paymaster::check_payment(payment_id),
783 PaymentStatus::Failure
784 );
785 assert!(Bounties::<T, I>::retry_payment(
786 RawOrigin::Signed(caller).into(),
787 s.parent_bounty_id,
788 Some(s.child_bounty_id),
789 )
790 .is_err());
791
792 Ok(())
793 }
794
795 #[benchmark]
796 fn retry_payment_payout() -> Result<(), BenchmarkError> {
797 let s = create_active_child_bounty::<T, I>()?;
798 let caller = s.curator.clone();
799
800 let new_status = BountyStatus::PayoutAttempted {
801 payment_status: PaymentState::Failed,
802 curator: s.child_curator.clone(),
803 beneficiary: s.beneficiary.clone(),
804 };
805 let _ = pallet_bounties::Pallet::<T, I>::update_bounty_status(
806 s.parent_bounty_id,
807 Some(s.child_bounty_id),
808 new_status,
809 );
810
811 #[block]
812 {
813 assert_ok!(Bounties::<T, I>::retry_payment(
814 RawOrigin::Signed(caller.clone()).into(),
815 s.parent_bounty_id,
816 Some(s.child_bounty_id),
817 ));
818 }
819
820 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
821 .expect("no payment attempt");
822 assert_last_event::<T, I>(
823 Event::Paid {
824 index: s.parent_bounty_id,
825 child_index: Some(s.child_bounty_id),
826 payment_id,
827 }
828 .into(),
829 );
830 assert_ne!(
831 <T as pallet::Config<I>>::Paymaster::check_payment(payment_id),
832 PaymentStatus::Failure
833 );
834 assert!(Bounties::<T, I>::retry_payment(
835 RawOrigin::Signed(caller).into(),
836 s.parent_bounty_id,
837 Some(s.child_bounty_id),
838 )
839 .is_err());
840
841 Ok(())
842 }
843
844 impl_benchmark_test_suite! {
845 Pallet,
846 crate::mock::ExtBuilder::default().build(),
847 crate::mock::Test
848 }
849}