bee_block/output/unlock_condition/
mod.rs

1// Copyright 2021-2022 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4mod address;
5mod expiration;
6mod governor_address;
7mod immutable_alias_address;
8mod state_controller_address;
9mod storage_deposit_return;
10mod timelock;
11
12use alloc::{boxed::Box, vec::Vec};
13
14use bitflags::bitflags;
15use derive_more::{Deref, From};
16use iterator_sorted::is_unique_sorted;
17use packable::{
18    bounded::BoundedU8,
19    error::{UnpackError, UnpackErrorExt},
20    packer::Packer,
21    prefix::BoxedSlicePrefix,
22    unpacker::Unpacker,
23    Packable,
24};
25
26pub use self::{
27    address::AddressUnlockCondition, expiration::ExpirationUnlockCondition,
28    governor_address::GovernorAddressUnlockCondition, immutable_alias_address::ImmutableAliasAddressUnlockCondition,
29    state_controller_address::StateControllerAddressUnlockCondition,
30    storage_deposit_return::StorageDepositReturnUnlockCondition, timelock::TimelockUnlockCondition,
31};
32use crate::{address::Address, create_bitflags, protocol::ProtocolParameters, Error};
33
34///
35#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, From)]
36#[cfg_attr(
37    feature = "serde",
38    derive(serde::Serialize, serde::Deserialize),
39    serde(tag = "type", content = "data")
40)]
41pub enum UnlockCondition {
42    /// An address unlock condition.
43    Address(AddressUnlockCondition),
44    /// A storage deposit return unlock condition.
45    StorageDepositReturn(StorageDepositReturnUnlockCondition),
46    /// A timelock unlock condition.
47    Timelock(TimelockUnlockCondition),
48    /// An expiration unlock condition.
49    Expiration(ExpirationUnlockCondition),
50    /// A state controller address unlock condition.
51    StateControllerAddress(StateControllerAddressUnlockCondition),
52    /// A governor address unlock condition.
53    GovernorAddress(GovernorAddressUnlockCondition),
54    /// An immutable alias address unlock condition.
55    ImmutableAliasAddress(ImmutableAliasAddressUnlockCondition),
56}
57
58impl UnlockCondition {
59    /// Return the output kind of an `Output`.
60    pub fn kind(&self) -> u8 {
61        match self {
62            Self::Address(_) => AddressUnlockCondition::KIND,
63            Self::StorageDepositReturn(_) => StorageDepositReturnUnlockCondition::KIND,
64            Self::Timelock(_) => TimelockUnlockCondition::KIND,
65            Self::Expiration(_) => ExpirationUnlockCondition::KIND,
66            Self::StateControllerAddress(_) => StateControllerAddressUnlockCondition::KIND,
67            Self::GovernorAddress(_) => GovernorAddressUnlockCondition::KIND,
68            Self::ImmutableAliasAddress(_) => ImmutableAliasAddressUnlockCondition::KIND,
69        }
70    }
71
72    /// Returns the [`UnlockConditionFlags`] for the given [`UnlockCondition`].
73    pub(crate) fn flag(&self) -> UnlockConditionFlags {
74        match self {
75            Self::Address(_) => UnlockConditionFlags::ADDRESS,
76            Self::StorageDepositReturn(_) => UnlockConditionFlags::STORAGE_DEPOSIT_RETURN,
77            Self::Timelock(_) => UnlockConditionFlags::TIMELOCK,
78            Self::Expiration(_) => UnlockConditionFlags::EXPIRATION,
79            Self::StateControllerAddress(_) => UnlockConditionFlags::STATE_CONTROLLER_ADDRESS,
80            Self::GovernorAddress(_) => UnlockConditionFlags::GOVERNOR_ADDRESS,
81            Self::ImmutableAliasAddress(_) => UnlockConditionFlags::IMMUTABLE_ALIAS_ADDRESS,
82        }
83    }
84}
85
86create_bitflags!(
87    /// A bitflags-based representation of the set of active [`UnlockCondition`]s.
88    pub UnlockConditionFlags,
89    u16,
90    [
91        (ADDRESS, AddressUnlockCondition),
92        (STORAGE_DEPOSIT_RETURN, StorageDepositReturnUnlockCondition),
93        (TIMELOCK, TimelockUnlockCondition),
94        (EXPIRATION, ExpirationUnlockCondition),
95        (STATE_CONTROLLER_ADDRESS, StateControllerAddressUnlockCondition),
96        (GOVERNOR_ADDRESS, GovernorAddressUnlockCondition),
97        (IMMUTABLE_ALIAS_ADDRESS, ImmutableAliasAddressUnlockCondition),
98    ]
99);
100
101impl Packable for UnlockCondition {
102    type UnpackError = Error;
103    type UnpackVisitor = ProtocolParameters;
104
105    fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
106        match self {
107            UnlockCondition::Address(unlock_condition) => {
108                AddressUnlockCondition::KIND.pack(packer)?;
109                unlock_condition.pack(packer)
110            }
111            UnlockCondition::StorageDepositReturn(unlock_condition) => {
112                StorageDepositReturnUnlockCondition::KIND.pack(packer)?;
113                unlock_condition.pack(packer)
114            }
115            UnlockCondition::Timelock(unlock_condition) => {
116                TimelockUnlockCondition::KIND.pack(packer)?;
117                unlock_condition.pack(packer)
118            }
119            UnlockCondition::Expiration(unlock_condition) => {
120                ExpirationUnlockCondition::KIND.pack(packer)?;
121                unlock_condition.pack(packer)
122            }
123            UnlockCondition::StateControllerAddress(unlock_condition) => {
124                StateControllerAddressUnlockCondition::KIND.pack(packer)?;
125                unlock_condition.pack(packer)
126            }
127            UnlockCondition::GovernorAddress(unlock_condition) => {
128                GovernorAddressUnlockCondition::KIND.pack(packer)?;
129                unlock_condition.pack(packer)
130            }
131            UnlockCondition::ImmutableAliasAddress(unlock_condition) => {
132                ImmutableAliasAddressUnlockCondition::KIND.pack(packer)?;
133                unlock_condition.pack(packer)
134            }
135        }?;
136
137        Ok(())
138    }
139
140    fn unpack<U: Unpacker, const VERIFY: bool>(
141        unpacker: &mut U,
142        visitor: &Self::UnpackVisitor,
143    ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
144        Ok(match u8::unpack::<_, VERIFY>(unpacker, &()).coerce()? {
145            AddressUnlockCondition::KIND => {
146                UnlockCondition::from(AddressUnlockCondition::unpack::<_, VERIFY>(unpacker, &()).coerce()?)
147            }
148            StorageDepositReturnUnlockCondition::KIND => UnlockCondition::from(
149                StorageDepositReturnUnlockCondition::unpack::<_, VERIFY>(unpacker, visitor).coerce()?,
150            ),
151            TimelockUnlockCondition::KIND => {
152                UnlockCondition::from(TimelockUnlockCondition::unpack::<_, VERIFY>(unpacker, &()).coerce()?)
153            }
154            ExpirationUnlockCondition::KIND => {
155                UnlockCondition::from(ExpirationUnlockCondition::unpack::<_, VERIFY>(unpacker, &()).coerce()?)
156            }
157            StateControllerAddressUnlockCondition::KIND => UnlockCondition::from(
158                StateControllerAddressUnlockCondition::unpack::<_, VERIFY>(unpacker, &()).coerce()?,
159            ),
160            GovernorAddressUnlockCondition::KIND => {
161                UnlockCondition::from(GovernorAddressUnlockCondition::unpack::<_, VERIFY>(unpacker, &()).coerce()?)
162            }
163            ImmutableAliasAddressUnlockCondition::KIND => UnlockCondition::from(
164                ImmutableAliasAddressUnlockCondition::unpack::<_, VERIFY>(unpacker, &()).coerce()?,
165            ),
166            k => return Err(Error::InvalidOutputKind(k)).map_err(UnpackError::Packable),
167        })
168    }
169}
170
171pub(crate) type UnlockConditionCount = BoundedU8<0, { UnlockConditions::COUNT_MAX }>;
172
173///
174#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Deref, Packable)]
175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176#[packable(unpack_error = Error, with = |e| e.unwrap_item_err_or_else(|p| Error::InvalidUnlockConditionCount(p.into())))]
177#[packable(unpack_visitor = ProtocolParameters)]
178pub struct UnlockConditions(
179    #[packable(verify_with = verify_unique_sorted_packable)] BoxedSlicePrefix<UnlockCondition, UnlockConditionCount>,
180);
181
182impl TryFrom<Vec<UnlockCondition>> for UnlockConditions {
183    type Error = Error;
184
185    #[inline(always)]
186    fn try_from(unlock_conditions: Vec<UnlockCondition>) -> Result<Self, Self::Error> {
187        Self::new(unlock_conditions)
188    }
189}
190
191impl IntoIterator for UnlockConditions {
192    type Item = UnlockCondition;
193    type IntoIter = alloc::vec::IntoIter<Self::Item>;
194
195    fn into_iter(self) -> Self::IntoIter {
196        Vec::from(Into::<Box<[UnlockCondition]>>::into(self.0)).into_iter()
197    }
198}
199
200impl UnlockConditions {
201    ///
202    pub const COUNT_MAX: u8 = 7;
203
204    /// Creates a new [`UnlockConditions`].
205    pub fn new(unlock_conditions: Vec<UnlockCondition>) -> Result<Self, Error> {
206        let mut unlock_conditions =
207            BoxedSlicePrefix::<UnlockCondition, UnlockConditionCount>::try_from(unlock_conditions.into_boxed_slice())
208                .map_err(Error::InvalidUnlockConditionCount)?;
209
210        unlock_conditions.sort_by_key(UnlockCondition::kind);
211        // Sort is obviously fine now but uniqueness still needs to be checked.
212        verify_unique_sorted::<true>(&unlock_conditions)?;
213
214        Ok(Self(unlock_conditions))
215    }
216
217    /// Gets a reference to an [`UnlockCondition`] from an unlock condition kind, if any.
218    #[inline(always)]
219    pub fn get(&self, key: u8) -> Option<&UnlockCondition> {
220        self.0
221            .binary_search_by_key(&key, UnlockCondition::kind)
222            // PANIC: indexation is fine since the index has been found.
223            .map(|index| &self.0[index])
224            .ok()
225    }
226
227    /// Gets a reference to an [`AddressUnlockCondition`], if any.
228    #[inline(always)]
229    pub fn address(&self) -> Option<&AddressUnlockCondition> {
230        if let Some(UnlockCondition::Address(address)) = self.get(AddressUnlockCondition::KIND) {
231            Some(address)
232        } else {
233            None
234        }
235    }
236
237    /// Gets a reference to a [`StorageDepositReturnUnlockCondition`], if any.
238    #[inline(always)]
239    pub fn storage_deposit_return(&self) -> Option<&StorageDepositReturnUnlockCondition> {
240        if let Some(UnlockCondition::StorageDepositReturn(storage_deposit_return)) =
241            self.get(StorageDepositReturnUnlockCondition::KIND)
242        {
243            Some(storage_deposit_return)
244        } else {
245            None
246        }
247    }
248
249    /// Gets a reference to a [`TimelockUnlockCondition`], if any.
250    #[inline(always)]
251    pub fn timelock(&self) -> Option<&TimelockUnlockCondition> {
252        if let Some(UnlockCondition::Timelock(timelock)) = self.get(TimelockUnlockCondition::KIND) {
253            Some(timelock)
254        } else {
255            None
256        }
257    }
258
259    /// Gets a reference to an [`ExpirationUnlockCondition`], if any.
260    #[inline(always)]
261    pub fn expiration(&self) -> Option<&ExpirationUnlockCondition> {
262        if let Some(UnlockCondition::Expiration(expiration)) = self.get(ExpirationUnlockCondition::KIND) {
263            Some(expiration)
264        } else {
265            None
266        }
267    }
268
269    /// Gets a reference to a [`StateControllerAddressUnlockCondition`], if any.
270    #[inline(always)]
271    pub fn state_controller_address(&self) -> Option<&StateControllerAddressUnlockCondition> {
272        if let Some(UnlockCondition::StateControllerAddress(state_controller_address)) =
273            self.get(StateControllerAddressUnlockCondition::KIND)
274        {
275            Some(state_controller_address)
276        } else {
277            None
278        }
279    }
280
281    /// Gets a reference to a [`GovernorAddressUnlockCondition`], if any.
282    #[inline(always)]
283    pub fn governor_address(&self) -> Option<&GovernorAddressUnlockCondition> {
284        if let Some(UnlockCondition::GovernorAddress(governor_address)) = self.get(GovernorAddressUnlockCondition::KIND)
285        {
286            Some(governor_address)
287        } else {
288            None
289        }
290    }
291
292    /// Gets a reference to an [`ImmutableAliasAddressUnlockCondition`], if any.
293    #[inline(always)]
294    pub fn immutable_alias_address(&self) -> Option<&ImmutableAliasAddressUnlockCondition> {
295        if let Some(UnlockCondition::ImmutableAliasAddress(immutable_alias_address)) =
296            self.get(ImmutableAliasAddressUnlockCondition::KIND)
297        {
298            Some(immutable_alias_address)
299        } else {
300            None
301        }
302    }
303
304    /// Returns the address to be unlocked.
305    #[inline(always)]
306    pub fn locked_address<'a>(&'a self, address: &'a Address, milestone_timestamp: u32) -> &'a Address {
307        self.expiration()
308            .and_then(|e| e.return_address_expired(milestone_timestamp))
309            .unwrap_or(address)
310    }
311
312    /// Returns whether a time lock exists and is still relevant.
313    #[inline(always)]
314    pub fn is_time_locked(&self, milestone_timestamp: u32) -> bool {
315        self.timelock()
316            .map_or(false, |timelock| milestone_timestamp < timelock.timestamp())
317    }
318
319    /// Returns whether an expiration exists and is expired.
320    #[inline(always)]
321    pub fn is_expired(&self, milestone_timestamp: u32) -> bool {
322        self.expiration()
323            .map_or(false, |expiration| milestone_timestamp >= expiration.timestamp())
324    }
325}
326
327#[inline]
328fn verify_unique_sorted<const VERIFY: bool>(unlock_conditions: &[UnlockCondition]) -> Result<(), Error> {
329    if VERIFY && !is_unique_sorted(unlock_conditions.iter().map(UnlockCondition::kind)) {
330        Err(Error::UnlockConditionsNotUniqueSorted)
331    } else {
332        Ok(())
333    }
334}
335
336#[inline]
337fn verify_unique_sorted_packable<const VERIFY: bool>(
338    unlock_conditions: &[UnlockCondition],
339    _: &ProtocolParameters,
340) -> Result<(), Error> {
341    verify_unique_sorted::<VERIFY>(unlock_conditions)
342}
343
344pub(crate) fn verify_allowed_unlock_conditions(
345    unlock_conditions: &UnlockConditions,
346    allowed_unlock_conditions: UnlockConditionFlags,
347) -> Result<(), Error> {
348    for (index, unlock_condition) in unlock_conditions.iter().enumerate() {
349        if !allowed_unlock_conditions.contains(unlock_condition.flag()) {
350            return Err(Error::UnallowedUnlockCondition {
351                index,
352                kind: unlock_condition.kind(),
353            });
354        }
355    }
356
357    Ok(())
358}
359
360#[cfg(test)]
361mod test {
362    use super::*;
363
364    #[test]
365    fn all_flags_present() {
366        assert_eq!(
367            UnlockConditionFlags::ALL_FLAGS,
368            &[
369                UnlockConditionFlags::ADDRESS,
370                UnlockConditionFlags::STORAGE_DEPOSIT_RETURN,
371                UnlockConditionFlags::TIMELOCK,
372                UnlockConditionFlags::EXPIRATION,
373                UnlockConditionFlags::STATE_CONTROLLER_ADDRESS,
374                UnlockConditionFlags::GOVERNOR_ADDRESS,
375                UnlockConditionFlags::IMMUTABLE_ALIAS_ADDRESS
376            ]
377        );
378    }
379}
380
381#[cfg(feature = "dto")]
382#[allow(missing_docs)]
383pub mod dto {
384    use serde::{Deserialize, Serialize, Serializer};
385    use serde_json::Value;
386
387    pub use self::{
388        address::dto::AddressUnlockConditionDto, expiration::dto::ExpirationUnlockConditionDto,
389        governor_address::dto::GovernorAddressUnlockConditionDto,
390        immutable_alias_address::dto::ImmutableAliasAddressUnlockConditionDto,
391        state_controller_address::dto::StateControllerAddressUnlockConditionDto,
392        storage_deposit_return::dto::StorageDepositReturnUnlockConditionDto, timelock::dto::TimelockUnlockConditionDto,
393    };
394    use super::*;
395    use crate::{
396        address::{dto::AddressDto, Address},
397        error::dto::DtoError,
398    };
399
400    #[derive(Clone, Debug, Eq, PartialEq, From)]
401    pub enum UnlockConditionDto {
402        /// An address unlock condition.
403        Address(AddressUnlockConditionDto),
404        /// A storage deposit return unlock condition.
405        StorageDepositReturn(StorageDepositReturnUnlockConditionDto),
406        /// A timelock unlock condition.
407        Timelock(TimelockUnlockConditionDto),
408        /// An expiration unlock condition.
409        Expiration(ExpirationUnlockConditionDto),
410        /// A state controller address unlock condition.
411        StateControllerAddress(StateControllerAddressUnlockConditionDto),
412        /// A governor address unlock condition.
413        GovernorAddress(GovernorAddressUnlockConditionDto),
414        /// An immutable alias address unlock condition.
415        ImmutableAliasAddress(ImmutableAliasAddressUnlockConditionDto),
416    }
417
418    impl<'de> Deserialize<'de> for UnlockConditionDto {
419        fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
420            let value = Value::deserialize(d)?;
421            Ok(
422                match value
423                    .get("type")
424                    .and_then(Value::as_u64)
425                    .ok_or_else(|| serde::de::Error::custom("invalid unlock condition type"))?
426                    as u8
427                {
428                    AddressUnlockCondition::KIND => {
429                        UnlockConditionDto::Address(AddressUnlockConditionDto::deserialize(value).map_err(|e| {
430                            serde::de::Error::custom(format!("cannot deserialize address unlock condition: {}", e))
431                        })?)
432                    }
433                    StorageDepositReturnUnlockCondition::KIND => UnlockConditionDto::StorageDepositReturn(
434                        StorageDepositReturnUnlockConditionDto::deserialize(value).map_err(|e| {
435                            serde::de::Error::custom(format!(
436                                "cannot deserialize storage deposit unlock condition: {}",
437                                e
438                            ))
439                        })?,
440                    ),
441                    TimelockUnlockCondition::KIND => {
442                        UnlockConditionDto::Timelock(TimelockUnlockConditionDto::deserialize(value).map_err(|e| {
443                            serde::de::Error::custom(format!("cannot deserialize timelock unlock condition: {}", e))
444                        })?)
445                    }
446                    ExpirationUnlockCondition::KIND => UnlockConditionDto::Expiration(
447                        ExpirationUnlockConditionDto::deserialize(value).map_err(|e| {
448                            serde::de::Error::custom(format!("cannot deserialize expiration unlock condition: {}", e))
449                        })?,
450                    ),
451                    StateControllerAddressUnlockCondition::KIND => UnlockConditionDto::StateControllerAddress(
452                        StateControllerAddressUnlockConditionDto::deserialize(value).map_err(|e| {
453                            serde::de::Error::custom(format!(
454                                "cannot deserialize state controller unlock condition: {}",
455                                e
456                            ))
457                        })?,
458                    ),
459                    GovernorAddressUnlockCondition::KIND => UnlockConditionDto::GovernorAddress(
460                        GovernorAddressUnlockConditionDto::deserialize(value).map_err(|e| {
461                            serde::de::Error::custom(format!("cannot deserialize governor unlock condition: {}", e))
462                        })?,
463                    ),
464                    ImmutableAliasAddressUnlockCondition::KIND => UnlockConditionDto::ImmutableAliasAddress(
465                        ImmutableAliasAddressUnlockConditionDto::deserialize(value).map_err(|e| {
466                            serde::de::Error::custom(format!(
467                                "cannot deserialize immutable alias address unlock condition: {}",
468                                e
469                            ))
470                        })?,
471                    ),
472                    _ => return Err(serde::de::Error::custom("invalid unlock condition type")),
473                },
474            )
475        }
476    }
477
478    impl Serialize for UnlockConditionDto {
479        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
480        where
481            S: Serializer,
482        {
483            #[derive(Serialize)]
484            #[serde(untagged)]
485            enum UnlockConditionDto_<'a> {
486                T1(&'a AddressUnlockConditionDto),
487                T2(&'a StorageDepositReturnUnlockConditionDto),
488                T3(&'a TimelockUnlockConditionDto),
489                T4(&'a ExpirationUnlockConditionDto),
490                T5(&'a StateControllerAddressUnlockConditionDto),
491                T6(&'a GovernorAddressUnlockConditionDto),
492                T7(&'a ImmutableAliasAddressUnlockConditionDto),
493            }
494            #[derive(Serialize)]
495            struct TypedUnlockCondition<'a> {
496                #[serde(flatten)]
497                unlock_condition: UnlockConditionDto_<'a>,
498            }
499            let unlock_condition = match self {
500                UnlockConditionDto::Address(o) => TypedUnlockCondition {
501                    unlock_condition: UnlockConditionDto_::T1(o),
502                },
503                UnlockConditionDto::StorageDepositReturn(o) => TypedUnlockCondition {
504                    unlock_condition: UnlockConditionDto_::T2(o),
505                },
506                UnlockConditionDto::Timelock(o) => TypedUnlockCondition {
507                    unlock_condition: UnlockConditionDto_::T3(o),
508                },
509                UnlockConditionDto::Expiration(o) => TypedUnlockCondition {
510                    unlock_condition: UnlockConditionDto_::T4(o),
511                },
512                UnlockConditionDto::StateControllerAddress(o) => TypedUnlockCondition {
513                    unlock_condition: UnlockConditionDto_::T5(o),
514                },
515                UnlockConditionDto::GovernorAddress(o) => TypedUnlockCondition {
516                    unlock_condition: UnlockConditionDto_::T6(o),
517                },
518                UnlockConditionDto::ImmutableAliasAddress(o) => TypedUnlockCondition {
519                    unlock_condition: UnlockConditionDto_::T7(o),
520                },
521            };
522            unlock_condition.serialize(serializer)
523        }
524    }
525
526    impl From<&UnlockCondition> for UnlockConditionDto {
527        fn from(value: &UnlockCondition) -> Self {
528            match value {
529                UnlockCondition::Address(v) => Self::Address(AddressUnlockConditionDto {
530                    kind: AddressUnlockCondition::KIND,
531                    address: v.address().into(),
532                }),
533                UnlockCondition::StorageDepositReturn(v) => {
534                    Self::StorageDepositReturn(StorageDepositReturnUnlockConditionDto {
535                        kind: StorageDepositReturnUnlockCondition::KIND,
536                        return_address: AddressDto::from(v.return_address()),
537                        amount: v.amount().to_string(),
538                    })
539                }
540                UnlockCondition::Timelock(v) => Self::Timelock(TimelockUnlockConditionDto {
541                    kind: TimelockUnlockCondition::KIND,
542                    timestamp: v.timestamp(),
543                }),
544                UnlockCondition::Expiration(v) => Self::Expiration(ExpirationUnlockConditionDto {
545                    kind: ExpirationUnlockCondition::KIND,
546                    return_address: v.return_address().into(),
547                    timestamp: v.timestamp(),
548                }),
549                UnlockCondition::StateControllerAddress(v) => {
550                    Self::StateControllerAddress(StateControllerAddressUnlockConditionDto {
551                        kind: StateControllerAddressUnlockCondition::KIND,
552                        address: v.address().into(),
553                    })
554                }
555                UnlockCondition::GovernorAddress(v) => Self::GovernorAddress(GovernorAddressUnlockConditionDto {
556                    kind: GovernorAddressUnlockCondition::KIND,
557                    address: v.address().into(),
558                }),
559                UnlockCondition::ImmutableAliasAddress(v) => {
560                    Self::ImmutableAliasAddress(ImmutableAliasAddressUnlockConditionDto {
561                        kind: ImmutableAliasAddressUnlockCondition::KIND,
562                        address: v.address().into(),
563                    })
564                }
565            }
566        }
567    }
568
569    impl UnlockCondition {
570        pub fn try_from_dto(value: &UnlockConditionDto, token_supply: u64) -> Result<UnlockCondition, DtoError> {
571            Ok(match value {
572                UnlockConditionDto::Address(v) => UnlockCondition::Address(AddressUnlockCondition::new(
573                    (&v.address)
574                        .try_into()
575                        .map_err(|_e| DtoError::InvalidField("AddressUnlockCondition"))?,
576                )),
577                UnlockConditionDto::StorageDepositReturn(v) => {
578                    UnlockCondition::StorageDepositReturn(StorageDepositReturnUnlockCondition::new(
579                        Address::try_from(&v.return_address)?,
580                        v.amount.parse::<u64>().map_err(|_| DtoError::InvalidField("amount"))?,
581                        token_supply,
582                    )?)
583                }
584                UnlockConditionDto::Timelock(v) => UnlockCondition::Timelock(
585                    TimelockUnlockCondition::new(v.timestamp)
586                        .map_err(|_| DtoError::InvalidField("TimelockUnlockCondition"))?,
587                ),
588                UnlockConditionDto::Expiration(v) => UnlockCondition::Expiration(
589                    ExpirationUnlockCondition::new(
590                        (&v.return_address)
591                            .try_into()
592                            .map_err(|_e| DtoError::InvalidField("ExpirationUnlockCondition"))?,
593                        v.timestamp,
594                    )
595                    .map_err(|_| DtoError::InvalidField("ExpirationUnlockCondition"))?,
596                ),
597                UnlockConditionDto::StateControllerAddress(v) => {
598                    UnlockCondition::StateControllerAddress(StateControllerAddressUnlockCondition::new(
599                        (&v.address)
600                            .try_into()
601                            .map_err(|_e| DtoError::InvalidField("StateControllerAddressUnlockCondition"))?,
602                    ))
603                }
604                UnlockConditionDto::GovernorAddress(v) => {
605                    UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new(
606                        (&v.address)
607                            .try_into()
608                            .map_err(|_e| DtoError::InvalidField("GovernorAddressUnlockCondition"))?,
609                    ))
610                }
611                UnlockConditionDto::ImmutableAliasAddress(v) => {
612                    let address: Address = (&v.address)
613                        .try_into()
614                        .map_err(|_e| DtoError::InvalidField("ImmutableAliasAddressUnlockCondition"))?;
615                    // An ImmutableAliasAddressUnlockCondition must have an AliasAddress.
616                    if let Address::Alias(alias_address) = &address {
617                        UnlockCondition::ImmutableAliasAddress(ImmutableAliasAddressUnlockCondition::new(
618                            *alias_address,
619                        ))
620                    } else {
621                        return Err(DtoError::InvalidField("ImmutableAliasAddressUnlockCondition"));
622                    }
623                }
624            })
625        }
626    }
627
628    impl UnlockConditionDto {
629        /// Return the unlock condition kind of a `UnlockConditionDto`.
630        pub fn kind(&self) -> u8 {
631            match self {
632                Self::Address(_) => AddressUnlockCondition::KIND,
633                Self::StorageDepositReturn(_) => StorageDepositReturnUnlockCondition::KIND,
634                Self::Timelock(_) => TimelockUnlockCondition::KIND,
635                Self::Expiration(_) => ExpirationUnlockCondition::KIND,
636                Self::StateControllerAddress(_) => StateControllerAddressUnlockCondition::KIND,
637                Self::GovernorAddress(_) => GovernorAddressUnlockCondition::KIND,
638                Self::ImmutableAliasAddress(_) => ImmutableAliasAddressUnlockCondition::KIND,
639            }
640        }
641    }
642}