Skip to main content

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