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