iota_types/stardust/output/
alias.rs1use iota_protocol_config::ProtocolConfig;
5use iota_stardust_sdk::types::block::output::AliasOutput as StardustAlias;
6use move_core_types::{ident_str, identifier::IdentStr, language_storage::StructTag};
7use serde::{Deserialize, Serialize};
8use serde_with::serde_as;
9
10use crate::{
11 STARDUST_ADDRESS, TypeTag,
12 balance::Balance,
13 base_types::{IotaAddress, ObjectID, SequenceNumber, TxContext},
14 collection_types::Bag,
15 error::IotaError,
16 id::UID,
17 object::{Data, MoveObject, Object, Owner},
18 stardust::{coin_type::CoinType, stardust_to_iota_address},
19};
20
21pub const ALIAS_MODULE_NAME: &IdentStr = ident_str!("alias");
22pub const ALIAS_OUTPUT_MODULE_NAME: &IdentStr = ident_str!("alias_output");
23pub const ALIAS_OUTPUT_STRUCT_NAME: &IdentStr = ident_str!("AliasOutput");
24pub const ALIAS_STRUCT_NAME: &IdentStr = ident_str!("Alias");
25pub const ALIAS_DYNAMIC_OBJECT_FIELD_KEY: &[u8] = b"alias";
26pub const ALIAS_DYNAMIC_OBJECT_FIELD_KEY_TYPE: &str = "vector<u8>";
27
28#[serde_as]
29#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
30pub struct Alias {
31 pub id: UID,
34
35 pub legacy_state_controller: IotaAddress,
37 pub state_index: u32,
39 pub state_metadata: Option<Vec<u8>>,
41
42 pub sender: Option<IotaAddress>,
44 pub metadata: Option<Vec<u8>>,
46
47 pub immutable_issuer: Option<IotaAddress>,
49 pub immutable_metadata: Option<Vec<u8>>,
51}
52
53impl Alias {
54 pub fn tag() -> StructTag {
57 StructTag {
58 address: STARDUST_ADDRESS,
59 module: ALIAS_MODULE_NAME.to_owned(),
60 name: ALIAS_STRUCT_NAME.to_owned(),
61 type_params: Vec::new(),
62 }
63 }
64
65 pub fn try_from_stardust(
67 alias_id: ObjectID,
68 alias: &StardustAlias,
69 ) -> Result<Self, anyhow::Error> {
70 if alias_id.as_ref() == [0; 32] {
71 anyhow::bail!("alias_id must be non-zeroed");
72 }
73
74 let state_metadata: Option<Vec<u8>> = if alias.state_metadata().is_empty() {
75 None
76 } else {
77 Some(alias.state_metadata().to_vec())
78 };
79 let sender: Option<IotaAddress> = alias
80 .features()
81 .sender()
82 .map(|sender_feat| stardust_to_iota_address(sender_feat.address()))
83 .transpose()?;
84 let metadata: Option<Vec<u8>> = alias
85 .features()
86 .metadata()
87 .map(|metadata_feat| metadata_feat.data().to_vec());
88 let immutable_issuer: Option<IotaAddress> = alias
89 .immutable_features()
90 .issuer()
91 .map(|issuer_feat| stardust_to_iota_address(issuer_feat.address()))
92 .transpose()?;
93 let immutable_metadata: Option<Vec<u8>> = alias
94 .immutable_features()
95 .metadata()
96 .map(|metadata_feat| metadata_feat.data().to_vec());
97
98 Ok(Alias {
99 id: UID::new(alias_id),
100 legacy_state_controller: stardust_to_iota_address(alias.state_controller_address())?,
101 state_index: alias.state_index(),
102 state_metadata,
103 sender,
104 metadata,
105 immutable_issuer,
106 immutable_metadata,
107 })
108 }
109
110 pub fn to_genesis_object(
111 &self,
112 owner: Owner,
113 protocol_config: &ProtocolConfig,
114 tx_context: &TxContext,
115 version: SequenceNumber,
116 ) -> anyhow::Result<Object> {
117 let move_alias_object = {
119 MoveObject::new_from_execution(
120 Self::tag().into(),
121 version,
122 bcs::to_bytes(&self)?,
123 protocol_config,
124 )?
125 };
126
127 let move_alias_object = Object::new_from_genesis(
128 Data::Move(move_alias_object),
129 owner,
132 tx_context.digest(),
133 );
134
135 Ok(move_alias_object)
136 }
137}
138
139#[serde_as]
140#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
141pub struct AliasOutput {
142 pub id: UID,
144
145 pub balance: Balance,
147 pub native_tokens: Bag,
151}
152
153impl AliasOutput {
154 pub fn tag(type_param: TypeTag) -> StructTag {
157 StructTag {
158 address: STARDUST_ADDRESS,
159 module: ALIAS_OUTPUT_MODULE_NAME.to_owned(),
160 name: ALIAS_OUTPUT_STRUCT_NAME.to_owned(),
161 type_params: vec![type_param],
162 }
163 }
164
165 pub fn try_from_stardust(
168 object_id: ObjectID,
169 alias: &StardustAlias,
170 native_tokens: Bag,
171 ) -> Result<Self, anyhow::Error> {
172 Ok(AliasOutput {
173 id: UID::new(object_id),
174 balance: Balance::new(alias.amount()),
175 native_tokens,
176 })
177 }
178
179 pub fn to_genesis_object(
180 &self,
181 owner: Owner,
182 protocol_config: &ProtocolConfig,
183 tx_context: &TxContext,
184 version: SequenceNumber,
185 coin_type: CoinType,
186 ) -> anyhow::Result<Object> {
187 let move_alias_output_object = {
189 MoveObject::new_from_execution(
190 AliasOutput::tag(coin_type.to_type_tag()).into(),
191 version,
192 bcs::to_bytes(&self)?,
193 protocol_config,
194 )?
195 };
196
197 let move_alias_output_object = Object::new_from_genesis(
198 Data::Move(move_alias_output_object),
199 owner,
200 tx_context.digest(),
201 );
202
203 Ok(move_alias_output_object)
204 }
205
206 pub fn from_bcs_bytes(content: &[u8]) -> Result<Self, IotaError> {
208 bcs::from_bytes(content).map_err(|err| IotaError::ObjectDeserialization {
209 error: format!("Unable to deserialize AliasOutput object: {:?}", err),
210 })
211 }
212
213 pub fn is_alias_output(s: &StructTag) -> bool {
214 s.address == STARDUST_ADDRESS
215 && s.module.as_ident_str() == ALIAS_OUTPUT_MODULE_NAME
216 && s.name.as_ident_str() == ALIAS_OUTPUT_STRUCT_NAME
217 }
218}
219
220impl TryFrom<&Object> for AliasOutput {
221 type Error = IotaError;
222 fn try_from(object: &Object) -> Result<Self, Self::Error> {
223 match &object.data {
224 Data::Move(o) => {
225 if o.type_().is_alias_output() {
226 return AliasOutput::from_bcs_bytes(o.contents());
227 }
228 }
229 Data::Package(_) => {}
230 }
231
232 Err(IotaError::Type {
233 error: format!("Object type is not an AliasOutput: {:?}", object),
234 })
235 }
236}