radix_engine/blueprints/resource/fungible/
fungible_proof.rs

1use crate::blueprints::resource::{LocalRef, ProofError, ProofMoveableSubstate};
2use crate::errors::{ApplicationError, RuntimeError};
3use crate::internal_prelude::*;
4use radix_engine_interface::api::field_api::LockFlags;
5use radix_engine_interface::api::{FieldValue, SystemApi, ACTOR_REF_OUTER, ACTOR_STATE_SELF};
6use radix_engine_interface::blueprints::resource::*;
7
8#[derive(Debug, Clone, ScryptoSbor)]
9pub struct FungibleProofSubstate {
10    pub total_locked: Decimal,
11    /// The supporting containers.
12    pub evidence: IndexMap<LocalRef, Decimal>,
13}
14
15impl FungibleProofSubstate {
16    pub fn new(
17        total_locked: Decimal,
18        evidence: IndexMap<LocalRef, Decimal>,
19    ) -> Result<FungibleProofSubstate, ProofError> {
20        if total_locked.is_zero() {
21            return Err(ProofError::EmptyProofNotAllowed);
22        }
23
24        Ok(Self {
25            total_locked,
26            evidence,
27        })
28    }
29
30    pub fn clone_proof<Y: SystemApi<RuntimeError>>(
31        &self,
32        api: &mut Y,
33    ) -> Result<Self, RuntimeError> {
34        for (container, locked_amount) in &self.evidence {
35            api.call_method(
36                container.as_node_id(),
37                match container {
38                    LocalRef::Bucket(_) => FUNGIBLE_BUCKET_LOCK_AMOUNT_IDENT,
39                    LocalRef::Vault(_) => FUNGIBLE_VAULT_LOCK_FUNGIBLE_AMOUNT_IDENT,
40                },
41                scrypto_args!(locked_amount),
42            )?;
43        }
44        Ok(Self {
45            total_locked: self.total_locked,
46            evidence: self.evidence.clone(),
47        })
48    }
49
50    pub fn teardown<Y: SystemApi<RuntimeError>>(self, api: &mut Y) -> Result<(), RuntimeError> {
51        for (container, locked_amount) in &self.evidence {
52            api.call_method(
53                container.as_node_id(),
54                match container {
55                    LocalRef::Bucket(_) => FUNGIBLE_BUCKET_UNLOCK_AMOUNT_IDENT,
56                    LocalRef::Vault(_) => FUNGIBLE_VAULT_UNLOCK_FUNGIBLE_AMOUNT_IDENT,
57                },
58                scrypto_args!(locked_amount),
59            )?;
60        }
61        Ok(())
62    }
63
64    pub fn amount(&self) -> Decimal {
65        self.total_locked
66    }
67}
68
69pub struct FungibleProofBlueprint;
70
71impl FungibleProofBlueprint {
72    pub(crate) fn clone<Y: SystemApi<RuntimeError>>(api: &mut Y) -> Result<Proof, RuntimeError> {
73        let moveable = {
74            let handle = api.actor_open_field(
75                ACTOR_STATE_SELF,
76                FungibleProofField::Moveable.into(),
77                LockFlags::read_only(),
78            )?;
79            let substate_ref: ProofMoveableSubstate = api.field_read_typed(handle)?;
80            let moveable = substate_ref;
81            api.field_close(handle)?;
82            moveable
83        };
84
85        let handle = api.actor_open_field(
86            ACTOR_STATE_SELF,
87            FungibleProofField::ProofRefs.into(),
88            LockFlags::read_only(),
89        )?;
90        let substate_ref: FungibleProofSubstate = api.field_read_typed(handle)?;
91        let proof = substate_ref.clone();
92        let clone = proof.clone_proof(api)?;
93
94        let proof_id = api.new_simple_object(
95            FUNGIBLE_PROOF_BLUEPRINT,
96            indexmap! {
97                FungibleProofField::Moveable.field_index() => FieldValue::new(moveable),
98                FungibleProofField::ProofRefs.field_index() => FieldValue::new(clone),
99            },
100        )?;
101
102        // Drop after object creation to keep the reference alive
103        api.field_close(handle)?;
104
105        Ok(Proof(Own(proof_id)))
106    }
107
108    pub(crate) fn get_amount<Y: SystemApi<RuntimeError>>(
109        api: &mut Y,
110    ) -> Result<Decimal, RuntimeError> {
111        let handle = api.actor_open_field(
112            ACTOR_STATE_SELF,
113            FungibleProofField::ProofRefs.into(),
114            LockFlags::read_only(),
115        )?;
116        let substate_ref: FungibleProofSubstate = api.field_read_typed(handle)?;
117        let amount = substate_ref.amount();
118        api.field_close(handle)?;
119        Ok(amount)
120    }
121
122    pub(crate) fn get_resource_address<Y: SystemApi<RuntimeError>>(
123        api: &mut Y,
124    ) -> Result<ResourceAddress, RuntimeError> {
125        let address = ResourceAddress::new_or_panic(api.actor_get_node_id(ACTOR_REF_OUTER)?.into());
126        Ok(address)
127    }
128
129    pub(crate) fn drop<Y: SystemApi<RuntimeError>>(
130        proof: Proof,
131        api: &mut Y,
132    ) -> Result<(), RuntimeError> {
133        api.drop_object(proof.0.as_node_id())?;
134
135        Ok(())
136    }
137
138    pub(crate) fn on_drop<Y: SystemApi<RuntimeError>>(api: &mut Y) -> Result<(), RuntimeError> {
139        let handle = api.actor_open_field(
140            ACTOR_STATE_SELF,
141            FungibleProofField::ProofRefs.into(),
142            LockFlags::MUTABLE,
143        )?;
144        let proof_substate: FungibleProofSubstate = api.field_read_typed(handle)?;
145        proof_substate.teardown(api)?;
146        api.field_close(handle)?;
147
148        Ok(())
149    }
150
151    pub(crate) fn on_move<Y: SystemApi<RuntimeError>>(
152        is_moving_down: bool,
153        is_to_barrier: bool,
154        destination_blueprint_id: Option<BlueprintId>,
155        api: &mut Y,
156    ) -> Result<(), RuntimeError> {
157        if is_moving_down {
158            let is_to_self = destination_blueprint_id.eq(&Some(BlueprintId::new(
159                &RESOURCE_PACKAGE,
160                FUNGIBLE_PROOF_BLUEPRINT,
161            )));
162            let is_to_auth_zone = destination_blueprint_id.eq(&Some(BlueprintId::new(
163                &RESOURCE_PACKAGE,
164                AUTH_ZONE_BLUEPRINT,
165            )));
166            if !is_to_self && (is_to_barrier || is_to_auth_zone) {
167                let handle = api.actor_open_field(
168                    ACTOR_STATE_SELF,
169                    FungibleProofField::Moveable.into(),
170                    LockFlags::MUTABLE,
171                )?;
172                let mut proof: ProofMoveableSubstate = api.field_read_typed(handle)?;
173
174                // Check if the proof is restricted
175                if proof.restricted {
176                    return Err(RuntimeError::ApplicationError(
177                        ApplicationError::PanicMessage(
178                            "Moving restricted proof downstream".to_owned(),
179                        ),
180                    ));
181                }
182
183                // Update restricted flag
184                if is_to_barrier {
185                    proof.change_to_restricted();
186                }
187
188                api.field_write_typed(handle, &proof)?;
189                api.field_close(handle)?;
190                Ok(())
191            } else {
192                // Proofs can move freely as long as it's not to a barrier or auth zone.
193                Ok(())
194            }
195        } else {
196            // No restriction for moving up
197            Ok(())
198        }
199    }
200}