1use super::*;
2use crate::blueprints::util::{PresecurifiedRoleAssignment, SecurifiedRoleAssignment};
3use crate::errors::ApplicationError;
4use crate::errors::RuntimeError;
5use crate::internal_prelude::*;
6use radix_engine_interface::api::field_api::LockFlags;
7use radix_engine_interface::api::FieldValue;
8use radix_engine_interface::api::{AttachedModuleId, GenericArgs, SystemApi, ACTOR_STATE_SELF};
9use radix_engine_interface::blueprints::account::*;
10use radix_engine_interface::blueprints::hooks::OnVirtualizeInput;
11use radix_engine_interface::blueprints::hooks::OnVirtualizeOutput;
12use radix_engine_interface::blueprints::resource::{Bucket, Proof};
13use radix_engine_interface::metadata_init;
14use radix_engine_interface::object_modules::metadata::*;
15use radix_native_sdk::modules::metadata::Metadata;
16use radix_native_sdk::modules::role_assignment::RoleAssignment;
17use radix_native_sdk::resource::NativeFungibleVault;
18use radix_native_sdk::resource::NativeNonFungibleVault;
19use radix_native_sdk::resource::NativeVault;
20use radix_native_sdk::resource::{NativeBucket, NativeNonFungibleBucket};
21use radix_native_sdk::runtime::Runtime;
22
23pub const ACCOUNT_CREATE_PREALLOCATED_SECP256K1_ID: u8 = 0u8;
30pub const ACCOUNT_CREATE_PREALLOCATED_ED25519_ID: u8 = 1u8;
31
32#[derive(Debug, PartialEq, Eq, ScryptoSbor, Clone)]
33pub struct AccountSubstate {
34 pub default_deposit_rule: DefaultDepositRule,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
38pub enum AccountError {
39 VaultDoesNotExist { resource_address: ResourceAddress },
40 DepositIsDisallowed { resource_address: ResourceAddress },
41 NotAllBucketsCouldBeDeposited,
42 NotAnAuthorizedDepositor { depositor: ResourceOrNonFungible },
43}
44
45impl From<AccountError> for RuntimeError {
46 fn from(value: AccountError) -> Self {
47 Self::ApplicationError(ApplicationError::AccountError(value))
48 }
49}
50
51pub const SECURIFY_ROLE: &'static str = "securify";
52
53struct SecurifiedAccount;
54
55impl SecurifiedRoleAssignment for SecurifiedAccount {
56 type OwnerBadgeNonFungibleData = AccountOwnerBadgeData;
57 const OWNER_BADGE: ResourceAddress = ACCOUNT_OWNER_BADGE;
58 const SECURIFY_ROLE: Option<&'static str> = Some(SECURIFY_ROLE);
59}
60
61impl PresecurifiedRoleAssignment for SecurifiedAccount {}
62
63declare_native_blueprint_state! {
64 blueprint_ident: Account,
65 blueprint_snake_case: account,
66 features: {
67 },
68 fields: {
69 deposit_rule: {
70 ident: DepositRule,
71 field_type: {
72 kind: StaticSingleVersioned,
73 },
74 condition: Condition::Always,
75 }
76 },
77 collections: {
78 resource_vaults: KeyValue {
79 entry_ident: ResourceVault,
80 key_type: {
81 kind: Static,
82 content_type: ResourceAddress,
83 },
84 value_type: {
85 kind: StaticSingleVersioned,
86 },
87 allow_ownership: true,
88 },
89 resource_preferences: KeyValue {
90 entry_ident: ResourcePreference,
91 key_type: {
92 kind: Static,
93 content_type: ResourceAddress,
94 },
95 value_type: {
96 kind: StaticSingleVersioned,
97 },
98 allow_ownership: false,
99 },
100 authorized_depositors: KeyValue {
101 entry_ident: AuthorizedDepositor,
102 key_type: {
103 kind: Static,
104 content_type: ResourceOrNonFungible,
105 },
106 value_type: {
107 kind: StaticSingleVersioned,
108 },
109 allow_ownership: false,
110 },
111 }
112}
113
114pub type AccountDepositRuleV1 = AccountSubstate;
115pub type AccountResourceVaultV1 = Vault;
116pub type AccountResourcePreferenceV1 = ResourcePreference;
117pub type AccountAuthorizedDepositorV1 = ();
118
119pub struct AccountBlueprint;
120
121impl AccountBlueprint {
122 pub fn get_definition() -> BlueprintDefinitionInit {
123 let mut aggregator = TypeAggregator::<ScryptoCustomTypeKind>::new();
124
125 let feature_set = AccountFeatureSet::all_features();
126 let state = AccountStateSchemaInit::create_schema_init(&mut aggregator);
127
128 let mut functions = index_map_new();
129
130 functions.insert(
131 ACCOUNT_CREATE_ADVANCED_IDENT.to_string(),
132 FunctionSchemaInit {
133 receiver: None,
134 input: TypeRef::Static(
135 aggregator.add_child_type_and_descendents::<AccountCreateAdvancedInput>(),
136 ),
137 output: TypeRef::Static(
138 aggregator.add_child_type_and_descendents::<AccountCreateAdvancedOutput>(),
139 ),
140 export: ACCOUNT_CREATE_ADVANCED_IDENT.to_string(),
141 },
142 );
143
144 functions.insert(
145 ACCOUNT_CREATE_IDENT.to_string(),
146 FunctionSchemaInit {
147 receiver: None,
148 input: TypeRef::Static(
149 aggregator.add_child_type_and_descendents::<AccountCreateInput>(),
150 ),
151 output: TypeRef::Static(
152 aggregator.add_child_type_and_descendents::<AccountCreateOutput>(),
153 ),
154 export: ACCOUNT_CREATE_IDENT.to_string(),
155 },
156 );
157
158 functions.insert(
159 ACCOUNT_SECURIFY_IDENT.to_string(),
160 FunctionSchemaInit {
161 receiver: Some(ReceiverInfo::normal_ref_mut()),
162 input: TypeRef::Static(
163 aggregator.add_child_type_and_descendents::<AccountSecurifyInput>(),
164 ),
165 output: TypeRef::Static(
166 aggregator.add_child_type_and_descendents::<AccountSecurifyOutput>(),
167 ),
168 export: ACCOUNT_SECURIFY_IDENT.to_string(),
169 },
170 );
171
172 functions.insert(
173 ACCOUNT_LOCK_FEE_IDENT.to_string(),
174 FunctionSchemaInit {
175 receiver: Some(ReceiverInfo::normal_ref_mut()),
176 input: TypeRef::Static(
177 aggregator.add_child_type_and_descendents::<AccountLockFeeInput>(),
178 ),
179 output: TypeRef::Static(
180 aggregator.add_child_type_and_descendents::<AccountLockFeeOutput>(),
181 ),
182 export: ACCOUNT_LOCK_FEE_IDENT.to_string(),
183 },
184 );
185
186 functions.insert(
187 ACCOUNT_LOCK_CONTINGENT_FEE_IDENT.to_string(),
188 FunctionSchemaInit {
189 receiver: Some(ReceiverInfo::normal_ref_mut()),
190 input: TypeRef::Static(
191 aggregator.add_child_type_and_descendents::<AccountLockContingentFeeInput>(),
192 ),
193 output: TypeRef::Static(
194 aggregator.add_child_type_and_descendents::<AccountLockContingentFeeOutput>(),
195 ),
196 export: ACCOUNT_LOCK_CONTINGENT_FEE_IDENT.to_string(),
197 },
198 );
199
200 functions.insert(
201 ACCOUNT_DEPOSIT_IDENT.to_string(),
202 FunctionSchemaInit {
203 receiver: Some(ReceiverInfo::normal_ref_mut()),
204 input: TypeRef::Static(
205 aggregator.add_child_type_and_descendents::<AccountDepositInput>(),
206 ),
207 output: TypeRef::Static(
208 aggregator.add_child_type_and_descendents::<AccountDepositOutput>(),
209 ),
210 export: ACCOUNT_DEPOSIT_IDENT.to_string(),
211 },
212 );
213
214 functions.insert(
215 ACCOUNT_DEPOSIT_BATCH_IDENT.to_string(),
216 FunctionSchemaInit {
217 receiver: Some(ReceiverInfo::normal_ref_mut()),
218 input: TypeRef::Static(
219 aggregator.add_child_type_and_descendents::<AccountDepositBatchInput>(),
220 ),
221 output: TypeRef::Static(
222 aggregator.add_child_type_and_descendents::<AccountDepositBatchOutput>(),
223 ),
224 export: ACCOUNT_DEPOSIT_BATCH_IDENT.to_string(),
225 },
226 );
227
228 functions.insert(
229 ACCOUNT_WITHDRAW_IDENT.to_string(),
230 FunctionSchemaInit {
231 receiver: Some(ReceiverInfo::normal_ref_mut()),
232 input: TypeRef::Static(
233 aggregator.add_child_type_and_descendents::<AccountWithdrawInput>(),
234 ),
235 output: TypeRef::Static(
236 aggregator.add_child_type_and_descendents::<AccountWithdrawOutput>(),
237 ),
238 export: ACCOUNT_WITHDRAW_IDENT.to_string(),
239 },
240 );
241
242 functions.insert(
243 ACCOUNT_WITHDRAW_NON_FUNGIBLES_IDENT.to_string(),
244 FunctionSchemaInit {
245 receiver: Some(ReceiverInfo::normal_ref_mut()),
246 input: TypeRef::Static(
247 aggregator.add_child_type_and_descendents::<AccountWithdrawNonFungiblesInput>(),
248 ),
249 output: TypeRef::Static(
250 aggregator
251 .add_child_type_and_descendents::<AccountWithdrawNonFungiblesOutput>(),
252 ),
253 export: ACCOUNT_WITHDRAW_NON_FUNGIBLES_IDENT.to_string(),
254 },
255 );
256
257 functions.insert(
258 ACCOUNT_BURN_IDENT.to_string(),
259 FunctionSchemaInit {
260 receiver: Some(ReceiverInfo::normal_ref_mut()),
261 input: TypeRef::Static(
262 aggregator.add_child_type_and_descendents::<AccountBurnInput>(),
263 ),
264 output: TypeRef::Static(
265 aggregator.add_child_type_and_descendents::<AccountBurnOutput>(),
266 ),
267 export: ACCOUNT_BURN_IDENT.to_string(),
268 },
269 );
270
271 functions.insert(
272 ACCOUNT_BURN_NON_FUNGIBLES_IDENT.to_string(),
273 FunctionSchemaInit {
274 receiver: Some(ReceiverInfo::normal_ref_mut()),
275 input: TypeRef::Static(
276 aggregator.add_child_type_and_descendents::<AccountBurnNonFungiblesInput>(),
277 ),
278 output: TypeRef::Static(
279 aggregator.add_child_type_and_descendents::<AccountBurnNonFungiblesOutput>(),
280 ),
281 export: ACCOUNT_BURN_NON_FUNGIBLES_IDENT.to_string(),
282 },
283 );
284
285 functions.insert(
286 ACCOUNT_LOCK_FEE_AND_WITHDRAW_IDENT.to_string(),
287 FunctionSchemaInit {
288 receiver: Some(ReceiverInfo::normal_ref_mut()),
289 input: TypeRef::Static(
290 aggregator.add_child_type_and_descendents::<AccountLockFeeAndWithdrawInput>(),
291 ),
292 output: TypeRef::Static(
293 aggregator.add_child_type_and_descendents::<AccountLockFeeAndWithdrawOutput>(),
294 ),
295 export: ACCOUNT_LOCK_FEE_AND_WITHDRAW_IDENT.to_string(),
296 },
297 );
298
299 functions.insert(
300 ACCOUNT_LOCK_FEE_AND_WITHDRAW_NON_FUNGIBLES_IDENT.to_string(),
301 FunctionSchemaInit {
302 receiver: Some(ReceiverInfo::normal_ref_mut()),
303 input: TypeRef::Static(aggregator
304 .add_child_type_and_descendents::<AccountLockFeeAndWithdrawNonFungiblesInput>()),
305 output: TypeRef::Static(aggregator
306 .add_child_type_and_descendents::<AccountLockFeeAndWithdrawNonFungiblesOutput>(
307 )),
308 export: ACCOUNT_LOCK_FEE_AND_WITHDRAW_NON_FUNGIBLES_IDENT.to_string(),
309 },
310 );
311
312 functions.insert(
313 ACCOUNT_CREATE_PROOF_OF_AMOUNT_IDENT.to_string(),
314 FunctionSchemaInit {
315 receiver: Some(ReceiverInfo::normal_ref()),
316 input: TypeRef::Static(
317 aggregator.add_child_type_and_descendents::<AccountCreateProofOfAmountInput>(),
318 ),
319 output: TypeRef::Static(
320 aggregator.add_child_type_and_descendents::<AccountCreateProofOfAmountOutput>(),
321 ),
322 export: ACCOUNT_CREATE_PROOF_OF_AMOUNT_IDENT.to_string(),
323 },
324 );
325
326 functions.insert(
327 ACCOUNT_CREATE_PROOF_OF_NON_FUNGIBLES_IDENT.to_string(),
328 FunctionSchemaInit {
329 receiver: Some(ReceiverInfo::normal_ref()),
330 input: TypeRef::Static(
331 aggregator
332 .add_child_type_and_descendents::<AccountCreateProofOfNonFungiblesInput>(),
333 ),
334 output: TypeRef::Static(
335 aggregator
336 .add_child_type_and_descendents::<AccountCreateProofOfNonFungiblesOutput>(),
337 ),
338 export: ACCOUNT_CREATE_PROOF_OF_NON_FUNGIBLES_IDENT.to_string(),
339 },
340 );
341
342 functions.insert(
343 ACCOUNT_SET_DEFAULT_DEPOSIT_RULE_IDENT.to_string(),
344 FunctionSchemaInit {
345 receiver: Some(ReceiverInfo::normal_ref()),
346 input: TypeRef::Static(
347 aggregator
348 .add_child_type_and_descendents::<AccountSetDefaultDepositRuleInput>(),
349 ),
350 output: TypeRef::Static(
351 aggregator
352 .add_child_type_and_descendents::<AccountSetDefaultDepositRuleOutput>(),
353 ),
354 export: ACCOUNT_SET_DEFAULT_DEPOSIT_RULE_IDENT.to_string(),
355 },
356 );
357
358 functions.insert(
359 ACCOUNT_SET_RESOURCE_PREFERENCE_IDENT.to_string(),
360 FunctionSchemaInit {
361 receiver: Some(ReceiverInfo::normal_ref()),
362 input: TypeRef::Static(
363 aggregator
364 .add_child_type_and_descendents::<AccountSetResourcePreferenceInput>(),
365 ),
366 output: TypeRef::Static(
367 aggregator
368 .add_child_type_and_descendents::<AccountSetResourcePreferenceOutput>(),
369 ),
370 export: ACCOUNT_SET_RESOURCE_PREFERENCE_IDENT.to_string(),
371 },
372 );
373
374 functions.insert(
375 ACCOUNT_REMOVE_RESOURCE_PREFERENCE_IDENT.to_string(),
376 FunctionSchemaInit {
377 receiver: Some(ReceiverInfo::normal_ref()),
378 input: TypeRef::Static(
379 aggregator
380 .add_child_type_and_descendents::<AccountRemoveResourcePreferenceInput>(),
381 ),
382 output: TypeRef::Static(
383 aggregator
384 .add_child_type_and_descendents::<AccountRemoveResourcePreferenceOutput>(),
385 ),
386 export: ACCOUNT_REMOVE_RESOURCE_PREFERENCE_IDENT.to_string(),
387 },
388 );
389
390 functions.insert(
391 ACCOUNT_TRY_DEPOSIT_OR_REFUND_IDENT.to_string(),
392 FunctionSchemaInit {
393 receiver: Some(ReceiverInfo::normal_ref_mut()),
394 input: TypeRef::Static(
395 aggregator.add_child_type_and_descendents::<AccountTryDepositOrRefundInput>(),
396 ),
397 output: TypeRef::Static(
398 aggregator.add_child_type_and_descendents::<AccountTryDepositOrRefundOutput>(),
399 ),
400 export: ACCOUNT_TRY_DEPOSIT_OR_REFUND_IDENT.to_string(),
401 },
402 );
403
404 functions.insert(
405 ACCOUNT_TRY_DEPOSIT_BATCH_OR_REFUND_IDENT.to_string(),
406 FunctionSchemaInit {
407 receiver: Some(ReceiverInfo::normal_ref_mut()),
408 input: TypeRef::Static(
409 aggregator
410 .add_child_type_and_descendents::<AccountTryDepositBatchOrRefundInput>(),
411 ),
412 output: TypeRef::Static(
413 aggregator
414 .add_child_type_and_descendents::<AccountTryDepositBatchOrRefundOutput>(),
415 ),
416 export: ACCOUNT_TRY_DEPOSIT_BATCH_OR_REFUND_IDENT.to_string(),
417 },
418 );
419
420 functions.insert(
421 ACCOUNT_TRY_DEPOSIT_OR_ABORT_IDENT.to_string(),
422 FunctionSchemaInit {
423 receiver: Some(ReceiverInfo::normal_ref_mut()),
424 input: TypeRef::Static(
425 aggregator.add_child_type_and_descendents::<AccountTryDepositOrAbortInput>(),
426 ),
427 output: TypeRef::Static(
428 aggregator.add_child_type_and_descendents::<AccountTryDepositOrAbortOutput>(),
429 ),
430 export: ACCOUNT_TRY_DEPOSIT_OR_ABORT_IDENT.to_string(),
431 },
432 );
433
434 functions.insert(
435 ACCOUNT_TRY_DEPOSIT_BATCH_OR_ABORT_IDENT.to_string(),
436 FunctionSchemaInit {
437 receiver: Some(ReceiverInfo::normal_ref_mut()),
438 input: TypeRef::Static(
439 aggregator
440 .add_child_type_and_descendents::<AccountTryDepositBatchOrAbortInput>(),
441 ),
442 output: TypeRef::Static(
443 aggregator
444 .add_child_type_and_descendents::<AccountTryDepositBatchOrAbortOutput>(),
445 ),
446 export: ACCOUNT_TRY_DEPOSIT_BATCH_OR_ABORT_IDENT.to_string(),
447 },
448 );
449
450 functions.insert(
451 ACCOUNT_ADD_AUTHORIZED_DEPOSITOR_IDENT.to_string(),
452 FunctionSchemaInit {
453 receiver: Some(ReceiverInfo::normal_ref_mut()),
454 input: TypeRef::Static(
455 aggregator
456 .add_child_type_and_descendents::<AccountAddAuthorizedDepositorInput>(),
457 ),
458 output: TypeRef::Static(
459 aggregator
460 .add_child_type_and_descendents::<AccountAddAuthorizedDepositorOutput>(),
461 ),
462 export: ACCOUNT_ADD_AUTHORIZED_DEPOSITOR_IDENT.to_string(),
463 },
464 );
465
466 functions.insert(
467 ACCOUNT_REMOVE_AUTHORIZED_DEPOSITOR_IDENT.to_string(),
468 FunctionSchemaInit {
469 receiver: Some(ReceiverInfo::normal_ref_mut()),
470 input: TypeRef::Static(
471 aggregator
472 .add_child_type_and_descendents::<AccountRemoveAuthorizedDepositorInput>(),
473 ),
474 output: TypeRef::Static(
475 aggregator
476 .add_child_type_and_descendents::<AccountRemoveAuthorizedDepositorOutput>(),
477 ),
478 export: ACCOUNT_REMOVE_AUTHORIZED_DEPOSITOR_IDENT.to_string(),
479 },
480 );
481
482 let events = event_schema! {
483 aggregator,
484 [
485 WithdrawEvent,
486 DepositEvent,
487 RejectedDepositEvent,
488 SetResourcePreferenceEvent,
489 RemoveResourcePreferenceEvent,
490 SetDefaultDepositRuleEvent,
491 AddAuthorizedDepositorEvent,
492 RemoveAuthorizedDepositorEvent,
493 ]
494 };
495
496 let schema = generate_full_schema(aggregator);
497
498 BlueprintDefinitionInit {
499 blueprint_type: BlueprintType::default(),
500 is_transient: false,
501 feature_set,
502 dependencies: indexset!(
503 SECP256K1_SIGNATURE_RESOURCE.into(),
504 ED25519_SIGNATURE_RESOURCE.into(),
505 ACCOUNT_OWNER_BADGE.into(),
506 PACKAGE_OF_DIRECT_CALLER_RESOURCE.into(),
507 ),
508
509 schema: BlueprintSchemaInit {
510 generics: vec![],
511 schema,
512 state,
513 events,
514 types: BlueprintTypeSchemaInit::default(),
515 functions: BlueprintFunctionsSchemaInit { functions },
516 hooks: BlueprintHooksInit {
517 hooks: indexmap!(BlueprintHook::OnVirtualize => ACCOUNT_ON_VIRTUALIZE_EXPORT_NAME.to_string()),
518 },
519 },
520
521 royalty_config: PackageRoyaltyConfig::default(),
522 auth_config: AuthConfig {
523 function_auth: FunctionAuth::AllowAll,
524 method_auth: MethodAuthTemplate::StaticRoleDefinition(roles_template!(
525 roles {
526 SECURIFY_ROLE => updaters: [SELF_ROLE];
527 },
528 methods {
529 ACCOUNT_SECURIFY_IDENT => [SECURIFY_ROLE];
530
531 ACCOUNT_SET_DEFAULT_DEPOSIT_RULE_IDENT => [OWNER_ROLE];
532 ACCOUNT_SET_RESOURCE_PREFERENCE_IDENT => [OWNER_ROLE];
533 ACCOUNT_REMOVE_RESOURCE_PREFERENCE_IDENT => [OWNER_ROLE];
534 ACCOUNT_WITHDRAW_IDENT => [OWNER_ROLE];
535 ACCOUNT_WITHDRAW_NON_FUNGIBLES_IDENT => [OWNER_ROLE];
536 ACCOUNT_LOCK_FEE_IDENT => [OWNER_ROLE];
537 ACCOUNT_LOCK_CONTINGENT_FEE_IDENT => [OWNER_ROLE];
538 ACCOUNT_LOCK_FEE_AND_WITHDRAW_IDENT => [OWNER_ROLE];
539 ACCOUNT_LOCK_FEE_AND_WITHDRAW_NON_FUNGIBLES_IDENT => [OWNER_ROLE];
540 ACCOUNT_CREATE_PROOF_OF_AMOUNT_IDENT => [OWNER_ROLE];
541 ACCOUNT_CREATE_PROOF_OF_NON_FUNGIBLES_IDENT => [OWNER_ROLE];
542 ACCOUNT_DEPOSIT_IDENT => [OWNER_ROLE];
543 ACCOUNT_DEPOSIT_BATCH_IDENT => [OWNER_ROLE];
544 ACCOUNT_BURN_IDENT => [OWNER_ROLE];
545 ACCOUNT_BURN_NON_FUNGIBLES_IDENT => [OWNER_ROLE];
546 ACCOUNT_ADD_AUTHORIZED_DEPOSITOR_IDENT => [OWNER_ROLE];
547 ACCOUNT_REMOVE_AUTHORIZED_DEPOSITOR_IDENT => [OWNER_ROLE];
548
549 ACCOUNT_TRY_DEPOSIT_OR_REFUND_IDENT => MethodAccessibility::Public;
550 ACCOUNT_TRY_DEPOSIT_BATCH_OR_REFUND_IDENT => MethodAccessibility::Public;
551 ACCOUNT_TRY_DEPOSIT_OR_ABORT_IDENT => MethodAccessibility::Public;
552 ACCOUNT_TRY_DEPOSIT_BATCH_OR_ABORT_IDENT => MethodAccessibility::Public;
553 }
554 )),
555 },
556 }
557 }
558
559 fn create_modules<Y: SystemApi<RuntimeError>>(
560 role_assignment: RoleAssignment,
561 metadata_init: MetadataInit,
562 api: &mut Y,
563 ) -> Result<IndexMap<AttachedModuleId, Own>, RuntimeError> {
564 let metadata = Metadata::create_with_data(metadata_init, api)?;
565
566 let modules = indexmap!(
568 AttachedModuleId::RoleAssignment => role_assignment.0,
569 AttachedModuleId::Metadata => metadata,
570 );
571
572 Ok(modules)
573 }
574
575 pub fn on_virtualize<Y: SystemApi<RuntimeError>>(
576 input: OnVirtualizeInput,
577 api: &mut Y,
578 ) -> Result<OnVirtualizeOutput, RuntimeError> {
579 match input.variant_id {
580 ACCOUNT_CREATE_PREALLOCATED_SECP256K1_ID => {
581 let public_key_hash = PublicKeyHash::Secp256k1(Secp256k1PublicKeyHash(input.rid));
582 Self::create_virtual(public_key_hash, input.address_reservation, api)
583 }
584 ACCOUNT_CREATE_PREALLOCATED_ED25519_ID => {
585 let public_key_hash = PublicKeyHash::Ed25519(Ed25519PublicKeyHash(input.rid));
586 Self::create_virtual(public_key_hash, input.address_reservation, api)
587 }
588 x => Err(RuntimeError::ApplicationError(
589 ApplicationError::PanicMessage(format!("Unexpected variant id: {:?}", x)),
590 )),
591 }
592 }
593
594 fn create_virtual<Y: SystemApi<RuntimeError>>(
595 public_key_hash: PublicKeyHash,
596 address_reservation: GlobalAddressReservation,
597 api: &mut Y,
598 ) -> Result<(), RuntimeError> {
599 let owner_badge = {
600 let bytes = public_key_hash.get_hash_bytes();
601 let entity_type = match public_key_hash {
602 PublicKeyHash::Ed25519(..) => EntityType::GlobalPreallocatedEd25519Account,
603 PublicKeyHash::Secp256k1(..) => EntityType::GlobalPreallocatedSecp256k1Account,
604 };
605
606 let mut id_bytes = vec![entity_type as u8];
607 id_bytes.extend(bytes);
608
609 NonFungibleLocalId::bytes(id_bytes).unwrap()
610 };
611
612 let account = Self::create_local(api)?;
613 let owner_id = NonFungibleGlobalId::from_public_key_hash(public_key_hash);
614 let role_assignment = SecurifiedAccount::create_presecurified(owner_id, api)?;
615 let modules = Self::create_modules(
616 role_assignment,
617 metadata_init!(
618 "owner_keys" => vec![public_key_hash], updatable;
625 "owner_badge" => owner_badge, locked;
626 ),
627 api,
628 )?;
629
630 api.globalize(
631 account.0,
632 modules.into_iter().map(|(k, v)| (k, v.0)).collect(),
633 Some(address_reservation),
634 )?;
635 Ok(())
636 }
637
638 pub fn securify<Y: SystemApi<RuntimeError>>(api: &mut Y) -> Result<Bucket, RuntimeError> {
639 let receiver = Runtime::get_node_id(api)?;
640 let owner_badge_data = AccountOwnerBadgeData {
641 name: "Account Owner Badge".into(),
642 account: ComponentAddress::new_or_panic(receiver.0),
643 };
644 let bucket = SecurifiedAccount::securify(
645 &receiver,
646 owner_badge_data,
647 Some(NonFungibleLocalId::bytes(receiver.0).unwrap()),
648 api,
649 )?;
650 Ok(bucket.into())
651 }
652
653 pub fn create_advanced<Y: SystemApi<RuntimeError>>(
654 owner_role: OwnerRole,
655 address_reservation: Option<GlobalAddressReservation>,
656 api: &mut Y,
657 ) -> Result<GlobalAddress, RuntimeError> {
658 let account = Self::create_local(api)?;
659 let role_assignment = SecurifiedAccount::create_advanced(owner_role, api)?;
660 let modules = Self::create_modules(
661 role_assignment,
662 metadata_init!(
663 "owner_badge" => EMPTY, locked;
664 ),
665 api,
666 )?;
667 let modules = modules.into_iter().map(|(id, own)| (id, own.0)).collect();
668
669 let address = api.globalize(account.0, modules, address_reservation)?;
670
671 Ok(address)
672 }
673
674 pub fn create<Y: SystemApi<RuntimeError>>(
675 api: &mut Y,
676 ) -> Result<(GlobalAddress, Bucket), RuntimeError> {
677 let (address_reservation, address) = api.allocate_global_address(BlueprintId {
678 package_address: ACCOUNT_PACKAGE,
679 blueprint_name: ACCOUNT_BLUEPRINT.to_string(),
680 })?;
681
682 let account = Self::create_local(api)?;
683 let (role_assignment, bucket) = SecurifiedAccount::create_securified(
684 AccountOwnerBadgeData {
685 name: "Account Owner Badge".into(),
686 account: address.try_into().expect("Impossible Case"),
687 },
688 Some(NonFungibleLocalId::bytes(address.as_node_id().0).unwrap()),
689 api,
690 )?;
691 let modules = Self::create_modules(
692 role_assignment,
693 metadata_init! {
694 "owner_badge" => NonFungibleLocalId::bytes(address.as_node_id().0).unwrap(), locked;
695 },
696 api,
697 )?;
698 let modules = modules.into_iter().map(|(id, own)| (id, own.0)).collect();
699
700 let address = api.globalize(account.0, modules, Some(address_reservation))?;
701
702 Ok((address, bucket))
703 }
704
705 fn create_local<Y: SystemApi<RuntimeError>>(api: &mut Y) -> Result<Own, RuntimeError> {
706 let account_id = api.new_object(
707 ACCOUNT_BLUEPRINT,
708 vec![],
709 GenericArgs::default(),
710 indexmap! {
711 AccountField::DepositRule.field_index() => FieldValue::new(&AccountDepositRuleFieldPayload::from_content_source(AccountDepositRuleV1 {
712 default_deposit_rule: DefaultDepositRule::Accept,
713 }))
714 },
715 indexmap!(),
716 )?;
717
718 Ok(Own(account_id))
719 }
720
721 fn lock_fee_internal<Y: SystemApi<RuntimeError>>(
722 amount: Decimal,
723 contingent: bool,
724 api: &mut Y,
725 ) -> Result<(), RuntimeError> {
726 let resource_address = XRD;
727
728 Self::get_vault(
729 resource_address,
730 |vault, api| {
731 if contingent {
732 vault.lock_contingent_fee(api, amount)
733 } else {
734 vault.lock_fee(api, amount)
735 }
736 },
737 false,
738 api,
739 )?;
740
741 Ok(())
742 }
743
744 pub fn lock_fee<Y: SystemApi<RuntimeError>>(
745 amount: Decimal,
746 api: &mut Y,
747 ) -> Result<(), RuntimeError> {
748 Self::lock_fee_internal(amount, false, api)?;
749 Ok(())
750 }
751
752 pub fn lock_contingent_fee<Y: SystemApi<RuntimeError>>(
753 amount: Decimal,
754 api: &mut Y,
755 ) -> Result<(), RuntimeError> {
756 Self::lock_fee_internal(amount, true, api)?;
757 Ok(())
758 }
759
760 pub fn deposit<Y: SystemApi<RuntimeError>>(
762 bucket: Bucket,
763 api: &mut Y,
764 ) -> Result<(), RuntimeError> {
765 let resource_address = bucket.resource_address(api)?;
766 let event = if resource_address.is_fungible() {
767 DepositEvent::Fungible(resource_address, bucket.amount(api)?)
768 } else {
769 DepositEvent::NonFungible(resource_address, bucket.non_fungible_local_ids(api)?)
770 };
771 Self::get_vault(
772 resource_address,
773 |vault, api| vault.put(bucket, api),
774 true,
775 api,
776 )?;
777 Runtime::emit_event(api, event)?;
778 Ok(())
779 }
780
781 pub fn deposit_batch<Y: SystemApi<RuntimeError>>(
783 buckets: Vec<Bucket>,
784 api: &mut Y,
785 ) -> Result<(), RuntimeError> {
786 for bucket in buckets {
787 Self::deposit(bucket, api)?;
788 }
789 Ok(())
790 }
791
792 pub fn try_deposit_or_refund<Y: SystemApi<RuntimeError>>(
793 bucket: Bucket,
794 authorized_depositor_badge: Option<ResourceOrNonFungible>,
795 api: &mut Y,
796 ) -> Result<Option<Bucket>, RuntimeError> {
797 let resource_address = bucket.resource_address(api)?;
798 let is_deposit_allowed = Self::is_deposit_allowed(&resource_address, api)?;
799 if is_deposit_allowed {
800 Self::deposit(bucket, api)?;
801 Ok(None)
802 } else if let Some(badge) = authorized_depositor_badge {
803 Self::validate_badge_is_authorized_depositor(&badge, api)??;
804 Self::validate_badge_is_present(badge, api)?;
805 Self::deposit(bucket, api)?;
806 Ok(None)
807 } else {
808 let event = if resource_address.is_fungible() {
809 RejectedDepositEvent::Fungible(resource_address, bucket.amount(api)?)
810 } else {
811 RejectedDepositEvent::NonFungible(
812 resource_address,
813 bucket.non_fungible_local_ids(api)?,
814 )
815 };
816 Runtime::emit_event(api, event)?;
817 Ok(Some(bucket))
818 }
819 }
820
821 pub fn try_deposit_batch_or_refund<Y: SystemApi<RuntimeError>>(
822 buckets: Vec<Bucket>,
823 authorized_depositor_badge: Option<ResourceOrNonFungible>,
824 api: &mut Y,
825 ) -> Result<Option<Vec<Bucket>>, RuntimeError> {
826 let offending_buckets = buckets
827 .iter()
828 .map(|bucket| {
829 bucket
830 .resource_address(api)
831 .and_then(|resource_address| Self::is_deposit_allowed(&resource_address, api))
832 .map(|can_be_deposited| (bucket, can_be_deposited))
833 })
834 .collect::<Result<Vec<_>, _>>()?
835 .into_iter()
836 .filter_map(|(bucket, can_be_deposited)| {
837 if !can_be_deposited {
838 Some(Bucket(bucket.0))
839 } else {
840 None
841 }
842 })
843 .collect::<Vec<_>>();
844
845 if offending_buckets.is_empty() {
846 Self::deposit_batch(buckets, api)?;
847 Ok(None)
848 } else if let Some(badge) = authorized_depositor_badge {
849 Self::validate_badge_is_authorized_depositor(&badge, api)??;
850 Self::validate_badge_is_present(badge, api)?;
851 Self::deposit_batch(buckets, api)?;
852 Ok(None)
853 } else {
854 for bucket in offending_buckets {
855 let resource_address = bucket.resource_address(api)?;
856 let event = if resource_address.is_fungible() {
857 RejectedDepositEvent::Fungible(resource_address, bucket.amount(api)?)
858 } else {
859 RejectedDepositEvent::NonFungible(
860 resource_address,
861 bucket.non_fungible_local_ids(api)?,
862 )
863 };
864 Runtime::emit_event(api, event)?;
865 }
866 Ok(Some(buckets))
867 }
868 }
869
870 pub fn try_deposit_or_abort<Y: SystemApi<RuntimeError>>(
871 bucket: Bucket,
872 authorized_depositor_badge: Option<ResourceOrNonFungible>,
873 api: &mut Y,
874 ) -> Result<(), RuntimeError> {
875 if let Some(bucket) = Self::try_deposit_or_refund(bucket, authorized_depositor_badge, api)?
876 {
877 let resource_address = bucket.resource_address(api)?;
878 Err(AccountError::DepositIsDisallowed { resource_address }.into())
879 } else {
880 Ok(())
881 }
882 }
883
884 pub fn try_deposit_batch_or_abort<Y: SystemApi<RuntimeError>>(
887 buckets: Vec<Bucket>,
888 authorized_depositor_badge: Option<ResourceOrNonFungible>,
889 api: &mut Y,
890 ) -> Result<(), RuntimeError> {
891 let buckets = Self::try_deposit_batch_or_refund(buckets, authorized_depositor_badge, api)?;
892 if let Some(_) = buckets {
893 Err(AccountError::NotAllBucketsCouldBeDeposited.into())
894 } else {
895 Ok(())
896 }
897 }
898
899 fn validate_badge_is_authorized_depositor<Y: SystemApi<RuntimeError>>(
903 badge: &ResourceOrNonFungible,
904 api: &mut Y,
905 ) -> Result<Result<(), AccountError>, RuntimeError> {
906 let encoded_key =
909 scrypto_encode(badge).expect("Failed to SBOR encode a `ResourceOrNonFungible`.");
910 let kv_store_entry_lock_handle = api.actor_open_key_value_entry(
911 ACTOR_STATE_SELF,
912 AccountCollection::AuthorizedDepositorKeyValue.collection_index(),
913 &encoded_key,
914 LockFlags::read_only(),
915 )?;
916 let entry = api.key_value_entry_get_typed::<VersionedAccountAuthorizedDepositor>(
917 kv_store_entry_lock_handle,
918 )?;
919 api.key_value_entry_close(kv_store_entry_lock_handle)?;
920 if entry.is_none() {
921 Ok(Err(AccountError::NotAnAuthorizedDepositor {
922 depositor: badge.clone(),
923 }))
924 } else {
925 Ok(Ok(()))
926 }
927 }
928
929 fn validate_badge_is_present<Y: SystemApi<RuntimeError>>(
930 badge: ResourceOrNonFungible,
931 api: &mut Y,
932 ) -> Result<(), RuntimeError> {
933 let access_rule = AccessRule::Protected(CompositeRequirement::BasicRequirement(
936 BasicRequirement::Require(badge),
937 ));
938
939 Runtime::assert_access_rule(access_rule, api)?;
940 Ok(())
941 }
942
943 pub fn withdraw<Y: SystemApi<RuntimeError>>(
944 resource_address: ResourceAddress,
945 amount: Decimal,
946 api: &mut Y,
947 ) -> Result<Bucket, RuntimeError> {
948 let bucket = Self::get_vault(
949 resource_address,
950 |vault, api| vault.take(amount, api),
951 false,
952 api,
953 )?;
954 let event = if resource_address.is_fungible() {
955 WithdrawEvent::Fungible(resource_address, bucket.amount(api)?)
956 } else {
957 WithdrawEvent::NonFungible(resource_address, bucket.non_fungible_local_ids(api)?)
958 };
959 Runtime::emit_event(api, event)?;
960
961 Ok(bucket)
962 }
963
964 pub fn withdraw_non_fungibles<Y: SystemApi<RuntimeError>>(
965 resource_address: ResourceAddress,
966 ids: IndexSet<NonFungibleLocalId>,
967 api: &mut Y,
968 ) -> Result<Bucket, RuntimeError> {
969 let bucket = Self::get_vault(
970 resource_address,
971 |vault, api| vault.take_non_fungibles(ids, api),
972 false,
973 api,
974 )?;
975 let event =
976 WithdrawEvent::NonFungible(resource_address, bucket.non_fungible_local_ids(api)?);
977 Runtime::emit_event(api, event)?;
978
979 Ok(bucket)
980 }
981
982 pub fn burn<Y: SystemApi<RuntimeError>>(
983 resource_address: ResourceAddress,
984 amount: Decimal,
985 api: &mut Y,
986 ) -> Result<(), RuntimeError> {
987 Self::get_vault(
988 resource_address,
989 |vault, api| vault.burn(amount, api),
990 false,
991 api,
992 )
993 }
994
995 pub fn burn_non_fungibles<Y: SystemApi<RuntimeError>>(
996 resource_address: ResourceAddress,
997 ids: IndexSet<NonFungibleLocalId>,
998 api: &mut Y,
999 ) -> Result<(), RuntimeError> {
1000 Self::get_vault(
1001 resource_address,
1002 |vault, api| vault.burn_non_fungibles(ids, api),
1003 false,
1004 api,
1005 )
1006 }
1007
1008 pub fn lock_fee_and_withdraw<Y: SystemApi<RuntimeError>>(
1009 amount_to_lock: Decimal,
1010 resource_address: ResourceAddress,
1011 amount: Decimal,
1012 api: &mut Y,
1013 ) -> Result<Bucket, RuntimeError> {
1014 Self::lock_fee_internal(amount_to_lock, false, api)?;
1015
1016 let bucket = Self::get_vault(
1017 resource_address,
1018 |vault, api| vault.take(amount, api),
1019 false,
1020 api,
1021 )?;
1022
1023 Ok(bucket)
1024 }
1025
1026 pub fn lock_fee_and_withdraw_non_fungibles<Y: SystemApi<RuntimeError>>(
1027 amount_to_lock: Decimal,
1028 resource_address: ResourceAddress,
1029 ids: IndexSet<NonFungibleLocalId>,
1030 api: &mut Y,
1031 ) -> Result<Bucket, RuntimeError> {
1032 Self::lock_fee_internal(amount_to_lock, false, api)?;
1033
1034 let bucket = Self::get_vault(
1035 resource_address,
1036 |vault, api| vault.take_non_fungibles(ids, api),
1037 false,
1038 api,
1039 )?;
1040
1041 Ok(bucket)
1042 }
1043
1044 pub fn create_proof_of_amount<Y: SystemApi<RuntimeError>>(
1045 resource_address: ResourceAddress,
1046 amount: Decimal,
1047 api: &mut Y,
1048 ) -> Result<Proof, RuntimeError> {
1049 let proof = Self::get_vault(
1050 resource_address,
1051 |vault, api| vault.create_proof_of_amount(amount, api),
1052 false,
1053 api,
1054 )?;
1055
1056 Ok(proof)
1057 }
1058
1059 pub fn create_proof_of_non_fungibles<Y: SystemApi<RuntimeError>>(
1060 resource_address: ResourceAddress,
1061 ids: IndexSet<NonFungibleLocalId>,
1062 api: &mut Y,
1063 ) -> Result<Proof, RuntimeError> {
1064 let proof = Self::get_vault(
1065 resource_address,
1066 |vault, api| vault.create_proof_of_non_fungibles(ids, api),
1067 false,
1068 api,
1069 )?;
1070
1071 Ok(proof)
1072 }
1073
1074 pub fn set_default_deposit_rule<Y: SystemApi<RuntimeError>>(
1075 default: DefaultDepositRule,
1076 api: &mut Y,
1077 ) -> Result<(), RuntimeError> {
1078 let handle = api.actor_open_field(
1079 ACTOR_STATE_SELF,
1080 AccountField::DepositRule.field_index(),
1081 LockFlags::MUTABLE,
1082 )?;
1083 api.field_write_typed(
1084 handle,
1085 &AccountDepositRuleFieldPayload::from_content_source(AccountDepositRuleV1 {
1086 default_deposit_rule: default,
1087 }),
1088 )?;
1089 api.field_close(handle)?;
1090
1091 Runtime::emit_event(
1092 api,
1093 SetDefaultDepositRuleEvent {
1094 default_deposit_rule: default,
1095 },
1096 )?;
1097
1098 Ok(())
1099 }
1100
1101 pub fn set_resource_preference<Y: SystemApi<RuntimeError>>(
1102 resource_address: ResourceAddress,
1103 resource_preference: ResourcePreference,
1104 api: &mut Y,
1105 ) -> Result<(), RuntimeError> {
1106 let encoded_key = scrypto_encode(&resource_address).expect("Impossible Case!");
1107 let kv_store_entry_lock_handle = api.actor_open_key_value_entry(
1108 ACTOR_STATE_SELF,
1109 AccountCollection::ResourcePreferenceKeyValue.collection_index(),
1110 &encoded_key,
1111 LockFlags::MUTABLE,
1112 )?;
1113 api.key_value_entry_set_typed(
1114 kv_store_entry_lock_handle,
1115 &AccountResourcePreferenceVersions::V1(resource_preference).into_versioned(),
1116 )?;
1117 api.key_value_entry_close(kv_store_entry_lock_handle)?;
1118
1119 Runtime::emit_event(
1120 api,
1121 SetResourcePreferenceEvent {
1122 resource_address,
1123 preference: resource_preference,
1124 },
1125 )?;
1126
1127 Ok(())
1128 }
1129
1130 pub fn remove_resource_preference<Y: SystemApi<RuntimeError>>(
1131 resource_address: ResourceAddress,
1132 api: &mut Y,
1133 ) -> Result<(), RuntimeError> {
1134 let encoded_key = scrypto_encode(&resource_address).expect("Impossible Case!");
1135 api.actor_remove_key_value_entry(
1136 ACTOR_STATE_SELF,
1137 AccountCollection::ResourcePreferenceKeyValue.collection_index(),
1138 &encoded_key,
1139 )?;
1140
1141 Runtime::emit_event(api, RemoveResourcePreferenceEvent { resource_address })?;
1142
1143 Ok(())
1144 }
1145
1146 pub fn add_authorized_depositor<Y: SystemApi<RuntimeError>>(
1147 badge: ResourceOrNonFungible,
1148 api: &mut Y,
1149 ) -> Result<(), RuntimeError> {
1150 let encoded_key =
1151 scrypto_encode(&badge).expect("Failed to SBOR encode a `ResourceOrNonFungible`.");
1152 let kv_store_entry_lock_handle = api.actor_open_key_value_entry(
1153 ACTOR_STATE_SELF,
1154 AccountCollection::AuthorizedDepositorKeyValue.collection_index(),
1155 &encoded_key,
1156 LockFlags::MUTABLE,
1157 )?;
1158 api.key_value_entry_set_typed(
1159 kv_store_entry_lock_handle,
1160 &AccountAuthorizedDepositorEntryPayload::from_content_source(()),
1161 )?;
1162 api.key_value_entry_close(kv_store_entry_lock_handle)?;
1163
1164 Runtime::emit_event(
1165 api,
1166 AddAuthorizedDepositorEvent {
1167 authorized_depositor_badge: badge,
1168 },
1169 )?;
1170
1171 Ok(())
1172 }
1173
1174 pub fn remove_authorized_depositor<Y: SystemApi<RuntimeError>>(
1175 badge: ResourceOrNonFungible,
1176 api: &mut Y,
1177 ) -> Result<(), RuntimeError> {
1178 let encoded_key =
1179 scrypto_encode(&badge).expect("Failed to SBOR encode a `ResourceOrNonFungible`.");
1180 api.actor_remove_key_value_entry(
1181 ACTOR_STATE_SELF,
1182 AccountCollection::AuthorizedDepositorKeyValue.collection_index(),
1183 &encoded_key,
1184 )?;
1185
1186 Runtime::emit_event(
1187 api,
1188 RemoveAuthorizedDepositorEvent {
1189 authorized_depositor_badge: badge,
1190 },
1191 )?;
1192
1193 Ok(())
1194 }
1195
1196 fn get_default_deposit_rule<Y: SystemApi<RuntimeError>>(
1197 api: &mut Y,
1198 ) -> Result<DefaultDepositRule, RuntimeError> {
1199 let handle = api.actor_open_field(
1200 ACTOR_STATE_SELF,
1201 AccountField::DepositRule.field_index(),
1202 LockFlags::read_only(),
1203 )?;
1204 let deposit_rule = api
1205 .field_read_typed::<AccountDepositRuleFieldPayload>(handle)?
1206 .fully_update_and_into_latest_version();
1207 let default = deposit_rule.default_deposit_rule;
1208 api.field_close(handle)?;
1209
1210 Ok(default)
1211 }
1212
1213 fn get_vault<Y: SystemApi<RuntimeError>, R>(
1214 resource_address: ResourceAddress,
1215 vault_fn: impl FnOnce(&mut Vault, &mut Y) -> Result<R, RuntimeError>,
1216 create: bool,
1217 api: &mut Y,
1218 ) -> Result<R, RuntimeError> {
1219 let encoded_key = scrypto_encode(&resource_address).expect("Impossible Case!");
1220
1221 let mut kv_store_entry_lock_handle = api.actor_open_key_value_entry(
1222 ACTOR_STATE_SELF,
1223 AccountCollection::ResourceVaultKeyValue.collection_index(),
1224 &encoded_key,
1225 LockFlags::read_only(),
1226 )?;
1227
1228 let vault = {
1231 let entry = api
1232 .key_value_entry_get_typed::<AccountResourceVaultEntryPayload>(
1233 kv_store_entry_lock_handle,
1234 )?
1235 .map(|v| v.fully_update_and_into_latest_version());
1236
1237 match entry {
1238 Some(vault) => Ok(vault),
1239 None => {
1240 if create {
1241 api.key_value_entry_close(kv_store_entry_lock_handle)?;
1242 kv_store_entry_lock_handle = api.actor_open_key_value_entry(
1243 ACTOR_STATE_SELF,
1244 AccountCollection::ResourceVaultKeyValue.collection_index(),
1245 &encoded_key,
1246 LockFlags::MUTABLE,
1247 )?;
1248 let vault = Vault::create(resource_address, api)?;
1249 let own = vault.0;
1250 api.key_value_entry_set_typed(
1251 kv_store_entry_lock_handle,
1252 &AccountResourceVaultEntryPayload::from_content_source(vault),
1253 )?;
1254 Ok(Vault(own))
1255 } else {
1256 Err(AccountError::VaultDoesNotExist { resource_address })
1257 }
1258 }
1259 }
1260 };
1261
1262 if let Ok(mut vault) = vault {
1263 match vault_fn(&mut vault, api) {
1264 Ok(rtn) => {
1265 api.key_value_entry_close(kv_store_entry_lock_handle)?;
1266 Ok(rtn)
1267 }
1268 Err(error) => Err(error),
1269 }
1270 } else {
1271 api.key_value_entry_close(kv_store_entry_lock_handle)?;
1272 Err(vault.unwrap_err().into())
1273 }
1274 }
1275
1276 fn is_deposit_allowed<Y: SystemApi<RuntimeError>>(
1277 resource_address: &ResourceAddress,
1278 api: &mut Y,
1279 ) -> Result<bool, RuntimeError> {
1280 match Self::get_resource_preference(resource_address, api)? {
1281 Some(ResourcePreference::Allowed) => Ok(true),
1282 Some(ResourcePreference::Disallowed) => Ok(false),
1283 None => {
1284 let default = Self::get_default_deposit_rule(api)?;
1285 match default {
1286 DefaultDepositRule::Accept => Ok(true),
1287 DefaultDepositRule::Reject => Ok(false),
1288 DefaultDepositRule::AllowExisting => {
1289 Ok(*resource_address == XRD
1290 || Self::does_vault_exist(resource_address, api)?)
1291 }
1292 }
1293 }
1294 }
1295 }
1296
1297 fn does_vault_exist<Y: SystemApi<RuntimeError>>(
1298 resource_address: &ResourceAddress,
1299 api: &mut Y,
1300 ) -> Result<bool, RuntimeError> {
1301 let encoded_key = scrypto_encode(resource_address).expect("Impossible Case!");
1302
1303 let kv_store_entry_lock_handle = api.actor_open_key_value_entry(
1304 ACTOR_STATE_SELF,
1305 AccountCollection::ResourceVaultKeyValue.collection_index(),
1306 &encoded_key,
1307 LockFlags::read_only(),
1308 )?;
1309
1310 let does_vault_exist = {
1311 let entry = api.key_value_entry_get_typed::<AccountResourceVaultEntryPayload>(
1312 kv_store_entry_lock_handle,
1313 )?;
1314 entry.is_some()
1315 };
1316
1317 api.key_value_entry_close(kv_store_entry_lock_handle)?;
1318
1319 Ok(does_vault_exist)
1320 }
1321
1322 fn get_resource_preference<Y: SystemApi<RuntimeError>>(
1323 resource_address: &ResourceAddress,
1324 api: &mut Y,
1325 ) -> Result<Option<ResourcePreference>, RuntimeError> {
1326 let encoded_key = scrypto_encode(&resource_address).expect("Impossible Case!");
1327
1328 let kv_store_entry_lock_handle = api.actor_open_key_value_entry(
1329 ACTOR_STATE_SELF,
1330 AccountCollection::ResourcePreferenceKeyValue.collection_index(),
1331 &encoded_key,
1332 LockFlags::read_only(),
1333 )?;
1334
1335 let entry = api
1336 .key_value_entry_get_typed::<AccountResourcePreferenceEntryPayload>(
1337 kv_store_entry_lock_handle,
1338 )?
1339 .map(|v| v.fully_update_and_into_latest_version());
1340 api.key_value_entry_close(kv_store_entry_lock_handle)?;
1341 Ok(entry)
1342 }
1343}
1344
1345#[derive(ScryptoSbor)]
1346pub struct AccountOwnerBadgeData {
1347 pub name: String,
1348 pub account: ComponentAddress,
1349}
1350
1351impl NonFungibleData for AccountOwnerBadgeData {
1352 const MUTABLE_FIELDS: &'static [&'static str] = &[];
1353}
1354
1355pub struct AccountBlueprintBottlenoseExtension;
1356
1357impl AccountBlueprintBottlenoseExtension {
1358 pub fn invoke_export<Y: SystemApi<RuntimeError>>(
1359 export_name: &str,
1360 input: &IndexedScryptoValue,
1361 api: &mut Y,
1362 ) -> Result<IndexedScryptoValue, RuntimeError> {
1363 match export_name {
1364 ACCOUNT_TRY_DEPOSIT_OR_REFUND_IDENT => {
1365 let AccountTryDepositOrRefundInput {
1366 bucket,
1367 authorized_depositor_badge,
1368 } = input.as_typed().map_err(|e| {
1369 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
1370 })?;
1371
1372 let rtn = Self::try_deposit_or_refund(bucket, authorized_depositor_badge, api)?;
1373 Ok(IndexedScryptoValue::from_typed(&rtn))
1374 }
1375 ACCOUNT_TRY_DEPOSIT_BATCH_OR_REFUND_IDENT => {
1376 let AccountTryDepositBatchOrRefundInput {
1377 buckets,
1378 authorized_depositor_badge,
1379 } = input.as_typed().map_err(|e| {
1380 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
1381 })?;
1382
1383 let rtn =
1384 Self::try_deposit_batch_or_refund(buckets, authorized_depositor_badge, api)?;
1385 Ok(IndexedScryptoValue::from_typed(&rtn))
1386 }
1387 _ => Err(RuntimeError::ApplicationError(
1388 ApplicationError::ExportDoesNotExist(export_name.to_string()),
1389 )),
1390 }
1391 }
1392
1393 pub fn try_deposit_or_refund<Y: SystemApi<RuntimeError>>(
1394 bucket: Bucket,
1395 authorized_depositor_badge: Option<ResourceOrNonFungible>,
1396 api: &mut Y,
1397 ) -> Result<Option<Bucket>, RuntimeError> {
1398 let resource_address = bucket.resource_address(api)?;
1399 let is_deposit_allowed = AccountBlueprint::is_deposit_allowed(&resource_address, api)?;
1400 if is_deposit_allowed {
1401 AccountBlueprint::deposit(bucket, api)?;
1402 Ok(None)
1403 } else if let Some(badge) = authorized_depositor_badge {
1404 match AccountBlueprint::validate_badge_is_authorized_depositor(&badge, api)? {
1405 Ok(_) => {}
1407 Err(AccountError::NotAnAuthorizedDepositor { .. }) => {
1410 let event = if resource_address.is_fungible() {
1411 RejectedDepositEvent::Fungible(resource_address, bucket.amount(api)?)
1412 } else {
1413 RejectedDepositEvent::NonFungible(
1414 resource_address,
1415 bucket.non_fungible_local_ids(api)?,
1416 )
1417 };
1418 Runtime::emit_event(api, event)?;
1419 return Ok(Some(bucket));
1420 }
1421 Err(error) => return Err(error.into()),
1424 }
1425 AccountBlueprint::validate_badge_is_present(badge, api)?;
1426 AccountBlueprint::deposit(bucket, api)?;
1427 Ok(None)
1428 } else {
1429 let event = if resource_address.is_fungible() {
1430 RejectedDepositEvent::Fungible(resource_address, bucket.amount(api)?)
1431 } else {
1432 RejectedDepositEvent::NonFungible(
1433 resource_address,
1434 bucket.non_fungible_local_ids(api)?,
1435 )
1436 };
1437 Runtime::emit_event(api, event)?;
1438 Ok(Some(bucket))
1439 }
1440 }
1441
1442 pub fn try_deposit_batch_or_refund<Y: SystemApi<RuntimeError>>(
1443 buckets: Vec<Bucket>,
1444 authorized_depositor_badge: Option<ResourceOrNonFungible>,
1445 api: &mut Y,
1446 ) -> Result<Option<Vec<Bucket>>, RuntimeError> {
1447 let offending_buckets = buckets
1448 .iter()
1449 .map(|bucket| {
1450 bucket
1451 .resource_address(api)
1452 .and_then(|resource_address| {
1453 AccountBlueprint::is_deposit_allowed(&resource_address, api)
1454 })
1455 .map(|can_be_deposited| (bucket, can_be_deposited))
1456 })
1457 .collect::<Result<Vec<_>, _>>()?
1458 .into_iter()
1459 .filter_map(|(bucket, can_be_deposited)| {
1460 if !can_be_deposited {
1461 Some(Bucket(bucket.0))
1462 } else {
1463 None
1464 }
1465 })
1466 .collect::<Vec<_>>();
1467
1468 if offending_buckets.is_empty() {
1469 AccountBlueprint::deposit_batch(buckets, api)?;
1470 Ok(None)
1471 } else if let Some(badge) = authorized_depositor_badge {
1472 match AccountBlueprint::validate_badge_is_authorized_depositor(&badge, api)? {
1473 Ok(_) => {}
1475 Err(AccountError::NotAnAuthorizedDepositor { .. }) => {
1478 for bucket in offending_buckets {
1479 let resource_address = bucket.resource_address(api)?;
1480 let event = if resource_address.is_fungible() {
1481 RejectedDepositEvent::Fungible(resource_address, bucket.amount(api)?)
1482 } else {
1483 RejectedDepositEvent::NonFungible(
1484 resource_address,
1485 bucket.non_fungible_local_ids(api)?,
1486 )
1487 };
1488 Runtime::emit_event(api, event)?;
1489 }
1490 return Ok(Some(buckets));
1491 }
1492 Err(error) => return Err(error.into()),
1495 }
1496 AccountBlueprint::validate_badge_is_present(badge, api)?;
1497 AccountBlueprint::deposit_batch(buckets, api)?;
1498 Ok(None)
1499 } else {
1500 for bucket in offending_buckets {
1501 let resource_address = bucket.resource_address(api)?;
1502 let event = if resource_address.is_fungible() {
1503 RejectedDepositEvent::Fungible(resource_address, bucket.amount(api)?)
1504 } else {
1505 RejectedDepositEvent::NonFungible(
1506 resource_address,
1507 bucket.non_fungible_local_ids(api)?,
1508 )
1509 };
1510 Runtime::emit_event(api, event)?;
1511 }
1512 Ok(Some(buckets))
1513 }
1514 }
1515}
1516
1517pub struct AccountBlueprintCuttlefishExtension;
1518
1519impl AccountBlueprintCuttlefishExtension {
1520 pub fn added_functions_schema() -> (
1521 IndexMap<String, FunctionSchemaInit>,
1522 VersionedSchema<ScryptoCustomSchema>,
1523 ) {
1524 let mut aggregator = TypeAggregator::<ScryptoCustomTypeKind>::new();
1525 let mut functions = index_map_new();
1526 functions.insert(
1527 ACCOUNT_BALANCE_IDENT.to_string(),
1528 FunctionSchemaInit {
1529 receiver: Some(ReceiverInfo::normal_ref()),
1530 input: TypeRef::Static(
1531 aggregator.add_child_type_and_descendents::<AccountBalanceInput>(),
1532 ),
1533 output: TypeRef::Static(
1534 aggregator.add_child_type_and_descendents::<AccountBalanceOutput>(),
1535 ),
1536 export: ACCOUNT_BALANCE_IDENT.to_string(),
1537 },
1538 );
1539
1540 functions.insert(
1541 ACCOUNT_NON_FUNGIBLE_LOCAL_IDS_IDENT.to_string(),
1542 FunctionSchemaInit {
1543 receiver: Some(ReceiverInfo::normal_ref()),
1544 input: TypeRef::Static(
1545 aggregator.add_child_type_and_descendents::<AccountNonFungibleLocalIdsInput>(),
1546 ),
1547 output: TypeRef::Static(
1548 aggregator.add_child_type_and_descendents::<AccountNonFungibleLocalIdsOutput>(),
1549 ),
1550 export: ACCOUNT_NON_FUNGIBLE_LOCAL_IDS_IDENT.to_string(),
1551 },
1552 );
1553
1554 functions.insert(
1555 ACCOUNT_HAS_NON_FUNGIBLE_IDENT.to_string(),
1556 FunctionSchemaInit {
1557 receiver: Some(ReceiverInfo::normal_ref()),
1558 input: TypeRef::Static(
1559 aggregator.add_child_type_and_descendents::<AccountHasNonFungibleInput>(),
1560 ),
1561 output: TypeRef::Static(
1562 aggregator.add_child_type_and_descendents::<AccountHasNonFungibleOutput>(),
1563 ),
1564 export: ACCOUNT_HAS_NON_FUNGIBLE_IDENT.to_string(),
1565 },
1566 );
1567 let schema = generate_full_schema(aggregator);
1568 (functions, schema)
1569 }
1570
1571 pub fn invoke_export<Y: SystemApi<RuntimeError>>(
1572 export_name: &str,
1573 input: &IndexedScryptoValue,
1574 api: &mut Y,
1575 ) -> Result<IndexedScryptoValue, RuntimeError> {
1576 match export_name {
1577 ACCOUNT_BALANCE_IDENT => {
1578 let AccountBalanceInput { resource_address } = input.as_typed().map_err(|e| {
1579 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
1580 })?;
1581
1582 let rtn = Self::balance(resource_address, api)?;
1583 Ok(IndexedScryptoValue::from_typed(&rtn))
1584 }
1585 ACCOUNT_NON_FUNGIBLE_LOCAL_IDS_IDENT => {
1586 let AccountNonFungibleLocalIdsInput {
1587 resource_address,
1588 limit,
1589 } = input.as_typed().map_err(|e| {
1590 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
1591 })?;
1592
1593 let rtn = Self::non_fungible_local_ids(resource_address, limit, api)?;
1594 Ok(IndexedScryptoValue::from_typed(&rtn))
1595 }
1596 ACCOUNT_HAS_NON_FUNGIBLE_IDENT => {
1597 let AccountHasNonFungibleInput {
1598 resource_address,
1599 local_id,
1600 } = input.as_typed().map_err(|e| {
1601 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
1602 })?;
1603
1604 let rtn = Self::has_non_fungible(resource_address, local_id, api)?;
1605 Ok(IndexedScryptoValue::from_typed(&rtn))
1606 }
1607 _ => Err(RuntimeError::ApplicationError(
1608 ApplicationError::ExportDoesNotExist(export_name.to_string()),
1609 )),
1610 }
1611 }
1612
1613 pub fn balance<Y: SystemApi<RuntimeError>>(
1614 resource_address: ResourceAddress,
1615 api: &mut Y,
1616 ) -> Result<Decimal, RuntimeError> {
1617 match AccountBlueprint::get_vault(
1618 resource_address,
1619 |vault, api| vault.amount(api),
1620 false,
1621 api,
1622 ) {
1623 Ok(balance) => Ok(balance),
1624 Err(RuntimeError::ApplicationError(ApplicationError::AccountError(
1625 AccountError::VaultDoesNotExist { .. },
1626 ))) => Ok(Decimal::ZERO),
1627 Err(error) => Err(error),
1628 }
1629 }
1630
1631 pub fn non_fungible_local_ids<Y: SystemApi<RuntimeError>>(
1632 resource_address: ResourceAddress,
1633 limit: u32,
1634 api: &mut Y,
1635 ) -> Result<IndexSet<NonFungibleLocalId>, RuntimeError> {
1636 match AccountBlueprint::get_vault(
1637 resource_address,
1638 |vault, api| vault.non_fungible_local_ids(limit, api),
1639 false,
1640 api,
1641 ) {
1642 Ok(ids) => Ok(ids),
1643 Err(RuntimeError::ApplicationError(ApplicationError::AccountError(
1644 AccountError::VaultDoesNotExist { .. },
1645 ))) => Ok(Default::default()),
1646 Err(error) => Err(error),
1647 }
1648 }
1649
1650 pub fn has_non_fungible<Y: SystemApi<RuntimeError>>(
1651 resource_address: ResourceAddress,
1652 local_id: NonFungibleLocalId,
1653 api: &mut Y,
1654 ) -> Result<bool, RuntimeError> {
1655 match AccountBlueprint::get_vault(
1656 resource_address,
1657 |vault, api| vault.contains_non_fungible(local_id, api),
1658 false,
1659 api,
1660 ) {
1661 Ok(result) => Ok(result),
1662 Err(RuntimeError::ApplicationError(ApplicationError::AccountError(
1663 AccountError::VaultDoesNotExist { .. },
1664 ))) => Ok(false),
1665 Err(error) => Err(error),
1666 }
1667 }
1668}