partner_chains_plutus_data/
reserve.rs1use crate::{
3 DataDecodingError, DecodingResult, VersionedDatum, VersionedGenericDatum,
4 decoding_error_and_log, plutus_data_version_and_payload,
5};
6use cardano_serialization_lib::{BigInt, BigNum, ConstrPlutusData, PlutusData, PlutusList};
7use sidechain_domain::{AssetId, AssetName, PolicyId};
8
9#[derive(Debug, Clone)]
10pub enum ReserveRedeemer {
12 DepositToReserve = 0,
14 ReleaseFromReserve = 1,
16 UpdateReserve = 2,
18 Handover = 3,
20}
21
22#[derive(Debug, Clone, PartialEq)]
23pub struct ReserveDatum {
25 pub immutable_settings: ReserveImmutableSettings,
27 pub mutable_settings: ReserveMutableSettings,
29 pub stats: ReserveStats,
31}
32
33#[derive(Debug, Clone, PartialEq)]
34pub struct ReserveImmutableSettings {
36 pub t0: u64,
38 pub token: AssetId,
40}
41
42#[derive(Debug, Clone, PartialEq)]
43pub struct ReserveMutableSettings {
45 pub total_accrued_function_asset_name: PolicyId,
50 pub initial_incentive: u64,
52}
53
54#[derive(Debug, Clone, PartialEq)]
55pub struct ReserveStats {
57 pub token_total_amount_transferred: u64,
59}
60
61impl From<ReserveRedeemer> for PlutusData {
62 fn from(value: ReserveRedeemer) -> Self {
63 PlutusData::new_empty_constr_plutus_data(&BigNum::from(value as u64))
64 }
65}
66
67impl From<ReserveDatum> for PlutusData {
68 fn from(value: ReserveDatum) -> Self {
69 VersionedGenericDatum {
70 datum: {
71 let mut immutable_settings = PlutusList::new();
72 let t0 = PlutusData::new_integer(&BigInt::zero());
73 immutable_settings.add(&t0);
74 let (policy_id_bytes, asset_name_bytes) = {
75 let AssetId { policy_id, asset_name } = value.immutable_settings.token.clone();
76 (policy_id.0.to_vec(), asset_name.0.to_vec())
77 };
78 let token_data: PlutusData = {
79 let mut asset_data = PlutusList::new();
80 asset_data.add(&PlutusData::new_bytes(policy_id_bytes));
81 asset_data.add(&PlutusData::new_bytes(asset_name_bytes));
82 PlutusData::new_constr_plutus_data(&ConstrPlutusData::new(
83 &BigNum::zero(),
84 &asset_data,
85 ))
86 };
87 immutable_settings.add(&token_data);
88
89 let mut v_function_hash_and_initial_incentive = PlutusList::new();
90 v_function_hash_and_initial_incentive.add(&PlutusData::new_bytes(
91 value.mutable_settings.total_accrued_function_asset_name.0.to_vec(),
92 ));
93 v_function_hash_and_initial_incentive.add(&PlutusData::new_integer(&BigInt::from(
94 value.mutable_settings.initial_incentive,
95 )));
96
97 let mut datum = PlutusList::new();
98 datum.add(&PlutusData::new_list(&immutable_settings));
99 datum.add(&PlutusData::new_list(&v_function_hash_and_initial_incentive));
100 datum.add(&PlutusData::new_integer(
101 &value.stats.token_total_amount_transferred.into(),
102 ));
103 PlutusData::new_list(&datum)
104 },
105 appendix: PlutusData::new_empty_constr_plutus_data(&BigNum::zero()),
107 version: 0,
108 }
109 .into()
110 }
111}
112
113impl TryFrom<PlutusData> for ReserveDatum {
114 type Error = DataDecodingError;
115
116 fn try_from(datum: PlutusData) -> DecodingResult<Self> {
117 Self::decode(&datum)
118 }
119}
120
121impl VersionedDatum for ReserveDatum {
122 const NAME: &str = "ReserveDatum";
123
124 fn decode(datum: &PlutusData) -> DecodingResult<Self> {
125 match plutus_data_version_and_payload(datum) {
126 Some(VersionedGenericDatum { version: 0, datum, .. }) => {
127 decode_v0_reserve_datum(&datum)
128 .ok_or_else(|| decoding_error_and_log(&datum, "ReserveDatum", "invalid data"))
129 },
130 _ => Err(decoding_error_and_log(datum, "ReserveDatum", "unversioned datum")),
131 }
132 }
133}
134
135impl ReserveDatum {
136 pub fn after_withdrawal(self, amount: u64) -> Self {
138 Self {
139 stats: ReserveStats {
140 token_total_amount_transferred: self.stats.token_total_amount_transferred + amount,
141 },
142 ..self
143 }
144 }
145}
146
147fn decode_v0_reserve_datum(datum: &PlutusData) -> Option<ReserveDatum> {
148 let outer_list = datum.as_list()?;
149 let mut outer_iter = outer_list.into_iter();
150
151 let immutable_settings_list = outer_iter.next()?.as_list()?;
152 let mut immutable_settings_iter = immutable_settings_list.into_iter();
153 let t0: u64 = immutable_settings_iter.next()?.as_integer()?.as_u64()?.into();
154 let token = decode_token_id_datum(immutable_settings_iter.next()?)?;
155
156 let mutable_settings_list = outer_iter.next()?.as_list()?;
157 let mut mutable_settings_iter = mutable_settings_list.into_iter();
158 let total_accrued_function_script_hash =
159 PolicyId(mutable_settings_iter.next()?.as_bytes()?.to_vec().try_into().ok()?);
160 let initial_incentive = mutable_settings_iter.next()?.as_integer()?.as_u64()?.into();
161
162 let stats = ReserveStats {
163 token_total_amount_transferred: outer_iter.next()?.as_integer()?.as_u64()?.into(),
164 };
165
166 Some(ReserveDatum {
167 immutable_settings: ReserveImmutableSettings { t0, token },
168 mutable_settings: ReserveMutableSettings {
169 total_accrued_function_asset_name: total_accrued_function_script_hash,
170 initial_incentive,
171 },
172 stats,
173 })
174}
175
176fn decode_token_id_datum(pd: &PlutusData) -> Option<AssetId> {
177 let token_id_list = pd
178 .as_constr_plutus_data()
179 .filter(|constr| constr.alternative() == BigNum::zero())
180 .map(|constr| constr.data())?;
181 let mut token_id_list_iter = token_id_list.into_iter();
182 let policy_id = token_id_list_iter.next()?.as_bytes()?.to_vec();
183 let asset_name = token_id_list_iter.next()?.as_bytes()?.to_vec();
184 Some(AssetId {
185 policy_id: PolicyId(policy_id.try_into().ok()?),
186 asset_name: AssetName(asset_name.try_into().ok()?),
187 })
188}
189
190#[cfg(test)]
191mod tests {
192 use cardano_serialization_lib::PlutusData;
193 use pretty_assertions::assert_eq;
194 use sidechain_domain::{AssetName, PolicyId};
195
196 use crate::test_helpers::test_plutus_data;
197
198 use super::{ReserveDatum, ReserveImmutableSettings, ReserveMutableSettings, ReserveStats};
199
200 fn test_datum() -> ReserveDatum {
201 ReserveDatum {
202 immutable_settings: ReserveImmutableSettings {
203 t0: 0,
204 token: sidechain_domain::AssetId {
205 policy_id: PolicyId([0; 28]),
206 asset_name: AssetName::from_hex_unsafe("aabbcc"),
207 },
208 },
209 mutable_settings: ReserveMutableSettings {
210 total_accrued_function_asset_name: PolicyId([2; 28]),
211 initial_incentive: 0,
212 },
213 stats: ReserveStats { token_total_amount_transferred: 1000 },
214 }
215 }
216
217 fn test_datum_plutus_data() -> PlutusData {
218 test_plutus_data!({"list":[
219 {"list":[
220 {"list":[
221 {"int": 0},
222 {"constructor":0,
223 "fields":[
224 {"bytes": "00000000000000000000000000000000000000000000000000000000"},
225 {"bytes": "aabbcc"}]}
226 ]},
227 {"list":[
228 {"bytes": "02020202020202020202020202020202020202020202020202020202"},
229 {"int": 0}
230 ]},
231 {"int": 1000}
232 ]},
233 {"constructor":0,"fields":[]},
234 {"int":0}
235 ]})
236 }
237
238 #[test]
239 fn encode() {
240 assert_eq!(PlutusData::from(test_datum()), test_datum_plutus_data())
241 }
242
243 #[test]
244 fn decode() {
245 assert_eq!(ReserveDatum::try_from(test_datum_plutus_data()).unwrap(), test_datum())
246 }
247}