iota_types/stardust/output/
basic.rs

1// Copyright (c) 2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! Rust types and logic for the Move counterparts in the `stardust` system
5//! package.
6
7use move_core_types::{ident_str, identifier::IdentStr, language_storage::StructTag};
8use serde::{Deserialize, Serialize};
9use serde_with::serde_as;
10
11use super::unlock_conditions::{
12    ExpirationUnlockCondition, StorageDepositReturnUnlockCondition, TimelockUnlockCondition,
13};
14use crate::{
15    STARDUST_ADDRESS, TypeTag,
16    balance::Balance,
17    base_types::IotaAddress,
18    collection_types::Bag,
19    error::IotaError,
20    id::UID,
21    object::{Data, Object},
22};
23
24pub const BASIC_OUTPUT_MODULE_NAME: &IdentStr = ident_str!("basic_output");
25pub const BASIC_OUTPUT_STRUCT_NAME: &IdentStr = ident_str!("BasicOutput");
26
27/// Rust version of the stardust basic output.
28#[serde_as]
29#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
30pub struct BasicOutput {
31    /// Hash of the `OutputId` that was migrated.
32    pub id: UID,
33
34    /// The amount of coins held by the output.
35    pub balance: Balance,
36
37    /// The `Bag` holds native tokens, key-ed by the stringified type of the
38    /// asset. Example: key: "0xabcded::soon::SOON", value:
39    /// Balance<0xabcded::soon::SOON>.
40    pub native_tokens: Bag,
41
42    /// The storage deposit return unlock condition.
43    pub storage_deposit_return: Option<StorageDepositReturnUnlockCondition>,
44    /// The timelock unlock condition.
45    pub timelock: Option<TimelockUnlockCondition>,
46    /// The expiration unlock condition.
47    pub expiration: Option<ExpirationUnlockCondition>,
48
49    // Possible features, they have no effect and only here to hold data until the object is
50    // deleted.
51    /// The metadata feature.
52    pub metadata: Option<Vec<u8>>,
53    /// The tag feature.
54    pub tag: Option<Vec<u8>>,
55    /// The sender feature.
56    pub sender: Option<IotaAddress>,
57}
58
59impl BasicOutput {
60    /// Returns the struct tag of the BasicOutput struct
61    pub fn tag(type_param: TypeTag) -> StructTag {
62        StructTag {
63            address: STARDUST_ADDRESS,
64            module: BASIC_OUTPUT_MODULE_NAME.to_owned(),
65            name: BASIC_OUTPUT_STRUCT_NAME.to_owned(),
66            type_params: vec![type_param],
67        }
68    }
69
70    /// Create a `BasicOutput` from BCS bytes.
71    pub fn from_bcs_bytes(content: &[u8]) -> Result<Self, IotaError> {
72        bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
73            error: format!("Unable to deserialize BasicOutput object: {err:?}"),
74        })
75    }
76
77    /// Whether the given `StructTag` represents a `BasicOutput`.
78    pub fn is_basic_output(s: &StructTag) -> bool {
79        s.address == STARDUST_ADDRESS
80            && s.module.as_ident_str() == BASIC_OUTPUT_MODULE_NAME
81            && s.name.as_ident_str() == BASIC_OUTPUT_STRUCT_NAME
82    }
83}
84
85impl TryFrom<&Object> for BasicOutput {
86    type Error = IotaError;
87    fn try_from(object: &Object) -> Result<Self, Self::Error> {
88        match &object.data {
89            Data::Move(o) => {
90                if o.type_().is_basic_output() {
91                    return BasicOutput::from_bcs_bytes(o.contents());
92                }
93            }
94            Data::Package(_) => {}
95        }
96
97        Err(IotaError::Type {
98            error: format!("Object type is not a BasicOutput: {object:?}"),
99        })
100    }
101}