scrypto/resource/
vault.rs

1use crate::resource::*;
2use crate::*;
3use radix_common::data::scrypto::model::*;
4use radix_common::data::scrypto::{scrypto_decode, scrypto_encode};
5use radix_common::math::Decimal;
6use radix_common::traits::NonFungibleData;
7use radix_engine_interface::blueprints::resource::*;
8use radix_engine_interface::types::*;
9use runtime::LocalAuthZone;
10use sbor::rust::prelude::*;
11use scrypto::engine::scrypto_env::ScryptoVmV1Api;
12
13//========
14// Traits
15//========
16
17pub trait ScryptoVault {
18    type BucketType;
19    type ResourceManagerType;
20
21    fn with_bucket(bucket: Self::BucketType) -> Self;
22
23    fn new(resource_address: ResourceAddress) -> Self;
24
25    fn put(&mut self, bucket: Self::BucketType) -> ();
26
27    fn amount(&self) -> Decimal;
28
29    fn resource_address(&self) -> ResourceAddress;
30
31    fn resource_manager(&self) -> Self::ResourceManagerType;
32
33    fn is_empty(&self) -> bool;
34
35    fn take<A: Into<Decimal>>(&mut self, amount: A) -> Self::BucketType;
36
37    fn take_all(&mut self) -> Self::BucketType;
38
39    fn take_advanced<A: Into<Decimal>>(
40        &mut self,
41        amount: A,
42        withdraw_strategy: WithdrawStrategy,
43    ) -> Self::BucketType;
44
45    fn burn<A: Into<Decimal>>(&mut self, amount: A);
46}
47
48pub trait ScryptoGenericVault {
49    fn as_fungible(&self) -> FungibleVault;
50
51    fn as_non_fungible(&self) -> NonFungibleVault;
52}
53
54pub trait ScryptoFungibleVault {
55    fn lock_fee<A: Into<Decimal>>(&mut self, amount: A);
56
57    fn lock_contingent_fee<A: Into<Decimal>>(&mut self, amount: A);
58
59    fn create_proof_of_amount<A: Into<Decimal>>(&self, amount: A) -> FungibleProof;
60
61    fn authorize_with_amount<A: Into<Decimal>, F: FnOnce() -> O, O>(&self, amount: A, f: F) -> O;
62}
63
64pub trait ScryptoNonFungibleVault {
65    fn non_fungible_local_ids(&self, limit: u32) -> IndexSet<NonFungibleLocalId>;
66
67    fn contains_non_fungible(&self, id: &NonFungibleLocalId) -> bool;
68
69    fn non_fungibles<T: NonFungibleData>(&self, limit: u32) -> Vec<NonFungible<T>>;
70
71    fn non_fungible_local_id(&self) -> NonFungibleLocalId;
72
73    fn non_fungible_global_id(&self) -> NonFungibleGlobalId;
74
75    fn non_fungible<T: NonFungibleData>(&self) -> NonFungible<T>;
76
77    fn take_non_fungible(
78        &mut self,
79        non_fungible_local_id: &NonFungibleLocalId,
80    ) -> NonFungibleBucket;
81
82    fn take_non_fungibles(
83        &mut self,
84        non_fungible_local_ids: &IndexSet<NonFungibleLocalId>,
85    ) -> NonFungibleBucket;
86
87    fn burn_non_fungibles(&mut self, non_fungible_local_ids: &IndexSet<NonFungibleLocalId>);
88
89    fn create_proof_of_non_fungibles(
90        &self,
91        non_fungible_local_ids: &IndexSet<NonFungibleLocalId>,
92    ) -> NonFungibleProof;
93
94    fn authorize_with_non_fungibles<F: FnOnce() -> O, O>(
95        &self,
96        non_fungible_local_ids: &IndexSet<NonFungibleLocalId>,
97        f: F,
98    ) -> O;
99}
100
101//===========
102// Any vault
103//===========
104
105impl ScryptoVault for Vault {
106    type BucketType = Bucket;
107    type ResourceManagerType = ResourceManager;
108
109    /// Creates an empty vault and fills it with an initial bucket of resource.
110    fn with_bucket(bucket: Self::BucketType) -> Self {
111        let mut vault = Vault::new(bucket.resource_address());
112        vault.put(bucket);
113        vault
114    }
115
116    fn new(resource_address: ResourceAddress) -> Self {
117        let rtn = ScryptoVmV1Api::object_call(
118            resource_address.as_node_id(),
119            RESOURCE_MANAGER_CREATE_EMPTY_VAULT_IDENT,
120            scrypto_encode(&ResourceManagerCreateEmptyVaultInput {}).unwrap(),
121        );
122        scrypto_decode(&rtn).unwrap()
123    }
124
125    fn put(&mut self, bucket: Self::BucketType) -> () {
126        let rtn = ScryptoVmV1Api::object_call(
127            self.0.as_node_id(),
128            VAULT_PUT_IDENT,
129            scrypto_encode(&VaultPutInput { bucket }).unwrap(),
130        );
131        scrypto_decode(&rtn).unwrap()
132    }
133
134    fn amount(&self) -> Decimal {
135        let rtn = ScryptoVmV1Api::object_call(
136            self.0.as_node_id(),
137            VAULT_GET_AMOUNT_IDENT,
138            scrypto_encode(&VaultGetAmountInput {}).unwrap(),
139        );
140        scrypto_decode(&rtn).unwrap()
141    }
142
143    fn resource_address(&self) -> ResourceAddress {
144        let address = ScryptoVmV1Api::object_get_outer_object(self.0.as_node_id());
145        ResourceAddress::try_from(address).unwrap()
146    }
147
148    fn resource_manager(&self) -> Self::ResourceManagerType {
149        self.resource_address().into()
150    }
151
152    /// Takes some amount of resource from this vault into a bucket.
153    fn take<A: Into<Decimal>>(&mut self, amount: A) -> Self::BucketType {
154        let rtn = ScryptoVmV1Api::object_call(
155            self.0.as_node_id(),
156            VAULT_TAKE_IDENT,
157            scrypto_encode(&VaultTakeInput {
158                amount: amount.into(),
159            })
160            .unwrap(),
161        );
162        scrypto_decode(&rtn).unwrap()
163    }
164
165    /// Takes all resource stored in this vault.
166    fn take_all(&mut self) -> Self::BucketType {
167        self.take(self.amount())
168    }
169
170    fn take_advanced<A: Into<Decimal>>(
171        &mut self,
172        amount: A,
173        withdraw_strategy: WithdrawStrategy,
174    ) -> Self::BucketType {
175        let rtn = ScryptoVmV1Api::object_call(
176            self.0.as_node_id(),
177            VAULT_TAKE_ADVANCED_IDENT,
178            scrypto_encode(&VaultTakeAdvancedInput {
179                amount: amount.into(),
180                withdraw_strategy,
181            })
182            .unwrap(),
183        );
184        scrypto_decode(&rtn).unwrap()
185    }
186
187    /// Checks if this vault is empty.
188    fn is_empty(&self) -> bool {
189        self.amount() == 0.into()
190    }
191
192    fn burn<A: Into<Decimal>>(&mut self, amount: A) {
193        let rtn = ScryptoVmV1Api::object_call(
194            self.0.as_node_id(),
195            VAULT_BURN_IDENT,
196            scrypto_encode(&VaultBurnInput {
197                amount: amount.into(),
198            })
199            .unwrap(),
200        );
201        scrypto_decode(&rtn).unwrap()
202    }
203}
204
205impl ScryptoGenericVault for Vault {
206    fn as_fungible(&self) -> FungibleVault {
207        assert!(
208            self.0.as_node_id().is_internal_fungible_vault(),
209            "Not a fungible vault"
210        );
211        FungibleVault(Self(self.0))
212    }
213
214    fn as_non_fungible(&self) -> NonFungibleVault {
215        assert!(
216            self.0.as_node_id().is_internal_non_fungible_vault(),
217            "Not a non-fungible vault"
218        );
219        NonFungibleVault(Self(self.0))
220    }
221}
222
223//================
224// Fungible vault
225//================
226
227impl ScryptoVault for FungibleVault {
228    type BucketType = FungibleBucket;
229    type ResourceManagerType = FungibleResourceManager;
230
231    fn with_bucket(bucket: Self::BucketType) -> Self {
232        Self(Vault::with_bucket(bucket.0))
233    }
234
235    fn new(resource_address: ResourceAddress) -> Self {
236        assert!(resource_address
237            .as_node_id()
238            .is_global_fungible_resource_manager());
239        Self(Vault::new(resource_address))
240    }
241
242    fn put(&mut self, bucket: Self::BucketType) -> () {
243        self.0.put(bucket.0)
244    }
245
246    fn amount(&self) -> Decimal {
247        self.0.amount()
248    }
249
250    fn resource_address(&self) -> ResourceAddress {
251        self.0.resource_address()
252    }
253
254    fn resource_manager(&self) -> Self::ResourceManagerType {
255        self.resource_address().into()
256    }
257
258    fn is_empty(&self) -> bool {
259        self.0.is_empty()
260    }
261
262    fn take<A: Into<Decimal>>(&mut self, amount: A) -> Self::BucketType {
263        FungibleBucket(self.0.take(amount))
264    }
265
266    fn take_all(&mut self) -> Self::BucketType {
267        FungibleBucket(self.0.take_all())
268    }
269
270    fn take_advanced<A: Into<Decimal>>(
271        &mut self,
272        amount: A,
273        withdraw_strategy: WithdrawStrategy,
274    ) -> Self::BucketType {
275        FungibleBucket(self.0.take_advanced(amount, withdraw_strategy))
276    }
277
278    fn burn<A: Into<Decimal>>(&mut self, amount: A) {
279        self.0.burn(amount)
280    }
281}
282
283impl ScryptoFungibleVault for FungibleVault {
284    /// Locks the specified amount as transaction fee.
285    ///
286    /// Unused fee will be refunded to the vaults from the most recently locked to the least.
287    fn lock_fee<A: Into<Decimal>>(&mut self, amount: A) {
288        let _rtn = ScryptoVmV1Api::object_call(
289            self.0 .0.as_node_id(),
290            FUNGIBLE_VAULT_LOCK_FEE_IDENT,
291            scrypto_encode(&FungibleVaultLockFeeInput {
292                amount: amount.into(),
293                contingent: false,
294            })
295            .unwrap(),
296        );
297    }
298
299    /// Locks the given amount of resource as contingent fee.
300    ///
301    /// The locked amount will be used as transaction only if the transaction succeeds;
302    /// Unused amount will be refunded the original vault.
303    fn lock_contingent_fee<A: Into<Decimal>>(&mut self, amount: A) {
304        let _rtn = ScryptoVmV1Api::object_call(
305            self.0 .0.as_node_id(),
306            FUNGIBLE_VAULT_LOCK_FEE_IDENT,
307            scrypto_encode(&FungibleVaultLockFeeInput {
308                amount: amount.into(),
309                contingent: true,
310            })
311            .unwrap(),
312        );
313    }
314
315    fn create_proof_of_amount<A: Into<Decimal>>(&self, amount: A) -> FungibleProof {
316        let rtn = ScryptoVmV1Api::object_call(
317            self.0 .0.as_node_id(),
318            FUNGIBLE_VAULT_CREATE_PROOF_OF_AMOUNT_IDENT,
319            scrypto_encode(&FungibleVaultCreateProofOfAmountInput {
320                amount: amount.into(),
321            })
322            .unwrap(),
323        );
324        scrypto_decode(&rtn).unwrap()
325    }
326
327    fn authorize_with_amount<A: Into<Decimal>, F: FnOnce() -> O, O>(&self, amount: A, f: F) -> O {
328        LocalAuthZone::push(self.create_proof_of_amount(amount));
329        let output = f();
330        LocalAuthZone::pop()
331            .expect("Authorized closure changed auth zone proof stack")
332            .drop();
333        output
334    }
335}
336
337//====================
338// Non-fungible vault
339//====================
340
341impl ScryptoVault for NonFungibleVault {
342    type BucketType = NonFungibleBucket;
343    type ResourceManagerType = NonFungibleResourceManager;
344
345    fn with_bucket(bucket: Self::BucketType) -> Self {
346        Self(Vault::with_bucket(bucket.0))
347    }
348
349    fn new(resource_address: ResourceAddress) -> Self {
350        assert!(resource_address
351            .as_node_id()
352            .is_global_non_fungible_resource_manager());
353        Self(Vault::new(resource_address))
354    }
355
356    fn put(&mut self, bucket: Self::BucketType) -> () {
357        self.0.put(bucket.0)
358    }
359
360    fn amount(&self) -> Decimal {
361        self.0.amount()
362    }
363
364    fn resource_address(&self) -> ResourceAddress {
365        self.0.resource_address()
366    }
367
368    fn resource_manager(&self) -> Self::ResourceManagerType {
369        self.resource_address().into()
370    }
371
372    fn is_empty(&self) -> bool {
373        self.0.is_empty()
374    }
375
376    fn take<A: Into<Decimal>>(&mut self, amount: A) -> Self::BucketType {
377        NonFungibleBucket(self.0.take(amount))
378    }
379
380    fn take_all(&mut self) -> Self::BucketType {
381        NonFungibleBucket(self.0.take_all())
382    }
383
384    fn take_advanced<A: Into<Decimal>>(
385        &mut self,
386        amount: A,
387        withdraw_strategy: WithdrawStrategy,
388    ) -> Self::BucketType {
389        NonFungibleBucket(self.0.take_advanced(amount, withdraw_strategy))
390    }
391
392    fn burn<A: Into<Decimal>>(&mut self, amount: A) {
393        self.0.burn(amount)
394    }
395}
396
397impl ScryptoNonFungibleVault for NonFungibleVault {
398    fn non_fungible_local_ids(&self, limit: u32) -> IndexSet<NonFungibleLocalId> {
399        let rtn = ScryptoVmV1Api::object_call(
400            self.0 .0.as_node_id(),
401            NON_FUNGIBLE_VAULT_GET_NON_FUNGIBLE_LOCAL_IDS_IDENT,
402            scrypto_encode(&NonFungibleVaultGetNonFungibleLocalIdsInput { limit }).unwrap(),
403        );
404        scrypto_decode(&rtn).unwrap()
405    }
406
407    fn contains_non_fungible(&self, id: &NonFungibleLocalId) -> bool {
408        let rtn = ScryptoVmV1Api::object_call(
409            self.0 .0.as_node_id(),
410            NON_FUNGIBLE_VAULT_CONTAINS_NON_FUNGIBLE_IDENT,
411            scrypto_encode(&NonFungibleVaultContainsNonFungibleInput { id: id.clone() }).unwrap(),
412        );
413        scrypto_decode(&rtn).unwrap()
414    }
415
416    /// Returns all the non-fungible units contained.
417    ///
418    /// # Panics
419    /// Panics if this is not a non-fungible vault.
420    fn non_fungibles<T: NonFungibleData>(&self, limit: u32) -> Vec<NonFungible<T>> {
421        let resource_address = self.0.resource_address();
422        self.non_fungible_local_ids(limit)
423            .iter()
424            .map(|id| NonFungible::from(NonFungibleGlobalId::new(resource_address, id.clone())))
425            .collect()
426    }
427
428    /// Returns the non-fungible local id if this is a singleton non-fungible vault.
429    ///
430    /// # Panics
431    /// Panics if this is not a singleton vault
432    fn non_fungible_local_id(&self) -> NonFungibleLocalId {
433        let non_fungible_local_ids = self.non_fungible_local_ids(2);
434        if non_fungible_local_ids.len() != 1 {
435            panic!("Expecting singleton NFT vault");
436        }
437        non_fungible_local_ids.into_iter().next().unwrap()
438    }
439
440    /// Returns the non-fungible global id if this is a singleton non-fungible vault.
441    ///
442    /// # Panics
443    /// Panics if this is not a singleton vault
444    fn non_fungible_global_id(&self) -> NonFungibleGlobalId {
445        NonFungibleGlobalId::new(self.resource_address(), self.non_fungible_local_id())
446    }
447
448    /// Returns a singleton non-fungible.
449    ///
450    /// # Panics
451    /// Panics if this is not a singleton bucket
452    fn non_fungible<T: NonFungibleData>(&self) -> NonFungible<T> {
453        // Use limit of 2 in order to verify singleton
454        let non_fungibles = self.non_fungibles(2);
455        if non_fungibles.len() != 1 {
456            panic!("Expecting singleton NFT vault");
457        }
458        non_fungibles.into_iter().next().unwrap()
459    }
460
461    /// Takes a specific non-fungible from this vault.
462    ///
463    /// # Panics
464    /// Panics if this is not a non-fungible vault or the specified non-fungible resource is not found.
465    fn take_non_fungible(
466        &mut self,
467        non_fungible_local_id: &NonFungibleLocalId,
468    ) -> NonFungibleBucket {
469        self.take_non_fungibles(&indexset!(non_fungible_local_id.clone()))
470    }
471
472    fn take_non_fungibles(
473        &mut self,
474        non_fungible_local_ids: &IndexSet<NonFungibleLocalId>,
475    ) -> NonFungibleBucket {
476        let rtn = ScryptoVmV1Api::object_call(
477            self.0 .0.as_node_id(),
478            NON_FUNGIBLE_VAULT_TAKE_NON_FUNGIBLES_IDENT,
479            scrypto_encode(&NonFungibleVaultTakeNonFungiblesInput {
480                non_fungible_local_ids: non_fungible_local_ids.clone(),
481            })
482            .unwrap(),
483        );
484        scrypto_decode(&rtn).unwrap()
485    }
486
487    fn create_proof_of_non_fungibles(
488        &self,
489        ids: &IndexSet<NonFungibleLocalId>,
490    ) -> NonFungibleProof {
491        let rtn = ScryptoVmV1Api::object_call(
492            self.0 .0.as_node_id(),
493            NON_FUNGIBLE_VAULT_CREATE_PROOF_OF_NON_FUNGIBLES_IDENT,
494            scrypto_encode(&NonFungibleVaultCreateProofOfNonFungiblesInput { ids: ids.clone() })
495                .unwrap(),
496        );
497        scrypto_decode(&rtn).unwrap()
498    }
499
500    fn burn_non_fungibles(&mut self, non_fungible_local_ids: &IndexSet<NonFungibleLocalId>) {
501        let rtn = ScryptoVmV1Api::object_call(
502            self.0 .0.as_node_id(),
503            NON_FUNGIBLE_VAULT_BURN_NON_FUNGIBLES_IDENT,
504            scrypto_encode(&NonFungibleVaultBurnNonFungiblesInput {
505                non_fungible_local_ids: non_fungible_local_ids.clone(),
506            })
507            .unwrap(),
508        );
509        scrypto_decode(&rtn).unwrap()
510    }
511
512    fn authorize_with_non_fungibles<F: FnOnce() -> O, O>(
513        &self,
514        non_fungible_local_ids: &IndexSet<NonFungibleLocalId>,
515        f: F,
516    ) -> O {
517        LocalAuthZone::push(self.create_proof_of_non_fungibles(non_fungible_local_ids));
518        let output = f();
519        LocalAuthZone::pop()
520            .expect("Authorized closure changed auth zone proof stack")
521            .drop();
522        output
523    }
524}