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 },
276 BountyStatus::RefundAttempted { curator, .. } => {
277 BountyStatus::RefundAttempted { payment_status: new_payment_status, curator }
278 },
279 BountyStatus::PayoutAttempted { curator, beneficiary, .. } => {
280 BountyStatus::PayoutAttempted {
281 payment_status: new_payment_status,
282 curator,
283 beneficiary,
284 }
285 },
286 _ => return Err(BenchmarkError::Stop("unexpected bounty status")),
287 };
288
289 let _ = pallet_bounties::Pallet::<T, I>::update_bounty_status(
290 parent_bounty_id,
291 child_bounty_id,
292 new_status,
293 );
294
295 Ok(())
296}
297
298#[instance_benchmarks]
299mod benchmarks {
300 use super::*;
301
302 #[benchmark]
305 fn fund_bounty() -> Result<(), BenchmarkError> {
306 let s = setup_bounty::<T, I>()?;
307
308 let spend_origin =
309 T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
310 let curator_lookup = T::Lookup::unlookup(s.curator.clone());
311 let funding_source_account = Bounties::<T, I>::funding_source_account(s.asset_kind.clone())
312 .expect("conversion failed");
313 let parent_bounty_account =
314 Bounties::<T, I>::bounty_account(s.parent_bounty_id, s.asset_kind.clone())
315 .expect("conversion failed");
316 <T as pallet_bounties::Config<I>>::Paymaster::ensure_successful(
317 &funding_source_account,
318 &parent_bounty_account,
319 s.asset_kind.clone(),
320 s.value,
321 );
322
323 #[extrinsic_call]
324 _(spend_origin, Box::new(s.asset_kind), s.value, curator_lookup, s.metadata);
325
326 let parent_bounty_id = BountyCount::<T, I>::get() - 1;
327 assert_last_event::<T, I>(Event::BountyCreated { index: parent_bounty_id }.into());
328 let payment_id =
329 get_payment_id::<T, I>(parent_bounty_id, None).expect("no payment attempt");
330 assert_has_event::<T, I>(
331 Event::Paid { index: s.parent_bounty_id, child_index: None, payment_id }.into(),
332 );
333 assert_ne!(
334 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
335 PaymentStatus::Failure
336 );
337
338 Ok(())
339 }
340
341 #[benchmark]
342 fn fund_child_bounty() -> Result<(), BenchmarkError> {
343 let s = create_active_parent_bounty::<T, I>()?;
344 let child_curator_lookup = T::Lookup::unlookup(s.child_curator.clone());
345
346 #[extrinsic_call]
347 _(
348 RawOrigin::Signed(s.curator),
349 s.parent_bounty_id,
350 s.child_value,
351 s.metadata,
352 Some(child_curator_lookup),
353 );
354
355 let child_bounty_id =
356 pallet_bounties::TotalChildBountiesPerParent::<T, I>::get(s.parent_bounty_id) - 1;
357 assert_last_event::<T, I>(
358 Event::ChildBountyCreated { index: s.parent_bounty_id, child_index: child_bounty_id }
359 .into(),
360 );
361 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(child_bounty_id))
362 .expect("no payment attempt");
363 assert_has_event::<T, I>(
364 Event::Paid {
365 index: s.parent_bounty_id,
366 child_index: Some(child_bounty_id),
367 payment_id,
368 }
369 .into(),
370 );
371 assert_ne!(
372 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
373 PaymentStatus::Failure
374 );
375
376 Ok(())
377 }
378
379 #[benchmark]
382 fn propose_curator_parent_bounty() -> Result<(), BenchmarkError> {
383 let s = create_funded_bounty::<T, I>()?;
384
385 let spend_origin =
386 T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
387 Bounties::<T, I>::unassign_curator(
388 RawOrigin::Signed(s.curator.clone()).into(),
389 s.parent_bounty_id,
390 None,
391 )?;
392 let curator_lookup = T::Lookup::unlookup(s.curator.clone());
393
394 #[block]
395 {
396 assert_ok!(Bounties::<T, I>::propose_curator(
397 spend_origin,
398 s.parent_bounty_id,
399 None,
400 curator_lookup,
401 ));
402 }
403
404 assert_last_event::<T, I>(
405 Event::CuratorProposed {
406 index: s.parent_bounty_id,
407 child_index: None,
408 curator: s.curator,
409 }
410 .into(),
411 );
412
413 Ok(())
414 }
415
416 #[benchmark]
417 fn propose_curator_child_bounty() -> Result<(), BenchmarkError> {
418 let s = create_funded_child_bounty::<T, I>()?;
419 let child_curator_lookup = T::Lookup::unlookup(s.child_curator.clone());
420
421 Bounties::<T, I>::unassign_curator(
422 RawOrigin::Signed(s.curator.clone()).into(),
423 s.parent_bounty_id,
424 Some(s.child_bounty_id),
425 )?;
426
427 #[block]
428 {
429 assert_ok!(Bounties::<T, I>::propose_curator(
430 RawOrigin::Signed(s.curator).into(),
431 s.parent_bounty_id,
432 Some(s.child_bounty_id),
433 child_curator_lookup,
434 ));
435 }
436
437 assert_last_event::<T, I>(
438 Event::CuratorProposed {
439 index: s.parent_bounty_id,
440 child_index: Some(s.child_bounty_id),
441 curator: s.child_curator,
442 }
443 .into(),
444 );
445
446 Ok(())
447 }
448
449 #[benchmark]
450 fn accept_curator() -> Result<(), BenchmarkError> {
451 let s = create_funded_child_bounty::<T, I>()?;
452 let caller = s.child_curator.clone();
453
454 <T as pallet_bounties::Config<I>>::Consideration::ensure_successful(&caller, s.child_value);
455
456 #[block]
457 {
458 assert_ok!(Bounties::<T, I>::accept_curator(
459 RawOrigin::Signed(caller).into(),
460 s.parent_bounty_id,
461 Some(s.child_bounty_id),
462 ));
463 }
464
465 assert_last_event::<T, I>(
466 Event::BountyBecameActive {
467 index: s.parent_bounty_id,
468 child_index: Some(s.child_bounty_id),
469 curator: s.child_curator,
470 }
471 .into(),
472 );
473
474 Ok(())
475 }
476
477 #[benchmark]
478 fn unassign_curator() -> Result<(), BenchmarkError> {
479 let s = create_active_child_bounty::<T, I>()?;
480
481 #[extrinsic_call]
482 _(RawOrigin::Signed(s.curator), s.parent_bounty_id, Some(s.child_bounty_id));
483
484 assert_last_event::<T, I>(
485 Event::CuratorUnassigned {
486 index: s.parent_bounty_id,
487 child_index: Some(s.child_bounty_id),
488 }
489 .into(),
490 );
491
492 Ok(())
493 }
494
495 #[benchmark]
496 fn award_bounty() -> Result<(), BenchmarkError> {
497 let s = create_active_child_bounty::<T, I>()?;
498 let beneficiary_lookup = T::BeneficiaryLookup::unlookup(s.beneficiary.clone());
499
500 #[extrinsic_call]
501 _(
502 RawOrigin::Signed(s.child_curator),
503 s.parent_bounty_id,
504 Some(s.child_bounty_id),
505 beneficiary_lookup,
506 );
507
508 assert_last_event::<T, I>(
509 Event::BountyAwarded {
510 index: s.parent_bounty_id,
511 child_index: Some(s.child_bounty_id),
512 beneficiary: s.beneficiary,
513 }
514 .into(),
515 );
516
517 Ok(())
518 }
519
520 #[benchmark]
521 fn close_parent_bounty() -> Result<(), BenchmarkError> {
522 let s = create_active_parent_bounty::<T, I>()?;
523
524 let reject_origin =
525 T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
526
527 #[block]
528 {
529 assert_ok!(Bounties::<T, I>::close_bounty(
530 reject_origin.clone(),
531 s.parent_bounty_id,
532 None
533 ));
534 }
535
536 assert_last_event::<T, I>(
537 Event::BountyCanceled { index: s.parent_bounty_id, child_index: None }.into(),
538 );
539 let payment_id =
540 get_payment_id::<T, I>(s.parent_bounty_id, None).expect("no payment attempt");
541 assert_has_event::<T, I>(
542 Event::Paid { index: s.parent_bounty_id, child_index: None, payment_id }.into(),
543 );
544 assert_ne!(
545 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
546 PaymentStatus::Failure
547 );
548 assert!(Bounties::<T, I>::close_bounty(reject_origin, s.parent_bounty_id, None).is_err());
549
550 Ok(())
551 }
552
553 #[benchmark]
554 fn close_child_bounty() -> Result<(), BenchmarkError> {
555 let s = create_active_child_bounty::<T, I>()?;
556
557 let reject_origin =
558 T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
559
560 #[block]
561 {
562 assert_ok!(Bounties::<T, I>::close_bounty(
563 reject_origin.clone(),
564 s.parent_bounty_id,
565 Some(s.child_bounty_id),
566 ));
567 }
568
569 assert_last_event::<T, I>(
570 Event::BountyCanceled {
571 index: s.parent_bounty_id,
572 child_index: Some(s.child_bounty_id),
573 }
574 .into(),
575 );
576 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
577 .expect("no payment attempt");
578 assert_has_event::<T, I>(
579 Event::Paid {
580 index: s.parent_bounty_id,
581 child_index: Some(s.child_bounty_id),
582 payment_id,
583 }
584 .into(),
585 );
586 assert_ne!(
587 <T as pallet_bounties::Config<I>>::Paymaster::check_payment(payment_id),
588 PaymentStatus::Failure
589 );
590 assert!(Bounties::<T, I>::close_bounty(
591 reject_origin,
592 s.parent_bounty_id,
593 Some(s.child_bounty_id)
594 )
595 .is_err());
596
597 Ok(())
598 }
599
600 #[benchmark]
601 fn check_status_funding() -> Result<(), BenchmarkError> {
602 let s = create_child_bounty::<T, I>()?;
603 let caller = s.curator.clone();
604
605 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
606 .expect("no payment attempt");
607 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
608
609 #[block]
610 {
611 assert_ok!(Bounties::<T, I>::check_status(
612 RawOrigin::Signed(caller).into(),
613 s.parent_bounty_id,
614 Some(s.child_bounty_id),
615 ));
616 }
617
618 assert_last_event::<T, I>(
619 Event::BountyFundingProcessed {
620 index: s.parent_bounty_id,
621 child_index: Some(s.child_bounty_id),
622 }
623 .into(),
624 );
625 let child_bounty =
626 pallet_bounties::ChildBounties::<T, I>::get(s.parent_bounty_id, s.child_bounty_id)
627 .expect("no bounty");
628 assert!(matches!(child_bounty.status, BountyStatus::Funded { .. }));
629
630 Ok(())
631 }
632
633 #[benchmark]
634 fn check_status_refund() -> Result<(), BenchmarkError> {
635 let s = create_active_child_bounty::<T, I>()?;
636 let caller = s.curator.clone();
637
638 Bounties::<T, I>::close_bounty(
639 RawOrigin::Signed(caller.clone()).into(),
640 s.parent_bounty_id,
641 Some(s.child_bounty_id),
642 )?;
643 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
644 .expect("no payment attempt");
645 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
646
647 #[block]
648 {
649 assert_ok!(Bounties::<T, I>::check_status(
650 RawOrigin::Signed(caller).into(),
651 s.parent_bounty_id,
652 Some(s.child_bounty_id),
653 ));
654 }
655
656 assert_has_event::<T, I>(
657 Event::BountyRefundProcessed {
658 index: s.parent_bounty_id,
659 child_index: Some(s.child_bounty_id),
660 }
661 .into(),
662 );
663 assert_eq!(
664 pallet_bounties::ChildBounties::<T, I>::get(s.parent_bounty_id, s.child_bounty_id),
665 None
666 );
667
668 Ok(())
669 }
670
671 #[benchmark]
672 fn check_status_payout() -> Result<(), BenchmarkError> {
673 let s = create_awarded_child_bounty::<T, I>()?;
674 let caller = s.child_curator.clone();
675
676 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
677 .expect("no payment attempt");
678 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
679
680 #[block]
681 {
682 assert_ok!(Bounties::<T, I>::check_status(
683 RawOrigin::Signed(caller).into(),
684 s.parent_bounty_id,
685 Some(s.child_bounty_id),
686 ));
687 }
688
689 assert_has_event::<T, I>(
690 Event::BountyPayoutProcessed {
691 index: s.parent_bounty_id,
692 child_index: Some(s.child_bounty_id),
693 asset_kind: s.asset_kind,
694 value: s.child_value,
695 beneficiary: s.beneficiary,
696 }
697 .into(),
698 );
699 assert_eq!(
700 pallet_bounties::ChildBounties::<T, I>::get(s.parent_bounty_id, s.child_bounty_id),
701 None
702 );
703
704 Ok(())
705 }
706
707 #[benchmark]
708 fn retry_payment_funding() -> Result<(), BenchmarkError> {
709 let s = create_child_bounty::<T, I>()?;
710 let caller = s.curator.clone();
711
712 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
713 .expect("no payment attempt");
714 <T as pallet_bounties::Config<I>>::Paymaster::ensure_concluded(payment_id);
715 set_status::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id), PaymentState::Failed)?;
716
717 #[block]
718 {
719 assert_ok!(Bounties::<T, I>::retry_payment(
720 RawOrigin::Signed(caller.clone()).into(),
721 s.parent_bounty_id,
722 Some(s.child_bounty_id),
723 ));
724 }
725
726 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
727 .expect("no payment attempt");
728 assert_last_event::<T, I>(
729 Event::Paid {
730 index: s.parent_bounty_id,
731 child_index: Some(s.child_bounty_id),
732 payment_id,
733 }
734 .into(),
735 );
736 assert_ne!(
737 <T as pallet::Config<I>>::Paymaster::check_payment(payment_id),
738 PaymentStatus::Failure
739 );
740 assert!(Bounties::<T, I>::retry_payment(
741 RawOrigin::Signed(caller).into(),
742 s.parent_bounty_id,
743 Some(s.child_bounty_id),
744 )
745 .is_err());
746
747 Ok(())
748 }
749
750 #[benchmark]
751 fn retry_payment_refund() -> Result<(), BenchmarkError> {
752 let s = create_active_child_bounty::<T, I>()?;
753 let caller = s.curator.clone();
754
755 let new_status = BountyStatus::RefundAttempted {
756 payment_status: PaymentState::Failed,
757 curator: Some(s.child_curator),
758 };
759 let _ = pallet_bounties::Pallet::<T, I>::update_bounty_status(
760 s.parent_bounty_id,
761 Some(s.child_bounty_id),
762 new_status,
763 );
764
765 #[block]
766 {
767 assert_ok!(Bounties::<T, I>::retry_payment(
768 RawOrigin::Signed(caller.clone()).into(),
769 s.parent_bounty_id,
770 Some(s.child_bounty_id),
771 ));
772 }
773
774 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
775 .expect("no payment attempt");
776 assert_last_event::<T, I>(
777 Event::Paid {
778 index: s.parent_bounty_id,
779 child_index: Some(s.child_bounty_id),
780 payment_id,
781 }
782 .into(),
783 );
784 assert_ne!(
785 <T as pallet::Config<I>>::Paymaster::check_payment(payment_id),
786 PaymentStatus::Failure
787 );
788 assert!(Bounties::<T, I>::retry_payment(
789 RawOrigin::Signed(caller).into(),
790 s.parent_bounty_id,
791 Some(s.child_bounty_id),
792 )
793 .is_err());
794
795 Ok(())
796 }
797
798 #[benchmark]
799 fn retry_payment_payout() -> Result<(), BenchmarkError> {
800 let s = create_active_child_bounty::<T, I>()?;
801 let caller = s.curator.clone();
802
803 let new_status = BountyStatus::PayoutAttempted {
804 payment_status: PaymentState::Failed,
805 curator: s.child_curator.clone(),
806 beneficiary: s.beneficiary.clone(),
807 };
808 let _ = pallet_bounties::Pallet::<T, I>::update_bounty_status(
809 s.parent_bounty_id,
810 Some(s.child_bounty_id),
811 new_status,
812 );
813
814 #[block]
815 {
816 assert_ok!(Bounties::<T, I>::retry_payment(
817 RawOrigin::Signed(caller.clone()).into(),
818 s.parent_bounty_id,
819 Some(s.child_bounty_id),
820 ));
821 }
822
823 let payment_id = get_payment_id::<T, I>(s.parent_bounty_id, Some(s.child_bounty_id))
824 .expect("no payment attempt");
825 assert_last_event::<T, I>(
826 Event::Paid {
827 index: s.parent_bounty_id,
828 child_index: Some(s.child_bounty_id),
829 payment_id,
830 }
831 .into(),
832 );
833 assert_ne!(
834 <T as pallet::Config<I>>::Paymaster::check_payment(payment_id),
835 PaymentStatus::Failure
836 );
837 assert!(Bounties::<T, I>::retry_payment(
838 RawOrigin::Signed(caller).into(),
839 s.parent_bounty_id,
840 Some(s.child_bounty_id),
841 )
842 .is_err());
843
844 Ok(())
845 }
846
847 impl_benchmark_test_suite! {
848 Pallet,
849 crate::mock::ExtBuilder::default().build(),
850 crate::mock::Test
851 }
852}