1use alloc::vec::Vec;
5
6use packable::{
7 bounded::BoundedU16,
8 error::{UnpackError, UnpackErrorExt},
9 packer::Packer,
10 prefix::BoxedSlicePrefix,
11 unpacker::Unpacker,
12 Packable,
13};
14
15use crate::{
16 address::{Address, AliasAddress},
17 output::{
18 feature::{verify_allowed_features, Feature, FeatureFlags, Features},
19 unlock_condition::{verify_allowed_unlock_conditions, UnlockCondition, UnlockConditionFlags, UnlockConditions},
20 verify_output_amount, AliasId, ChainId, NativeToken, NativeTokens, Output, OutputBuilderAmount, OutputId, Rent,
21 RentStructure, StateTransitionError, StateTransitionVerifier,
22 },
23 protocol::ProtocolParameters,
24 semantic::{ConflictReason, ValidationContext},
25 unlock::Unlock,
26 Error,
27};
28
29#[derive(Clone)]
31#[must_use]
32pub struct AliasOutputBuilder {
33 amount: OutputBuilderAmount,
34 native_tokens: Vec<NativeToken>,
35 alias_id: AliasId,
36 state_index: Option<u32>,
37 state_metadata: Vec<u8>,
38 foundry_counter: Option<u32>,
39 unlock_conditions: Vec<UnlockCondition>,
40 features: Vec<Feature>,
41 immutable_features: Vec<Feature>,
42}
43
44impl AliasOutputBuilder {
45 pub fn new_with_amount(amount: u64, alias_id: AliasId) -> Result<AliasOutputBuilder, Error> {
47 Self::new(OutputBuilderAmount::Amount(amount), alias_id)
48 }
49
50 pub fn new_with_minimum_storage_deposit(
53 rent_structure: RentStructure,
54 alias_id: AliasId,
55 ) -> Result<AliasOutputBuilder, Error> {
56 Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_structure), alias_id)
57 }
58
59 fn new(amount: OutputBuilderAmount, alias_id: AliasId) -> Result<AliasOutputBuilder, Error> {
60 Ok(Self {
61 amount,
62 native_tokens: Vec::new(),
63 alias_id,
64 state_index: None,
65 state_metadata: Vec::new(),
66 foundry_counter: None,
67 unlock_conditions: Vec::new(),
68 features: Vec::new(),
69 immutable_features: Vec::new(),
70 })
71 }
72
73 #[inline(always)]
75 pub fn with_amount(mut self, amount: u64) -> Result<Self, Error> {
76 self.amount = OutputBuilderAmount::Amount(amount);
77 Ok(self)
78 }
79
80 #[inline(always)]
82 pub fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self {
83 self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_structure);
84 self
85 }
86
87 #[inline(always)]
89 pub fn add_native_token(mut self, native_token: NativeToken) -> Self {
90 self.native_tokens.push(native_token);
91 self
92 }
93
94 #[inline(always)]
96 pub fn with_native_tokens(mut self, native_tokens: impl IntoIterator<Item = NativeToken>) -> Self {
97 self.native_tokens = native_tokens.into_iter().collect();
98 self
99 }
100
101 #[inline(always)]
103 pub fn with_alias_id(mut self, alias_id: AliasId) -> Self {
104 self.alias_id = alias_id;
105 self
106 }
107
108 #[inline(always)]
110 pub fn with_state_index(mut self, state_index: u32) -> Self {
111 self.state_index.replace(state_index);
112 self
113 }
114
115 #[inline(always)]
117 pub fn with_state_metadata(mut self, state_metadata: Vec<u8>) -> Self {
118 self.state_metadata = state_metadata;
119 self
120 }
121
122 #[inline(always)]
124 pub fn with_foundry_counter(mut self, foundry_counter: u32) -> Self {
125 self.foundry_counter.replace(foundry_counter);
126 self
127 }
128
129 #[inline(always)]
131 pub fn add_unlock_condition(mut self, unlock_condition: UnlockCondition) -> Self {
132 self.unlock_conditions.push(unlock_condition);
133 self
134 }
135
136 #[inline(always)]
138 pub fn with_unlock_conditions(mut self, unlock_conditions: impl IntoIterator<Item = UnlockCondition>) -> Self {
139 self.unlock_conditions = unlock_conditions.into_iter().collect();
140 self
141 }
142
143 pub fn replace_unlock_condition(mut self, unlock_condition: UnlockCondition) -> Result<Self, Error> {
145 match self
146 .unlock_conditions
147 .iter_mut()
148 .find(|u| u.kind() == unlock_condition.kind())
149 {
150 Some(u) => *u = unlock_condition,
151 None => return Err(Error::CannotReplaceMissingField),
152 }
153 Ok(self)
154 }
155
156 #[inline(always)]
158 pub fn add_feature(mut self, feature: Feature) -> Self {
159 self.features.push(feature);
160 self
161 }
162
163 #[inline(always)]
165 pub fn with_features(mut self, features: impl IntoIterator<Item = Feature>) -> Self {
166 self.features = features.into_iter().collect();
167 self
168 }
169
170 pub fn replace_feature(mut self, feature: Feature) -> Result<Self, Error> {
172 match self.features.iter_mut().find(|f| f.kind() == feature.kind()) {
173 Some(f) => *f = feature,
174 None => return Err(Error::CannotReplaceMissingField),
175 }
176 Ok(self)
177 }
178
179 #[inline(always)]
181 pub fn add_immutable_feature(mut self, immutable_feature: Feature) -> Self {
182 self.immutable_features.push(immutable_feature);
183 self
184 }
185
186 #[inline(always)]
188 pub fn with_immutable_features(mut self, immutable_features: impl IntoIterator<Item = Feature>) -> Self {
189 self.immutable_features = immutable_features.into_iter().collect();
190 self
191 }
192
193 pub fn replace_immutable_feature(mut self, immutable_feature: Feature) -> Result<Self, Error> {
195 match self
196 .immutable_features
197 .iter_mut()
198 .find(|f| f.kind() == immutable_feature.kind())
199 {
200 Some(f) => *f = immutable_feature,
201 None => return Err(Error::CannotReplaceMissingField),
202 }
203 Ok(self)
204 }
205
206 pub fn finish(self, token_supply: u64) -> Result<AliasOutput, Error> {
208 let state_index = self.state_index.unwrap_or(0);
209 let foundry_counter = self.foundry_counter.unwrap_or(0);
210
211 let state_metadata = self
212 .state_metadata
213 .into_boxed_slice()
214 .try_into()
215 .map_err(Error::InvalidStateMetadataLength)?;
216
217 verify_index_counter(&self.alias_id, state_index, foundry_counter)?;
218
219 let unlock_conditions = UnlockConditions::new(self.unlock_conditions)?;
220
221 verify_unlock_conditions(&unlock_conditions, &self.alias_id)?;
222
223 let features = Features::new(self.features)?;
224
225 verify_allowed_features(&features, AliasOutput::ALLOWED_FEATURES)?;
226
227 let immutable_features = Features::new(self.immutable_features)?;
228
229 verify_allowed_features(&immutable_features, AliasOutput::ALLOWED_IMMUTABLE_FEATURES)?;
230
231 let mut output = AliasOutput {
232 amount: 1,
233 native_tokens: NativeTokens::new(self.native_tokens)?,
234 alias_id: self.alias_id,
235 state_index,
236 state_metadata,
237 foundry_counter,
238 unlock_conditions,
239 features,
240 immutable_features,
241 };
242
243 output.amount = match self.amount {
244 OutputBuilderAmount::Amount(amount) => amount,
245 OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => {
246 Output::Alias(output.clone()).rent_cost(&rent_structure)
247 }
248 };
249
250 verify_output_amount::<true>(&output.amount, &token_supply)?;
251
252 Ok(output)
253 }
254
255 pub fn finish_output(self, token_supply: u64) -> Result<Output, Error> {
257 Ok(Output::Alias(self.finish(token_supply)?))
258 }
259}
260
261impl From<&AliasOutput> for AliasOutputBuilder {
262 fn from(output: &AliasOutput) -> Self {
263 AliasOutputBuilder {
264 amount: OutputBuilderAmount::Amount(output.amount),
265 native_tokens: output.native_tokens.to_vec(),
266 alias_id: output.alias_id,
267 state_index: Some(output.state_index),
268 state_metadata: output.state_metadata.to_vec(),
269 foundry_counter: Some(output.foundry_counter),
270 unlock_conditions: output.unlock_conditions.to_vec(),
271 features: output.features.to_vec(),
272 immutable_features: output.immutable_features.to_vec(),
273 }
274 }
275}
276
277pub(crate) type StateMetadataLength = BoundedU16<0, { AliasOutput::STATE_METADATA_LENGTH_MAX }>;
278
279#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282pub struct AliasOutput {
283 amount: u64,
285 native_tokens: NativeTokens,
287 alias_id: AliasId,
289 state_index: u32,
291 state_metadata: BoxedSlicePrefix<u8, StateMetadataLength>,
293 foundry_counter: u32,
295 unlock_conditions: UnlockConditions,
296 features: Features,
298 immutable_features: Features,
300}
301
302impl AliasOutput {
303 pub const KIND: u8 = 4;
305 pub const STATE_METADATA_LENGTH_MAX: u16 = 8192;
307 pub const ALLOWED_UNLOCK_CONDITIONS: UnlockConditionFlags =
309 UnlockConditionFlags::STATE_CONTROLLER_ADDRESS.union(UnlockConditionFlags::GOVERNOR_ADDRESS);
310 pub const ALLOWED_FEATURES: FeatureFlags = FeatureFlags::SENDER.union(FeatureFlags::METADATA);
312 pub const ALLOWED_IMMUTABLE_FEATURES: FeatureFlags = FeatureFlags::ISSUER.union(FeatureFlags::METADATA);
314
315 #[inline(always)]
317 pub fn new_with_amount(amount: u64, alias_id: AliasId, token_supply: u64) -> Result<Self, Error> {
318 AliasOutputBuilder::new_with_amount(amount, alias_id)?.finish(token_supply)
319 }
320
321 #[inline(always)]
324 pub fn new_with_minimum_storage_deposit(
325 alias_id: AliasId,
326 rent_structure: RentStructure,
327 token_supply: u64,
328 ) -> Result<Self, Error> {
329 AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, alias_id)?.finish(token_supply)
330 }
331
332 #[inline(always)]
334 pub fn build_with_amount(amount: u64, alias_id: AliasId) -> Result<AliasOutputBuilder, Error> {
335 AliasOutputBuilder::new_with_amount(amount, alias_id)
336 }
337
338 #[inline(always)]
341 pub fn build_with_minimum_storage_deposit(
342 rent_structure: RentStructure,
343 alias_id: AliasId,
344 ) -> Result<AliasOutputBuilder, Error> {
345 AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, alias_id)
346 }
347
348 #[inline(always)]
350 pub fn amount(&self) -> u64 {
351 self.amount
352 }
353
354 #[inline(always)]
356 pub fn native_tokens(&self) -> &NativeTokens {
357 &self.native_tokens
358 }
359
360 #[inline(always)]
362 pub fn alias_id(&self) -> &AliasId {
363 &self.alias_id
364 }
365
366 #[inline(always)]
368 pub fn state_index(&self) -> u32 {
369 self.state_index
370 }
371
372 #[inline(always)]
374 pub fn state_metadata(&self) -> &[u8] {
375 &self.state_metadata
376 }
377
378 #[inline(always)]
380 pub fn foundry_counter(&self) -> u32 {
381 self.foundry_counter
382 }
383
384 #[inline(always)]
386 pub fn unlock_conditions(&self) -> &UnlockConditions {
387 &self.unlock_conditions
388 }
389
390 #[inline(always)]
392 pub fn features(&self) -> &Features {
393 &self.features
394 }
395
396 #[inline(always)]
398 pub fn immutable_features(&self) -> &Features {
399 &self.immutable_features
400 }
401
402 #[inline(always)]
404 pub fn state_controller_address(&self) -> &Address {
405 self.unlock_conditions
407 .state_controller_address()
408 .map(|unlock_condition| unlock_condition.address())
409 .unwrap()
410 }
411
412 #[inline(always)]
414 pub fn governor_address(&self) -> &Address {
415 self.unlock_conditions
417 .governor_address()
418 .map(|unlock_condition| unlock_condition.address())
419 .unwrap()
420 }
421
422 #[inline(always)]
424 pub fn chain_id(&self) -> ChainId {
425 ChainId::Alias(self.alias_id)
426 }
427
428 pub fn unlock(
430 &self,
431 output_id: &OutputId,
432 unlock: &Unlock,
433 inputs: &[(OutputId, &Output)],
434 context: &mut ValidationContext,
435 ) -> Result<(), ConflictReason> {
436 let alias_id = if self.alias_id().is_null() {
437 AliasId::from(*output_id)
438 } else {
439 *self.alias_id()
440 };
441 let next_state = context.output_chains.get(&ChainId::from(alias_id));
442
443 match next_state {
444 Some(Output::Alias(next_state)) => {
445 if self.state_index() == next_state.state_index() {
446 self.governor_address().unlock(unlock, inputs, context)?;
447 } else {
448 self.state_controller_address().unlock(unlock, inputs, context)?;
449 context
452 .unlocked_addresses
453 .insert(Address::from(AliasAddress::from(alias_id)));
454 }
455 }
456 None => self.governor_address().unlock(unlock, inputs, context)?,
457 Some(_) => unreachable!(),
459 };
460
461 Ok(())
462 }
463}
464
465impl StateTransitionVerifier for AliasOutput {
466 fn creation(next_state: &Self, context: &ValidationContext) -> Result<(), StateTransitionError> {
467 if !next_state.alias_id.is_null() {
468 return Err(StateTransitionError::NonZeroCreatedId);
469 }
470
471 if let Some(issuer) = next_state.immutable_features().issuer() {
472 if !context.unlocked_addresses.contains(issuer.address()) {
473 return Err(StateTransitionError::IssuerNotUnlocked);
474 }
475 }
476
477 Ok(())
478 }
479
480 fn transition(
481 current_state: &Self,
482 next_state: &Self,
483 context: &ValidationContext,
484 ) -> Result<(), StateTransitionError> {
485 if current_state.immutable_features != next_state.immutable_features {
486 return Err(StateTransitionError::MutatedImmutableField);
487 }
488
489 if next_state.state_index == current_state.state_index + 1 {
490 if current_state.state_controller_address() != next_state.state_controller_address()
492 || current_state.governor_address() != next_state.governor_address()
493 || current_state.features.metadata() != next_state.features.metadata()
494 {
495 return Err(StateTransitionError::MutatedFieldWithoutRights);
496 }
497
498 let created_foundries = context.essence.outputs().iter().filter_map(|output| {
499 if let Output::Foundry(foundry) = output {
500 if foundry.alias_address().alias_id() == &next_state.alias_id
501 && !context.input_chains.contains_key(&foundry.chain_id())
502 {
503 Some(foundry)
504 } else {
505 None
506 }
507 } else {
508 None
509 }
510 });
511
512 let mut created_foundries_count = 0;
513
514 for foundry in created_foundries {
515 created_foundries_count += 1;
516
517 if foundry.serial_number() != current_state.foundry_counter + created_foundries_count {
518 return Err(StateTransitionError::UnsortedCreatedFoundries);
519 }
520 }
521
522 if current_state.foundry_counter + created_foundries_count != next_state.foundry_counter {
523 return Err(StateTransitionError::InconsistentCreatedFoundriesCount);
524 }
525 } else if next_state.state_index == current_state.state_index {
526 if current_state.amount != next_state.amount
528 || current_state.native_tokens != next_state.native_tokens
529 || current_state.state_metadata != next_state.state_metadata
530 || current_state.foundry_counter != next_state.foundry_counter
531 {
532 return Err(StateTransitionError::MutatedFieldWithoutRights);
533 }
534 } else {
535 return Err(StateTransitionError::UnsupportedStateIndexOperation {
536 current_state: current_state.state_index,
537 next_state: next_state.state_index,
538 });
539 }
540
541 Ok(())
542 }
543
544 fn destruction(_current_state: &Self, _context: &ValidationContext) -> Result<(), StateTransitionError> {
545 Ok(())
546 }
547}
548
549impl Packable for AliasOutput {
550 type UnpackError = Error;
551 type UnpackVisitor = ProtocolParameters;
552
553 fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
554 self.amount.pack(packer)?;
555 self.native_tokens.pack(packer)?;
556 self.alias_id.pack(packer)?;
557 self.state_index.pack(packer)?;
558 self.state_metadata.pack(packer)?;
559 self.foundry_counter.pack(packer)?;
560 self.unlock_conditions.pack(packer)?;
561 self.features.pack(packer)?;
562 self.immutable_features.pack(packer)?;
563
564 Ok(())
565 }
566
567 fn unpack<U: Unpacker, const VERIFY: bool>(
568 unpacker: &mut U,
569 visitor: &Self::UnpackVisitor,
570 ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
571 let amount = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
572
573 verify_output_amount::<VERIFY>(&amount, &visitor.token_supply()).map_err(UnpackError::Packable)?;
574
575 let native_tokens = NativeTokens::unpack::<_, VERIFY>(unpacker, &())?;
576 let alias_id = AliasId::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
577 let state_index = u32::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
578 let state_metadata = BoxedSlicePrefix::<u8, StateMetadataLength>::unpack::<_, VERIFY>(unpacker, &())
579 .map_packable_err(|err| Error::InvalidStateMetadataLength(err.into_prefix_err().into()))?;
580
581 let foundry_counter = u32::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
582
583 if VERIFY {
584 verify_index_counter(&alias_id, state_index, foundry_counter).map_err(UnpackError::Packable)?;
585 }
586
587 let unlock_conditions = UnlockConditions::unpack::<_, VERIFY>(unpacker, visitor)?;
588
589 if VERIFY {
590 verify_unlock_conditions(&unlock_conditions, &alias_id).map_err(UnpackError::Packable)?;
591 }
592
593 let features = Features::unpack::<_, VERIFY>(unpacker, &())?;
594
595 if VERIFY {
596 verify_allowed_features(&features, AliasOutput::ALLOWED_FEATURES).map_err(UnpackError::Packable)?;
597 }
598
599 let immutable_features = Features::unpack::<_, VERIFY>(unpacker, &())?;
600
601 if VERIFY {
602 verify_allowed_features(&immutable_features, AliasOutput::ALLOWED_IMMUTABLE_FEATURES)
603 .map_err(UnpackError::Packable)?;
604 }
605
606 Ok(Self {
607 amount,
608 native_tokens,
609 alias_id,
610 state_index,
611 state_metadata,
612 foundry_counter,
613 unlock_conditions,
614 features,
615 immutable_features,
616 })
617 }
618}
619
620#[inline]
621fn verify_index_counter(alias_id: &AliasId, state_index: u32, foundry_counter: u32) -> Result<(), Error> {
622 if alias_id.is_null() && (state_index != 0 || foundry_counter != 0) {
623 Err(Error::NonZeroStateIndexOrFoundryCounter)
624 } else {
625 Ok(())
626 }
627}
628
629fn verify_unlock_conditions(unlock_conditions: &UnlockConditions, alias_id: &AliasId) -> Result<(), Error> {
630 if let Some(unlock_condition) = unlock_conditions.state_controller_address() {
631 if let Address::Alias(alias_address) = unlock_condition.address() {
632 if alias_address.alias_id() == alias_id {
633 return Err(Error::SelfControlledAliasOutput(*alias_id));
634 }
635 }
636 } else {
637 return Err(Error::MissingStateControllerUnlockCondition);
638 }
639
640 if let Some(unlock_condition) = unlock_conditions.governor_address() {
641 if let Address::Alias(alias_address) = unlock_condition.address() {
642 if alias_address.alias_id() == alias_id {
643 return Err(Error::SelfControlledAliasOutput(*alias_id));
644 }
645 }
646 } else {
647 return Err(Error::MissingGovernorUnlockCondition);
648 }
649
650 verify_allowed_unlock_conditions(unlock_conditions, AliasOutput::ALLOWED_UNLOCK_CONDITIONS)
651}
652
653#[cfg(feature = "dto")]
654#[allow(missing_docs)]
655pub mod dto {
656 use serde::{Deserialize, Serialize};
657
658 use super::*;
659 use crate::{
660 error::dto::DtoError,
661 output::{
662 alias_id::dto::AliasIdDto, dto::OutputBuilderAmountDto, feature::dto::FeatureDto,
663 native_token::dto::NativeTokenDto, unlock_condition::dto::UnlockConditionDto,
664 },
665 };
666
667 #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
669 pub struct AliasOutputDto {
670 #[serde(rename = "type")]
671 pub kind: u8,
672 pub amount: String,
674 #[serde(rename = "nativeTokens", skip_serializing_if = "Vec::is_empty", default)]
676 pub native_tokens: Vec<NativeTokenDto>,
677 #[serde(rename = "aliasId")]
679 pub alias_id: AliasIdDto,
680 #[serde(rename = "stateIndex")]
682 pub state_index: u32,
683 #[serde(rename = "stateMetadata", skip_serializing_if = "String::is_empty", default)]
685 pub state_metadata: String,
686 #[serde(rename = "foundryCounter")]
688 pub foundry_counter: u32,
689 #[serde(rename = "unlockConditions")]
691 pub unlock_conditions: Vec<UnlockConditionDto>,
692 #[serde(skip_serializing_if = "Vec::is_empty", default)]
694 pub features: Vec<FeatureDto>,
695 #[serde(rename = "immutableFeatures", skip_serializing_if = "Vec::is_empty", default)]
697 pub immutable_features: Vec<FeatureDto>,
698 }
699
700 impl From<&AliasOutput> for AliasOutputDto {
701 fn from(value: &AliasOutput) -> Self {
702 Self {
703 kind: AliasOutput::KIND,
704 amount: value.amount().to_string(),
705 native_tokens: value.native_tokens().iter().map(Into::into).collect::<_>(),
706 alias_id: AliasIdDto(value.alias_id().to_string()),
707 state_index: value.state_index(),
708 state_metadata: prefix_hex::encode(value.state_metadata()),
709 foundry_counter: value.foundry_counter(),
710 unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(),
711 features: value.features().iter().map(Into::into).collect::<_>(),
712 immutable_features: value.immutable_features().iter().map(Into::into).collect::<_>(),
713 }
714 }
715 }
716
717 impl AliasOutput {
718 pub fn try_from_dto(value: &AliasOutputDto, token_supply: u64) -> Result<AliasOutput, DtoError> {
719 let mut builder = AliasOutputBuilder::new_with_amount(
720 value
721 .amount
722 .parse::<u64>()
723 .map_err(|_| DtoError::InvalidField("amount"))?,
724 (&value.alias_id).try_into()?,
725 )?;
726
727 builder = builder.with_state_index(value.state_index);
728
729 if !value.state_metadata.is_empty() {
730 builder = builder.with_state_metadata(
731 prefix_hex::decode(&value.state_metadata).map_err(|_| DtoError::InvalidField("state_metadata"))?,
732 );
733 }
734
735 builder = builder.with_foundry_counter(value.foundry_counter);
736
737 for t in &value.native_tokens {
738 builder = builder.add_native_token(t.try_into()?);
739 }
740
741 for u in &value.unlock_conditions {
742 builder = builder.add_unlock_condition(UnlockCondition::try_from_dto(u, token_supply)?);
743 }
744
745 for b in &value.features {
746 builder = builder.add_feature(b.try_into()?);
747 }
748
749 for b in &value.immutable_features {
750 builder = builder.add_immutable_feature(b.try_into()?);
751 }
752
753 Ok(builder.finish(token_supply)?)
754 }
755
756 #[allow(clippy::too_many_arguments)]
757 pub fn try_from_dtos(
758 amount: OutputBuilderAmountDto,
759 native_tokens: Option<Vec<NativeTokenDto>>,
760 alias_id: &AliasIdDto,
761 state_index: Option<u32>,
762 state_metadata: Option<Vec<u8>>,
763 foundry_counter: Option<u32>,
764 unlock_conditions: Vec<UnlockConditionDto>,
765 features: Option<Vec<FeatureDto>>,
766 immutable_features: Option<Vec<FeatureDto>>,
767 token_supply: u64,
768 ) -> Result<AliasOutput, DtoError> {
769 let alias_id = AliasId::try_from(alias_id)?;
770
771 let mut builder = match amount {
772 OutputBuilderAmountDto::Amount(amount) => AliasOutputBuilder::new_with_amount(
773 amount.parse().map_err(|_| DtoError::InvalidField("amount"))?,
774 alias_id,
775 )?,
776 OutputBuilderAmountDto::MinimumStorageDeposit(rent_structure) => {
777 AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, alias_id)?
778 }
779 };
780
781 if let Some(native_tokens) = native_tokens {
782 let native_tokens = native_tokens
783 .iter()
784 .map(NativeToken::try_from)
785 .collect::<Result<Vec<NativeToken>, DtoError>>()?;
786 builder = builder.with_native_tokens(native_tokens);
787 }
788
789 if let Some(state_index) = state_index {
790 builder = builder.with_state_index(state_index);
791 }
792
793 if let Some(state_metadata) = state_metadata {
794 builder = builder.with_state_metadata(state_metadata);
795 }
796
797 if let Some(foundry_counter) = foundry_counter {
798 builder = builder.with_foundry_counter(foundry_counter);
799 }
800
801 let unlock_conditions = unlock_conditions
802 .iter()
803 .map(|u| UnlockCondition::try_from_dto(u, token_supply))
804 .collect::<Result<Vec<UnlockCondition>, DtoError>>()?;
805 builder = builder.with_unlock_conditions(unlock_conditions);
806
807 if let Some(features) = features {
808 let features = features
809 .iter()
810 .map(Feature::try_from)
811 .collect::<Result<Vec<Feature>, DtoError>>()?;
812 builder = builder.with_features(features);
813 }
814
815 if let Some(immutable_features) = immutable_features {
816 let immutable_features = immutable_features
817 .iter()
818 .map(Feature::try_from)
819 .collect::<Result<Vec<Feature>, DtoError>>()?;
820 builder = builder.with_immutable_features(immutable_features);
821 }
822
823 Ok(builder.finish(token_supply)?)
824 }
825 }
826}