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