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