1use crate::data_contract::associated_token::token_perpetual_distribution::distribution_recipient::{TokenDistributionRecipient, TokenDistributionResolvedRecipient};
2use crate::errors::ProtocolError;
3use bincode::{Decode, Encode, DecodeUntrusted};
4use platform_serialization_derive::{PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize};
5use platform_value::Identifier;
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use crate::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment;
9use crate::prelude::TimestampMillis;
10
11#[derive(
16 Serialize,
17 Deserialize,
18 Decode,
19 Encode,
20 Debug,
21 Clone,
22 Copy,
23 PartialEq,
24 Eq,
25 PartialOrd,
26 Default,
27 DecodeUntrusted,
28)]
29pub enum TokenDistributionType {
30 #[default]
32 PreProgrammed = 0,
33
34 Perpetual = 1,
36}
37
38#[derive(
43 Serialize, Deserialize, Decode, Encode, Debug, Clone, PartialEq, Eq, PartialOrd, DecodeUntrusted,
44)]
45#[serde(
46 into = "TokenDistributionTypeWithResolvedRecipientRepr",
47 from = "TokenDistributionTypeWithResolvedRecipientRepr"
48)]
49pub enum TokenDistributionTypeWithResolvedRecipient {
50 PreProgrammed(Identifier),
52
53 Perpetual(TokenDistributionResolvedRecipient),
55}
56
57#[derive(Serialize, Deserialize)]
60#[serde(tag = "$type", rename_all = "camelCase")]
61enum TokenDistributionTypeWithResolvedRecipientRepr {
62 PreProgrammed {
63 value: Identifier,
64 },
65 Perpetual {
66 value: TokenDistributionResolvedRecipient,
67 },
68}
69
70impl From<TokenDistributionTypeWithResolvedRecipient>
71 for TokenDistributionTypeWithResolvedRecipientRepr
72{
73 fn from(m: TokenDistributionTypeWithResolvedRecipient) -> Self {
74 match m {
75 TokenDistributionTypeWithResolvedRecipient::PreProgrammed(value) => {
76 Self::PreProgrammed { value }
77 }
78 TokenDistributionTypeWithResolvedRecipient::Perpetual(value) => {
79 Self::Perpetual { value }
80 }
81 }
82 }
83}
84
85impl From<TokenDistributionTypeWithResolvedRecipientRepr>
86 for TokenDistributionTypeWithResolvedRecipient
87{
88 fn from(r: TokenDistributionTypeWithResolvedRecipientRepr) -> Self {
89 match r {
90 TokenDistributionTypeWithResolvedRecipientRepr::PreProgrammed { value } => {
91 Self::PreProgrammed(value)
92 }
93 TokenDistributionTypeWithResolvedRecipientRepr::Perpetual { value } => {
94 Self::Perpetual(value)
95 }
96 }
97 }
98}
99
100#[derive(
106 Serialize, Deserialize, Decode, Encode, Debug, Clone, PartialEq, Eq, PartialOrd, DecodeUntrusted,
107)]
108#[serde(into = "TokenDistributionInfoRepr", from = "TokenDistributionInfoRepr")]
109pub enum TokenDistributionInfo {
110 PreProgrammed(TimestampMillis, Identifier),
113
114 Perpetual(RewardDistributionMoment, TokenDistributionResolvedRecipient),
118}
119
120#[derive(Serialize, Deserialize)]
125#[serde(tag = "$type", rename_all = "camelCase")]
126enum TokenDistributionInfoRepr {
127 PreProgrammed {
128 #[cfg_attr(
129 feature = "json-conversion",
130 serde(with = "crate::serialization::json_safe_u64")
131 )]
132 timestamp: TimestampMillis,
133 identity: Identifier,
134 },
135 Perpetual {
136 moment: RewardDistributionMoment,
137 recipient: TokenDistributionResolvedRecipient,
138 },
139}
140
141impl From<TokenDistributionInfo> for TokenDistributionInfoRepr {
142 fn from(m: TokenDistributionInfo) -> Self {
143 match m {
144 TokenDistributionInfo::PreProgrammed(timestamp, identity) => Self::PreProgrammed {
145 timestamp,
146 identity,
147 },
148 TokenDistributionInfo::Perpetual(moment, recipient) => {
149 Self::Perpetual { moment, recipient }
150 }
151 }
152 }
153}
154
155impl From<TokenDistributionInfoRepr> for TokenDistributionInfo {
156 fn from(r: TokenDistributionInfoRepr) -> Self {
157 match r {
158 TokenDistributionInfoRepr::PreProgrammed {
159 timestamp,
160 identity,
161 } => Self::PreProgrammed(timestamp, identity),
162 TokenDistributionInfoRepr::Perpetual { moment, recipient } => {
163 Self::Perpetual(moment, recipient)
164 }
165 }
166 }
167}
168
169impl From<TokenDistributionInfo> for TokenDistributionTypeWithResolvedRecipient {
170 fn from(info: TokenDistributionInfo) -> Self {
171 match info {
172 TokenDistributionInfo::PreProgrammed(_, recipient) => {
173 TokenDistributionTypeWithResolvedRecipient::PreProgrammed(recipient)
174 }
175 TokenDistributionInfo::Perpetual(_, recipient) => {
176 TokenDistributionTypeWithResolvedRecipient::Perpetual(recipient)
177 }
178 }
179 }
180}
181
182impl From<&TokenDistributionInfo> for TokenDistributionTypeWithResolvedRecipient {
183 fn from(info: &TokenDistributionInfo) -> Self {
184 match info {
185 TokenDistributionInfo::PreProgrammed(_, recipient) => {
186 TokenDistributionTypeWithResolvedRecipient::PreProgrammed(*recipient)
187 }
188 TokenDistributionInfo::Perpetual(_, recipient) => {
189 TokenDistributionTypeWithResolvedRecipient::Perpetual(recipient.clone())
190 }
191 }
192 }
193}
194
195impl fmt::Display for TokenDistributionType {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 match self {
198 TokenDistributionType::PreProgrammed => write!(f, "PreProgrammed"),
199 TokenDistributionType::Perpetual => write!(f, "Perpetual"),
200 }
201 }
202}
203
204#[derive(
205 Serialize,
206 Deserialize,
207 Decode,
208 Encode,
209 PlatformSerialize,
210 PlatformDeserializeTrusted,
211 PlatformDeserializeUntrusted,
212 Debug,
213 Clone,
214 PartialEq,
215 Eq,
216 DecodeUntrusted,
217)]
218#[platform_serialize(unversioned)]
219pub struct TokenDistributionKey {
220 pub token_id: Identifier,
221 pub recipient: TokenDistributionRecipient,
222 pub distribution_type: TokenDistributionType,
223}
224
225#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
227impl crate::serialization::JsonConvertible for TokenDistributionTypeWithResolvedRecipient {}
228
229#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
230impl crate::serialization::ValueConvertible for TokenDistributionTypeWithResolvedRecipient {}
231
232#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
233impl crate::serialization::JsonConvertible for TokenDistributionInfo {}
234
235#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
236impl crate::serialization::ValueConvertible for TokenDistributionInfo {}
237
238#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
239impl crate::serialization::JsonConvertible for TokenDistributionType {}
240
241#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
242impl crate::serialization::ValueConvertible for TokenDistributionType {}
243
244#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
245impl crate::serialization::JsonConvertible for TokenDistributionKey {}
246
247#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
248impl crate::serialization::ValueConvertible for TokenDistributionKey {}
249
250#[cfg(all(
251 test,
252 feature = "json-conversion",
253 feature = "value-conversion",
254 feature = "serde-conversion"
255))]
256mod json_convertible_tests_token_distribution_type_and_key {
257 use super::*;
258 use crate::serialization::{JsonConvertible, ValueConvertible};
259 use platform_value::{platform_value, Value};
260 use serde_json::json;
261
262 #[test]
263 fn token_distribution_type_round_trips_all_variants() {
264 let cases = [
267 (TokenDistributionType::PreProgrammed, "PreProgrammed"),
268 (TokenDistributionType::Perpetual, "Perpetual"),
269 ];
270 for (original, expected) in cases {
271 let json_v = original.to_json().expect("to_json");
272 assert_eq!(json_v, json!(expected));
273 assert_eq!(
274 TokenDistributionType::from_json(json_v).expect("from_json"),
275 original
276 );
277 let value = original.to_object().expect("to_object");
278 assert_eq!(value, platform_value!(expected));
279 assert_eq!(
280 TokenDistributionType::from_object(value).expect("from_object"),
281 original
282 );
283 }
284 }
285
286 fn key_fixture() -> TokenDistributionKey {
287 TokenDistributionKey {
288 token_id: Identifier::new([0x42; 32]),
289 recipient: TokenDistributionRecipient::EvonodesByParticipation,
290 distribution_type: TokenDistributionType::Perpetual,
291 }
292 }
293
294 #[test]
295 fn token_distribution_key_json_round_trip_with_full_wire_shape() {
296 let original = key_fixture();
297 let json = original.to_json().expect("to_json");
298 assert_eq!(
302 json,
303 json!({
304 "token_id": "5TeWSsjg2gbxCyWVniXeCmwM7UtHTCK7svzJr5xYJzHf",
305 "recipient": {"$type": "evonodesByParticipation"},
306 "distribution_type": "Perpetual",
307 })
308 );
309 let recovered = TokenDistributionKey::from_json(json).expect("from_json");
310 assert_eq!(original, recovered);
311 }
312
313 #[test]
314 fn token_distribution_key_value_round_trip_with_full_wire_shape() {
315 let original = key_fixture();
316 let value = original.to_object().expect("to_object");
317 let expected = Value::Map(vec![
318 (
319 Value::Text("token_id".to_string()),
320 Value::Identifier([0x42; 32]),
321 ),
322 (
323 Value::Text("recipient".to_string()),
324 Value::Map(vec![(
325 Value::Text("$type".to_string()),
326 Value::Text("evonodesByParticipation".to_string()),
327 )]),
328 ),
329 (
330 Value::Text("distribution_type".to_string()),
331 Value::Text("Perpetual".to_string()),
332 ),
333 ]);
334 assert_eq!(value, expected);
335 let recovered = TokenDistributionKey::from_object(value).expect("from_object");
336 assert_eq!(original, recovered);
337 }
338}
339
340#[cfg(all(
341 test,
342 feature = "json-conversion",
343 feature = "value-conversion",
344 feature = "serde-conversion"
345))]
346mod json_convertible_tests_token_distribution_info {
347 use super::*;
348 use platform_value::{Identifier, Value};
349 use serde_json::json;
350
351 fn fixture() -> TokenDistributionInfo {
355 TokenDistributionInfo::PreProgrammed(1_700_000_000_000, Identifier::new([0x42; 32]))
356 }
357
358 #[test]
359 fn json_round_trip_with_full_wire_shape() {
360 use crate::serialization::JsonConvertible;
361 let original = fixture();
362 let json = original.to_json().expect("to_json");
363 assert_eq!(
369 json,
370 json!({
371 "$type": "preProgrammed",
372 "timestamp": 1_700_000_000_000u64,
373 "identity": "5TeWSsjg2gbxCyWVniXeCmwM7UtHTCK7svzJr5xYJzHf",
374 })
375 );
376 let recovered = TokenDistributionInfo::from_json(json).expect("from_json");
377 assert_eq!(original, recovered);
378 }
379
380 #[test]
381 fn value_round_trip_with_full_wire_shape() {
382 use crate::serialization::ValueConvertible;
383 let original = fixture();
384 let value = original.to_object().expect("to_object");
385 let expected = Value::Map(vec![
390 (
391 Value::Text("$type".to_string()),
392 Value::Text("preProgrammed".to_string()),
393 ),
394 (
395 Value::Text("timestamp".to_string()),
396 Value::U64(1_700_000_000_000),
397 ),
398 (
399 Value::Text("identity".to_string()),
400 Value::Identifier([0x42; 32]),
401 ),
402 ]);
403 assert_eq!(value, expected);
404 let recovered = TokenDistributionInfo::from_object(value).expect("from_object");
405 assert_eq!(original, recovered);
406 }
407
408 #[test]
409 fn json_round_trip_perpetual_variant() {
410 use crate::data_contract::associated_token::token_perpetual_distribution::distribution_recipient::TokenDistributionResolvedRecipient;
411 use crate::data_contract::associated_token::token_perpetual_distribution::reward_distribution_moment::RewardDistributionMoment;
412 use crate::serialization::JsonConvertible;
413 let original = TokenDistributionInfo::Perpetual(
417 RewardDistributionMoment::BlockBasedMoment(500),
418 TokenDistributionResolvedRecipient::Identity(Identifier::new([0x77; 32])),
419 );
420 let json = original.to_json().expect("to_json");
421 assert_eq!(json["$type"], json!("perpetual"));
422 let recovered = TokenDistributionInfo::from_json(json).expect("from_json");
423 assert_eq!(original, recovered);
424 }
425}