radix_engine/blueprints/resource/fungible/
fungible_proof.rs1use 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 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 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 if proof.restricted {
176 return Err(RuntimeError::ApplicationError(
177 ApplicationError::PanicMessage(
178 "Moving restricted proof downstream".to_owned(),
179 ),
180 ));
181 }
182
183 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 Ok(())
194 }
195 } else {
196 Ok(())
198 }
199 }
200}