radix_transactions/manifest/static_resource_movements/
effect.rs

1use super::*;
2use radix_common::prelude::*;
3
4use radix_engine_interface::blueprints::access_controller::*;
5use radix_engine_interface::blueprints::account::*;
6use radix_engine_interface::blueprints::consensus_manager::*;
7use radix_engine_interface::blueprints::identity::*;
8use radix_engine_interface::blueprints::locker::*;
9use radix_engine_interface::blueprints::package::*;
10use radix_engine_interface::blueprints::pool::*;
11use radix_engine_interface::blueprints::resource::*;
12use radix_engine_interface::blueprints::transaction_tracker::*;
13use radix_engine_interface::object_modules::metadata::*;
14use radix_engine_interface::object_modules::role_assignment::*;
15use radix_engine_interface::object_modules::royalty::*;
16
17pub trait StaticInvocationResourcesOutput
18where
19    Self: ManifestEncode + ManifestDecode,
20{
21    fn output(
22        &self,
23        details: InvocationDetails,
24    ) -> Result<TrackedResources, StaticResourceMovementsError>;
25}
26
27pub struct InvocationDetails<'a> {
28    pub receiver: InvocationReceiver,
29    pub sent_resources: &'a TrackedResources,
30    pub source: ChangeSource,
31}
32
33#[derive(Debug)]
34pub enum InvocationReceiver {
35    GlobalMethod(ResolvedDynamicAddress<GlobalAddress>),
36    DirectAccess(InternalAddress),
37    BlueprintFunction(BlueprintId),
38}
39
40macro_rules! no_output_static_invocation_resources_output_impl {
41    (
42        $(
43            $output_ident: ident
44        ),* $(,)?
45    ) => {
46        $(
47            impl StaticInvocationResourcesOutput for $output_ident {
48                fn output(
49                    &self,
50                    _details: InvocationDetails
51                ) -> Result<TrackedResources, StaticResourceMovementsError> {
52                    Ok(TrackedResources::new_empty())
53                }
54            }
55        )*
56    };
57}
58
59macro_rules! unknown_output_static_invocation_resources_output_impl {
60    (
61        $(
62            $output_ident: ident
63        ),* $(,)?
64    ) => {
65        $(
66            impl StaticInvocationResourcesOutput for $output_ident {
67                fn output(
68                    &self,
69                    details: InvocationDetails
70                ) -> Result<TrackedResources, StaticResourceMovementsError> {
71                    Ok(TrackedResources::new_with_possible_balance_of_unspecified_resources([
72                        details.source
73                    ]))
74                }
75            }
76        )*
77    };
78}
79
80no_output_static_invocation_resources_output_impl![
81    // AccessController
82    AccessControllerCreateManifestInput,
83    AccessControllerCreateProofManifestInput,
84    AccessControllerInitiateRecoveryAsPrimaryManifestInput,
85    AccessControllerInitiateRecoveryAsRecoveryManifestInput,
86    AccessControllerQuickConfirmPrimaryRoleRecoveryProposalManifestInput,
87    AccessControllerQuickConfirmRecoveryRoleRecoveryProposalManifestInput,
88    AccessControllerTimedConfirmRecoveryManifestInput,
89    AccessControllerCancelPrimaryRoleRecoveryProposalManifestInput,
90    AccessControllerCancelRecoveryRoleRecoveryProposalManifestInput,
91    AccessControllerLockPrimaryRoleManifestInput,
92    AccessControllerUnlockPrimaryRoleManifestInput,
93    AccessControllerStopTimedRecoveryManifestInput,
94    AccessControllerInitiateBadgeWithdrawAttemptAsPrimaryManifestInput,
95    AccessControllerInitiateBadgeWithdrawAttemptAsRecoveryManifestInput,
96    AccessControllerCancelPrimaryRoleBadgeWithdrawAttemptManifestInput,
97    AccessControllerCancelRecoveryRoleBadgeWithdrawAttemptManifestInput,
98    AccessControllerLockRecoveryFeeManifestInput,
99    AccessControllerContributeRecoveryFeeManifestInput,
100    // Account
101    AccountCreateAdvancedManifestInput,
102    AccountLockFeeManifestInput,
103    AccountLockContingentFeeManifestInput,
104    AccountDepositManifestInput,
105    AccountDepositBatchManifestInput,
106    AccountBurnManifestInput,
107    AccountBurnNonFungiblesManifestInput,
108    AccountCreateProofOfAmountManifestInput,
109    AccountCreateProofOfNonFungiblesManifestInput,
110    AccountSetDefaultDepositRuleManifestInput,
111    AccountSetResourcePreferenceManifestInput,
112    AccountRemoveResourcePreferenceManifestInput,
113    AccountTryDepositOrAbortManifestInput,
114    AccountTryDepositBatchOrAbortManifestInput,
115    AccountAddAuthorizedDepositorManifestInput,
116    AccountRemoveAuthorizedDepositorManifestInput,
117    // ConsensusManager
118    ConsensusManagerCreateManifestInput,
119    ConsensusManagerGetCurrentEpochManifestInput,
120    ConsensusManagerStartManifestInput,
121    ConsensusManagerGetCurrentTimeManifestInputV1,
122    ConsensusManagerGetCurrentTimeManifestInputV2,
123    ConsensusManagerCompareCurrentTimeManifestInputV1,
124    ConsensusManagerCompareCurrentTimeManifestInputV2,
125    ConsensusManagerNextRoundManifestInput,
126    // Validator
127    ValidatorRegisterManifestInput,
128    ValidatorUnregisterManifestInput,
129    ValidatorUpdateKeyManifestInput,
130    ValidatorUpdateFeeManifestInput,
131    ValidatorUpdateAcceptDelegatedStakeManifestInput,
132    ValidatorAcceptsDelegatedStakeManifestInput,
133    ValidatorTotalStakeXrdAmountManifestInput,
134    ValidatorTotalStakeUnitSupplyManifestInput,
135    ValidatorGetRedemptionValueManifestInput,
136    ValidatorSignalProtocolUpdateReadinessManifestInput,
137    ValidatorGetProtocolUpdateReadinessManifestInput,
138    ValidatorLockOwnerStakeUnitsManifestInput,
139    ValidatorStartUnlockOwnerStakeUnitsManifestInput,
140    ValidatorApplyEmissionManifestInput,
141    ValidatorApplyRewardManifestInput,
142    // Identity
143    IdentityCreateAdvancedManifestInput,
144    // AccountLocker
145    AccountLockerInstantiateManifestInput,
146    AccountLockerStoreManifestInput,
147    AccountLockerGetAmountManifestInput,
148    AccountLockerGetNonFungibleLocalIdsManifestInput,
149    // Package
150    PackagePublishWasmAdvancedManifestInput,
151    PackagePublishNativeManifestInput,
152    // OneResourcePool
153    OneResourcePoolInstantiateManifestInput,
154    OneResourcePoolProtectedDepositManifestInput,
155    OneResourcePoolGetRedemptionValueManifestInput,
156    OneResourcePoolGetVaultAmountManifestInput,
157    // TwoResourcePool
158    TwoResourcePoolInstantiateManifestInput,
159    TwoResourcePoolProtectedDepositManifestInput,
160    TwoResourcePoolGetRedemptionValueManifestInput,
161    TwoResourcePoolGetVaultAmountsManifestInput,
162    // MultiResourcePool
163    MultiResourcePoolInstantiateManifestInput,
164    MultiResourcePoolProtectedDepositManifestInput,
165    MultiResourcePoolGetRedemptionValueManifestInput,
166    MultiResourcePoolGetVaultAmountsManifestInput,
167    // ResourceManager
168    ResourceManagerBurnManifestInput,
169    ResourceManagerPackageBurnManifestInput,
170    ResourceManagerGetTotalSupplyManifestInput,
171    ResourceManagerGetResourceTypeManifestInput,
172    ResourceManagerCreateEmptyVaultManifestInput,
173    ResourceManagerGetAmountForWithdrawalManifestInput,
174    ResourceManagerDropEmptyBucketManifestInput,
175    // FungibleResourceManager
176    FungibleResourceManagerCreateManifestInput,
177    // NonFungibleResourceManager
178    NonFungibleResourceManagerCreateManifestInput,
179    NonFungibleResourceManagerGetNonFungibleManifestInput,
180    NonFungibleResourceManagerUpdateDataManifestInput,
181    NonFungibleResourceManagerExistsManifestInput,
182    // Vault
183    VaultGetAmountManifestInput,
184    VaultFreezeManifestInput,
185    VaultUnfreezeManifestInput,
186    VaultBurnManifestInput,
187    VaultPutManifestInput,
188    // FungibleVault
189    FungibleVaultLockFeeManifestInput,
190    FungibleVaultCreateProofOfAmountManifestInput,
191    FungibleVaultLockFungibleAmountManifestInput,
192    FungibleVaultUnlockFungibleAmountManifestInput,
193    // NonFungibleVault
194    NonFungibleVaultGetNonFungibleLocalIdsManifestInput,
195    NonFungibleVaultContainsNonFungibleManifestInput,
196    NonFungibleVaultCreateProofOfNonFungiblesManifestInput,
197    NonFungibleVaultLockNonFungiblesManifestInput,
198    NonFungibleVaultUnlockNonFungiblesManifestInput,
199    NonFungibleVaultBurnNonFungiblesManifestInput,
200    // TransactionTracker
201    TransactionTrackerCreateManifestInput,
202    // Metadata
203    MetadataCreateManifestInput,
204    MetadataCreateWithDataManifestInput,
205    MetadataSetManifestInput,
206    MetadataLockManifestInput,
207    MetadataGetManifestInput,
208    MetadataRemoveManifestInput,
209    // RoleAssignment
210    RoleAssignmentCreateManifestInput,
211    RoleAssignmentSetOwnerManifestInput,
212    RoleAssignmentLockOwnerManifestInput,
213    RoleAssignmentSetManifestInput,
214    RoleAssignmentGetManifestInput,
215    // ComponentRoyalty
216    ComponentRoyaltyCreateManifestInput,
217    ComponentRoyaltySetManifestInput,
218    ComponentRoyaltyLockManifestInput,
219];
220
221unknown_output_static_invocation_resources_output_impl![
222    /* AccessController */
223    // The withdrawn badge is of an unknown resource
224    AccessControllerQuickConfirmPrimaryRoleBadgeWithdrawAttemptManifestInput,
225    // The withdrawn badge is of an unknown resource
226    AccessControllerQuickConfirmRecoveryRoleBadgeWithdrawAttemptManifestInput,
227    // The minted badge is of a new / unknown resource
228    AccessControllerMintRecoveryBadgesManifestInput,
229    // The validator stake unit resource is unknown at static validation time
230    /* Validator */
231    ValidatorStakeAsOwnerManifestInput,
232    // The validator stake unit resource is unknown at static validation time
233    ValidatorStakeManifestInput,
234    // The validator unstake receipt is unknown at static validation time
235    ValidatorUnstakeManifestInput,
236    // This can return validator stake units which are an unknown resource at static validation time
237    ValidatorFinishUnlockOwnerStakeUnitsManifestInput,
238    // This generates and returns a new badge resource, which is unknowable at static time
239    /* AccountLocker */
240    AccountLockerInstantiateSimpleManifestInput,
241    /* OneResourcePool */
242    // This returns pool units of an unknown resource address and an unknown amount.
243    OneResourcePoolContributeManifestInput,
244    // This returns unknown resources of an unknown amount from the redemption.
245    OneResourcePoolRedeemManifestInput,
246    // This returns an unknown resource but a known amount which we can't do much with.
247    OneResourcePoolProtectedWithdrawManifestInput,
248    /* TwoResourcePool */
249    // This returns pool units of an unknown resource address and an unknown amount.
250    TwoResourcePoolContributeManifestInput,
251    // This returns unknown resources of an unknown amount from the redemption.
252    TwoResourcePoolRedeemManifestInput,
253    /* MultiResourcePool */
254    // This returns pool units of an unknown resource address and an unknown amount.
255    MultiResourcePoolContributeManifestInput,
256    // This returns unknown resources of an unknown amount from the redemption.
257    MultiResourcePoolRedeemManifestInput,
258    /* FungibleResourceManager */
259    // This returns this resource so we know the amount but we don't know the resource address
260    // so we can't do much with that.
261    FungibleResourceManagerCreateWithInitialSupplyManifestInput,
262    /* NonFungibleResourceManager */
263    // This returns this resource so we know the ids but we don't know the resource address
264    // so we can't do much with that.
265    NonFungibleResourceManagerCreateWithInitialSupplyManifestInput,
266    // This returns this resource so we know the ids but we don't know the resource address
267    // so we can't do much with that.
268    NonFungibleResourceManagerCreateRuidWithInitialSupplyManifestInput,
269    /* Vault */
270    // We don't know what resource is in the vault. We know the amount/ids returned but not the
271    // resource address.
272    VaultTakeManifestInput,
273    // We don't know what resource is in the vault. We know the amount/ids returned but not the
274    // resource address.
275    VaultTakeAdvancedManifestInput,
276    // We don't know what resource is in the vault. We know the amount/ids returned but not the
277    // resource address.
278    VaultRecallManifestInput,
279    /* NonFungibleVault */
280    // We don't know what resource is in the vault. We know the amount/ids returned but not the
281    // resource address.
282    NonFungibleVaultTakeNonFungiblesManifestInput,
283    // We don't know what resource is in the vault. We know the amount/ids returned but not the
284    // resource address.
285    NonFungibleVaultRecallNonFungiblesManifestInput,
286];
287
288// region:Typed Invocation
289impl StaticInvocationResourcesOutput for TypedManifestNativeInvocation {
290    fn output(
291        &self,
292        details: InvocationDetails,
293    ) -> Result<TrackedResources, StaticResourceMovementsError> {
294        uniform_match_on_manifest_typed_invocation!(self => (input) => input.output(details))
295    }
296}
297// endregion:Typed Invocation
298
299// region:AccessController
300impl StaticInvocationResourcesOutput for AccessControllerWithdrawRecoveryFeeManifestInput {
301    fn output(
302        &self,
303        details: InvocationDetails,
304    ) -> Result<TrackedResources, StaticResourceMovementsError> {
305        TrackedResources::new_empty().add_resource(
306            XRD,
307            TrackedResource::exact_amount(self.amount, [details.source])?,
308        )
309    }
310}
311// endregion:AccessController
312
313// region:Account
314impl StaticInvocationResourcesOutput for AccountCreateManifestInput {
315    fn output(
316        &self,
317        details: InvocationDetails,
318    ) -> Result<TrackedResources, StaticResourceMovementsError> {
319        TrackedResources::new_empty().add_resource(
320            ACCOUNT_OWNER_BADGE,
321            TrackedResource::exact_amount(1, [details.source])?,
322        )
323    }
324}
325
326impl StaticInvocationResourcesOutput for AccountSecurifyManifestInput {
327    fn output(
328        &self,
329        details: InvocationDetails,
330    ) -> Result<TrackedResources, StaticResourceMovementsError> {
331        match details.receiver {
332            InvocationReceiver::GlobalMethod(ResolvedDynamicAddress::StaticAddress(
333                global_address,
334            )) => {
335                let local_id = NonFungibleLocalId::bytes(global_address.as_bytes()).unwrap();
336                TrackedResources::new_empty().add_resource(
337                    ACCOUNT_OWNER_BADGE,
338                    TrackedResource::exact_non_fungibles([local_id], [details.source]),
339                )
340            }
341            InvocationReceiver::GlobalMethod(
342                ResolvedDynamicAddress::BlueprintResolvedFromNamedAddress(_),
343            ) => TrackedResources::new_empty().add_resource(
344                ACCOUNT_OWNER_BADGE,
345                TrackedResource::exact_amount(1, [details.source])?,
346            ),
347            InvocationReceiver::DirectAccess(_) | InvocationReceiver::BlueprintFunction(_) => {
348                unreachable!()
349            }
350        }
351    }
352}
353
354impl StaticInvocationResourcesOutput for AccountWithdrawManifestInput {
355    fn output(
356        &self,
357        details: InvocationDetails,
358    ) -> Result<TrackedResources, StaticResourceMovementsError> {
359        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
360            return Ok(
361                TrackedResources::new_with_possible_balance_of_unspecified_resources([
362                    details.source
363                ]),
364            );
365        };
366        TrackedResources::new_empty().add_resource(
367            resource_address,
368            TrackedResource::exact_amount(self.amount, [details.source])?,
369        )
370    }
371}
372
373impl StaticInvocationResourcesOutput for AccountWithdrawNonFungiblesManifestInput {
374    fn output(
375        &self,
376        details: InvocationDetails,
377    ) -> Result<TrackedResources, StaticResourceMovementsError> {
378        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
379            return Ok(
380                TrackedResources::new_with_possible_balance_of_unspecified_resources([
381                    details.source
382                ]),
383            );
384        };
385        TrackedResources::new_empty().add_resource(
386            resource_address,
387            TrackedResource::exact_non_fungibles(self.ids.clone(), [details.source]),
388        )
389    }
390}
391
392impl StaticInvocationResourcesOutput for AccountLockFeeAndWithdrawManifestInput {
393    fn output(
394        &self,
395        details: InvocationDetails,
396    ) -> Result<TrackedResources, StaticResourceMovementsError> {
397        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
398            return Ok(
399                TrackedResources::new_with_possible_balance_of_unspecified_resources([
400                    details.source
401                ]),
402            );
403        };
404        TrackedResources::new_empty().add_resource(
405            resource_address,
406            TrackedResource::exact_amount(self.amount, [details.source])?,
407        )
408    }
409}
410
411impl StaticInvocationResourcesOutput for AccountLockFeeAndWithdrawNonFungiblesManifestInput {
412    fn output(
413        &self,
414        details: InvocationDetails,
415    ) -> Result<TrackedResources, StaticResourceMovementsError> {
416        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
417            return Ok(
418                TrackedResources::new_with_possible_balance_of_unspecified_resources([
419                    details.source
420                ]),
421            );
422        };
423        TrackedResources::new_empty().add_resource(
424            resource_address,
425            TrackedResource::exact_non_fungibles(self.ids.clone(), [details.source]),
426        )
427    }
428}
429
430impl StaticInvocationResourcesOutput for AccountTryDepositOrRefundManifestInput {
431    fn output(
432        &self,
433        details: InvocationDetails,
434    ) -> Result<TrackedResources, StaticResourceMovementsError> {
435        handle_possible_refund(details)
436    }
437}
438
439impl StaticInvocationResourcesOutput for AccountTryDepositBatchOrRefundManifestInput {
440    fn output(
441        &self,
442        details: InvocationDetails,
443    ) -> Result<TrackedResources, StaticResourceMovementsError> {
444        handle_possible_refund(details)
445    }
446}
447
448fn handle_possible_refund(
449    details: InvocationDetails,
450) -> Result<TrackedResources, StaticResourceMovementsError> {
451    let mut sent_resources = details.sent_resources.clone();
452    let mut refunded_resources = TrackedResources::new_empty();
453
454    // Handle the specified resources. First dump the resource keys to work around the borrow checker...
455    let known_resources = sent_resources
456        .specified_resources()
457        .keys()
458        .cloned()
459        .collect::<Vec<_>>();
460    for known_resource in known_resources {
461        let attempted_deposit = sent_resources.mut_take_resource(
462            known_resource,
463            ResourceTakeAmount::All,
464            details.source,
465        )?;
466        let (bounds, _history) = attempted_deposit.deconstruct();
467        // Either nothing or everything is returned, but we can't currently model a fork in
468        // the timeline, so instead we handle it as a return of some amount between 0 and the sent amount.
469        let refunded_amount = bounds.replace_lower_bounds_with_zero();
470        refunded_resources.mut_add_resource(
471            known_resource,
472            TrackedResource::general(refunded_amount, [details.source]),
473        )?;
474    }
475    // Handle the possible refund of the remaining unspecified resources
476    if sent_resources.unspecified_resources().may_be_present() {
477        refunded_resources.mut_add_unspecified_resources([details.source]);
478    }
479
480    Ok(refunded_resources)
481}
482// endregion:Account
483
484// region:ConsensusManager
485impl StaticInvocationResourcesOutput for ConsensusManagerCreateValidatorManifestInput {
486    fn output(
487        &self,
488        details: InvocationDetails,
489    ) -> Result<TrackedResources, StaticResourceMovementsError> {
490        TrackedResources::new_empty().add_resource(
491            VALIDATOR_OWNER_BADGE,
492            TrackedResource::exact_amount(1, [details.source])?,
493        )
494    }
495}
496// endregion:ConsensusManager
497
498// region:Validator
499impl StaticInvocationResourcesOutput for ValidatorClaimXrdManifestInput {
500    fn output(
501        &self,
502        details: InvocationDetails,
503    ) -> Result<TrackedResources, StaticResourceMovementsError> {
504        TrackedResources::new_empty()
505            .add_resource(XRD, TrackedResource::zero_or_more([details.source]))
506    }
507}
508
509// endregion:Validator
510
511// region:Identity
512impl StaticInvocationResourcesOutput for IdentityCreateManifestInput {
513    fn output(
514        &self,
515        details: InvocationDetails,
516    ) -> Result<TrackedResources, StaticResourceMovementsError> {
517        TrackedResources::new_empty().add_resource(
518            IDENTITY_OWNER_BADGE,
519            TrackedResource::exact_amount(1, [details.source])?,
520        )
521    }
522}
523
524impl StaticInvocationResourcesOutput for IdentitySecurifyToSingleBadgeManifestInput {
525    fn output(
526        &self,
527        details: InvocationDetails,
528    ) -> Result<TrackedResources, StaticResourceMovementsError> {
529        Ok(match details.receiver {
530            InvocationReceiver::GlobalMethod(ResolvedDynamicAddress::StaticAddress(
531                global_address,
532            )) => {
533                let local_id = NonFungibleLocalId::bytes(global_address.as_bytes()).unwrap();
534                TrackedResources::new_empty().add_resource(
535                    IDENTITY_OWNER_BADGE,
536                    TrackedResource::exact_non_fungibles([local_id], [details.source]),
537                )?
538            }
539            InvocationReceiver::GlobalMethod(
540                ResolvedDynamicAddress::BlueprintResolvedFromNamedAddress(_),
541            ) => TrackedResources::new_empty().add_resource(
542                IDENTITY_OWNER_BADGE,
543                TrackedResource::exact_amount(1, [details.source])?,
544            )?,
545            InvocationReceiver::DirectAccess(_) | InvocationReceiver::BlueprintFunction(_) => {
546                unreachable!()
547            }
548        })
549    }
550}
551// endregion:Identity
552
553// region:AccountLocker
554impl StaticInvocationResourcesOutput for AccountLockerAirdropManifestInput {
555    fn output(
556        &self,
557        details: InvocationDetails,
558    ) -> Result<TrackedResources, StaticResourceMovementsError> {
559        // This behaves roughly like a possible refund...
560        // We could be even more exact... We can subtract the claimants from the bucket to calculate what gets returned.
561        // But this is good enough for now.
562        handle_possible_refund(details)
563    }
564}
565
566impl StaticInvocationResourcesOutput for AccountLockerRecoverManifestInput {
567    fn output(
568        &self,
569        details: InvocationDetails,
570    ) -> Result<TrackedResources, StaticResourceMovementsError> {
571        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
572            return Ok(
573                TrackedResources::new_with_possible_balance_of_unspecified_resources([
574                    details.source
575                ]),
576            );
577        };
578        TrackedResources::new_empty().add_resource(
579            resource_address,
580            TrackedResource::exact_amount(self.amount, [details.source])?,
581        )
582    }
583}
584
585impl StaticInvocationResourcesOutput for AccountLockerRecoverNonFungiblesManifestInput {
586    fn output(
587        &self,
588        details: InvocationDetails,
589    ) -> Result<TrackedResources, StaticResourceMovementsError> {
590        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
591            return Ok(
592                TrackedResources::new_with_possible_balance_of_unspecified_resources([
593                    details.source
594                ]),
595            );
596        };
597        TrackedResources::new_empty().add_resource(
598            resource_address,
599            TrackedResource::exact_non_fungibles(self.ids.clone(), [details.source]),
600        )
601    }
602}
603
604impl StaticInvocationResourcesOutput for AccountLockerClaimManifestInput {
605    fn output(
606        &self,
607        details: InvocationDetails,
608    ) -> Result<TrackedResources, StaticResourceMovementsError> {
609        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
610            return Ok(
611                TrackedResources::new_with_possible_balance_of_unspecified_resources([
612                    details.source
613                ]),
614            );
615        };
616        TrackedResources::new_empty().add_resource(
617            resource_address,
618            TrackedResource::exact_amount(self.amount, [details.source])?,
619        )
620    }
621}
622
623impl StaticInvocationResourcesOutput for AccountLockerClaimNonFungiblesManifestInput {
624    fn output(
625        &self,
626        details: InvocationDetails,
627    ) -> Result<TrackedResources, StaticResourceMovementsError> {
628        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
629            return Ok(
630                TrackedResources::new_with_possible_balance_of_unspecified_resources([
631                    details.source
632                ]),
633            );
634        };
635        TrackedResources::new_empty().add_resource(
636            resource_address,
637            TrackedResource::exact_non_fungibles(self.ids.clone(), [details.source]),
638        )
639    }
640}
641// endregion:AccountLocker
642
643// region:Package
644impl StaticInvocationResourcesOutput for PackagePublishWasmManifestInput {
645    fn output(
646        &self,
647        details: InvocationDetails,
648    ) -> Result<TrackedResources, StaticResourceMovementsError> {
649        TrackedResources::new_empty().add_resource(
650            PACKAGE_OWNER_BADGE,
651            TrackedResource::exact_amount(1, [details.source])?,
652        )
653    }
654}
655
656impl StaticInvocationResourcesOutput for PackageClaimRoyaltiesManifestInput {
657    fn output(
658        &self,
659        details: InvocationDetails,
660    ) -> Result<TrackedResources, StaticResourceMovementsError> {
661        TrackedResources::new_empty()
662            .add_resource(XRD, TrackedResource::zero_or_more([details.source]))
663    }
664}
665// endregion:Package
666
667// region:TwoResourcePool
668impl StaticInvocationResourcesOutput for TwoResourcePoolProtectedWithdrawManifestInput {
669    fn output(
670        &self,
671        details: InvocationDetails,
672    ) -> Result<TrackedResources, StaticResourceMovementsError> {
673        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
674            return Ok(
675                TrackedResources::new_with_possible_balance_of_unspecified_resources([
676                    details.source
677                ]),
678            );
679        };
680        TrackedResources::new_empty().add_resource(
681            resource_address,
682            TrackedResource::exact_amount(self.amount, [details.source])?,
683        )
684    }
685}
686// endregion:TwoResourcePool
687
688// region:MultiResourcePool
689impl StaticInvocationResourcesOutput for MultiResourcePoolProtectedWithdrawManifestInput {
690    fn output(
691        &self,
692        details: InvocationDetails,
693    ) -> Result<TrackedResources, StaticResourceMovementsError> {
694        let ManifestResourceAddress::Static(resource_address) = self.resource_address else {
695            return Ok(
696                TrackedResources::new_with_possible_balance_of_unspecified_resources([
697                    details.source
698                ]),
699            );
700        };
701        TrackedResources::new_empty().add_resource(
702            resource_address,
703            TrackedResource::exact_amount(self.amount, [details.source])?,
704        )
705    }
706}
707// endregion:MultiResourcePool
708
709// region:FungibleResourceManager
710impl StaticInvocationResourcesOutput for FungibleResourceManagerMintManifestInput {
711    fn output(
712        &self,
713        details: InvocationDetails,
714    ) -> Result<TrackedResources, StaticResourceMovementsError> {
715        // If the receiver is a global address then we can return something useful. Otherwise it
716        // is a known amount and an unknown resource address.
717        match details.receiver {
718            InvocationReceiver::GlobalMethod(ResolvedDynamicAddress::StaticAddress(
719                global_address,
720            )) => {
721                // Attempt to convert the global address to a resource address. Error if that fails.
722                if let Ok(resource_address) = ResourceAddress::try_from(global_address) {
723                    TrackedResources::new_empty().add_resource(
724                        resource_address,
725                        TrackedResource::exact_amount(self.amount, [details.source])?,
726                    )
727                } else {
728                    Err(StaticResourceMovementsError::NotAResourceAddress(
729                        global_address,
730                    ))
731                }
732            }
733            InvocationReceiver::GlobalMethod(
734                ResolvedDynamicAddress::BlueprintResolvedFromNamedAddress(_),
735            )
736            | InvocationReceiver::DirectAccess(_)
737            | InvocationReceiver::BlueprintFunction(_) => Ok(
738                TrackedResources::new_with_possible_balance_of_unspecified_resources([
739                    details.source
740                ]),
741            ),
742        }
743    }
744}
745
746impl StaticInvocationResourcesOutput for ResourceManagerCreateEmptyBucketInput {
747    fn output(
748        &self,
749        _: InvocationDetails,
750    ) -> Result<TrackedResources, StaticResourceMovementsError> {
751        // An empty bucket is returned so we just return an empty set of `TrackedResources`. I have
752        // this as a manual implementation instead of one of the invocations in the macro invocation
753        // because NONE of the invocations there return a bucket while this invocation returns a
754        // bucket. To be consistent and have all invocations returning a bucket have a manual impl
755        // I'm keeping this manual and hand-written implementation with a comment on why it's here
756        // and why this invocation returns nothing.
757        Ok(TrackedResources::new_empty())
758    }
759}
760// endregion:FungibleResourceManager
761
762// region:NonFungibleResourceManager
763impl StaticInvocationResourcesOutput for NonFungibleResourceManagerMintManifestInput {
764    fn output(
765        &self,
766        details: InvocationDetails,
767    ) -> Result<TrackedResources, StaticResourceMovementsError> {
768        // If the receiver is a global address then we can return something useful. Otherwise it
769        // is a known ids and an unknown resource address.
770        match details.receiver {
771            InvocationReceiver::GlobalMethod(ResolvedDynamicAddress::StaticAddress(
772                global_address,
773            )) => {
774                // Attempt to convert the global address to a resource address. Error if that fails.
775                if let Ok(resource_address) = ResourceAddress::try_from(global_address) {
776                    TrackedResources::new_empty().add_resource(
777                        resource_address,
778                        TrackedResource::exact_non_fungibles(
779                            self.entries.keys().cloned(),
780                            [details.source],
781                        ),
782                    )
783                } else {
784                    Err(StaticResourceMovementsError::NotAResourceAddress(
785                        global_address,
786                    ))
787                }
788            }
789            InvocationReceiver::GlobalMethod(
790                ResolvedDynamicAddress::BlueprintResolvedFromNamedAddress(_),
791            )
792            | InvocationReceiver::DirectAccess(_)
793            | InvocationReceiver::BlueprintFunction(_) => Ok(
794                TrackedResources::new_with_possible_balance_of_unspecified_resources([
795                    details.source
796                ]),
797            ),
798        }
799    }
800}
801
802impl StaticInvocationResourcesOutput for NonFungibleResourceManagerMintRuidManifestInput {
803    fn output(
804        &self,
805        details: InvocationDetails,
806    ) -> Result<TrackedResources, StaticResourceMovementsError> {
807        // If the receiver is a global address then we can return something useful. Otherwise it
808        // is a known amount and an unknown resource address.
809        match details.receiver {
810            InvocationReceiver::GlobalMethod(ResolvedDynamicAddress::StaticAddress(
811                global_address,
812            )) => {
813                // Attempt to convert the global address to a resource address. Error if that fails.
814                if let Ok(resource_address) = ResourceAddress::try_from(global_address) {
815                    TrackedResources::new_empty().add_resource(
816                        resource_address,
817                        TrackedResource::exact_amount(self.entries.len(), [details.source])?,
818                    )
819                } else {
820                    Err(StaticResourceMovementsError::NotAResourceAddress(
821                        global_address,
822                    ))
823                }
824            }
825            InvocationReceiver::GlobalMethod(
826                ResolvedDynamicAddress::BlueprintResolvedFromNamedAddress(_),
827            )
828            | InvocationReceiver::DirectAccess(_)
829            | InvocationReceiver::BlueprintFunction(_) => Ok(
830                TrackedResources::new_with_possible_balance_of_unspecified_resources([
831                    details.source
832                ]),
833            ),
834        }
835    }
836}
837
838impl StaticInvocationResourcesOutput for NonFungibleResourceManagerMintSingleRuidManifestInput {
839    fn output(
840        &self,
841        details: InvocationDetails,
842    ) -> Result<TrackedResources, StaticResourceMovementsError> {
843        // If the receiver is a global address then we can return something useful. Otherwise it
844        // is a known amount and an unknown resource address.
845        match details.receiver {
846            InvocationReceiver::GlobalMethod(ResolvedDynamicAddress::StaticAddress(
847                global_address,
848            )) => {
849                // Attempt to convert the global address to a resource address. Error if that fails.
850                if let Ok(resource_address) = ResourceAddress::try_from(global_address) {
851                    TrackedResources::new_empty().add_resource(
852                        resource_address,
853                        TrackedResource::exact_amount(Decimal::ONE, [details.source])?,
854                    )
855                } else {
856                    Err(StaticResourceMovementsError::NotAResourceAddress(
857                        global_address,
858                    ))
859                }
860            }
861            InvocationReceiver::GlobalMethod(
862                ResolvedDynamicAddress::BlueprintResolvedFromNamedAddress(_),
863            )
864            | InvocationReceiver::DirectAccess(_)
865            | InvocationReceiver::BlueprintFunction(_) => Ok(
866                TrackedResources::new_with_possible_balance_of_unspecified_resources([
867                    details.source
868                ]),
869            ),
870        }
871    }
872}
873// endregion:NonFungibleResourceManager
874
875// region:ComponentRoyalty
876impl StaticInvocationResourcesOutput for ComponentClaimRoyaltiesManifestInput {
877    fn output(
878        &self,
879        details: InvocationDetails,
880    ) -> Result<TrackedResources, StaticResourceMovementsError> {
881        TrackedResources::new_empty()
882            .add_resource(XRD, TrackedResource::zero_or_more([details.source]))
883    }
884}
885// endregion:ComponentRoyalty