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::block::{
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(Copy, Clone, Debug, Eq, PartialEq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub enum AliasTransition {
33 State,
35 Governance,
37}
38
39impl AliasTransition {
40 pub fn is_state(&self) -> bool {
42 matches!(self, Self::State)
43 }
44
45 pub fn is_governance(&self) -> bool {
47 matches!(self, Self::Governance)
48 }
49}
50
51impl std::fmt::Display for AliasTransition {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 match self {
54 Self::State => write!(f, "state"),
55 Self::Governance => write!(f, "governance"),
56 }
57 }
58}
59
60#[derive(Clone)]
62#[must_use]
63pub struct AliasOutputBuilder {
64 amount: OutputBuilderAmount,
65 native_tokens: Vec<NativeToken>,
66 alias_id: AliasId,
67 state_index: Option<u32>,
68 state_metadata: Vec<u8>,
69 foundry_counter: Option<u32>,
70 unlock_conditions: Vec<UnlockCondition>,
71 features: Vec<Feature>,
72 immutable_features: Vec<Feature>,
73}
74
75impl AliasOutputBuilder {
76 pub fn new_with_amount(amount: u64, alias_id: AliasId) -> Result<Self, Error> {
78 Self::new(OutputBuilderAmount::Amount(amount), alias_id)
79 }
80
81 pub fn new_with_minimum_storage_deposit(rent_structure: RentStructure, alias_id: AliasId) -> Result<Self, Error> {
84 Self::new(OutputBuilderAmount::MinimumStorageDeposit(rent_structure), alias_id)
85 }
86
87 fn new(amount: OutputBuilderAmount, alias_id: AliasId) -> Result<Self, Error> {
88 Ok(Self {
89 amount,
90 native_tokens: Vec::new(),
91 alias_id,
92 state_index: None,
93 state_metadata: Vec::new(),
94 foundry_counter: None,
95 unlock_conditions: Vec::new(),
96 features: Vec::new(),
97 immutable_features: Vec::new(),
98 })
99 }
100
101 #[inline(always)]
103 pub fn with_amount(mut self, amount: u64) -> Result<Self, Error> {
104 self.amount = OutputBuilderAmount::Amount(amount);
105 Ok(self)
106 }
107
108 #[inline(always)]
110 pub fn with_minimum_storage_deposit(mut self, rent_structure: RentStructure) -> Self {
111 self.amount = OutputBuilderAmount::MinimumStorageDeposit(rent_structure);
112 self
113 }
114
115 #[inline(always)]
117 pub fn add_native_token(mut self, native_token: NativeToken) -> Self {
118 self.native_tokens.push(native_token);
119 self
120 }
121
122 #[inline(always)]
124 pub fn with_native_tokens(mut self, native_tokens: impl IntoIterator<Item = NativeToken>) -> Self {
125 self.native_tokens = native_tokens.into_iter().collect();
126 self
127 }
128
129 #[inline(always)]
131 pub fn with_alias_id(mut self, alias_id: AliasId) -> Self {
132 self.alias_id = alias_id;
133 self
134 }
135
136 #[inline(always)]
138 pub fn with_state_index(mut self, state_index: u32) -> Self {
139 self.state_index.replace(state_index);
140 self
141 }
142
143 #[inline(always)]
145 pub fn with_state_metadata(mut self, state_metadata: Vec<u8>) -> Self {
146 self.state_metadata = state_metadata;
147 self
148 }
149
150 #[inline(always)]
152 pub fn with_foundry_counter(mut self, foundry_counter: u32) -> Self {
153 self.foundry_counter.replace(foundry_counter);
154 self
155 }
156
157 #[inline(always)]
159 pub fn add_unlock_condition(mut self, unlock_condition: UnlockCondition) -> Self {
160 self.unlock_conditions.push(unlock_condition);
161 self
162 }
163
164 #[inline(always)]
166 pub fn with_unlock_conditions(mut self, unlock_conditions: impl IntoIterator<Item = UnlockCondition>) -> Self {
167 self.unlock_conditions = unlock_conditions.into_iter().collect();
168 self
169 }
170
171 pub fn replace_unlock_condition(mut self, unlock_condition: UnlockCondition) -> Self {
173 match self
174 .unlock_conditions
175 .iter_mut()
176 .find(|u| u.kind() == unlock_condition.kind())
177 {
178 Some(u) => *u = unlock_condition,
179 None => self.unlock_conditions.push(unlock_condition),
180 }
181 self
182 }
183
184 #[inline(always)]
186 pub fn add_feature(mut self, feature: Feature) -> Self {
187 self.features.push(feature);
188 self
189 }
190
191 #[inline(always)]
193 pub fn with_features(mut self, features: impl IntoIterator<Item = Feature>) -> Self {
194 self.features = features.into_iter().collect();
195 self
196 }
197
198 pub fn replace_feature(mut self, feature: Feature) -> Self {
200 match self.features.iter_mut().find(|f| f.kind() == feature.kind()) {
201 Some(f) => *f = feature,
202 None => self.features.push(feature),
203 }
204 self
205 }
206
207 #[inline(always)]
209 pub fn add_immutable_feature(mut self, immutable_feature: Feature) -> Self {
210 self.immutable_features.push(immutable_feature);
211 self
212 }
213
214 #[inline(always)]
216 pub fn with_immutable_features(mut self, immutable_features: impl IntoIterator<Item = Feature>) -> Self {
217 self.immutable_features = immutable_features.into_iter().collect();
218 self
219 }
220
221 pub fn replace_immutable_feature(mut self, immutable_feature: Feature) -> Self {
223 match self
224 .immutable_features
225 .iter_mut()
226 .find(|f| f.kind() == immutable_feature.kind())
227 {
228 Some(f) => *f = immutable_feature,
229 None => self.immutable_features.push(immutable_feature),
230 }
231 self
232 }
233
234 pub fn finish_unverified(self) -> Result<AliasOutput, Error> {
236 let state_index = self.state_index.unwrap_or(0);
237 let foundry_counter = self.foundry_counter.unwrap_or(0);
238
239 let state_metadata = self
240 .state_metadata
241 .into_boxed_slice()
242 .try_into()
243 .map_err(Error::InvalidStateMetadataLength)?;
244
245 verify_index_counter(&self.alias_id, state_index, foundry_counter)?;
246
247 let unlock_conditions = UnlockConditions::new(self.unlock_conditions)?;
248
249 verify_unlock_conditions(&unlock_conditions, &self.alias_id)?;
250
251 let features = Features::new(self.features)?;
252
253 verify_allowed_features(&features, AliasOutput::ALLOWED_FEATURES)?;
254
255 let immutable_features = Features::new(self.immutable_features)?;
256
257 verify_allowed_features(&immutable_features, AliasOutput::ALLOWED_IMMUTABLE_FEATURES)?;
258
259 let mut output = AliasOutput {
260 amount: 1,
261 native_tokens: NativeTokens::new(self.native_tokens)?,
262 alias_id: self.alias_id,
263 state_index,
264 state_metadata,
265 foundry_counter,
266 unlock_conditions,
267 features,
268 immutable_features,
269 };
270
271 output.amount = match self.amount {
272 OutputBuilderAmount::Amount(amount) => amount,
273 OutputBuilderAmount::MinimumStorageDeposit(rent_structure) => {
274 Output::Alias(output.clone()).rent_cost(&rent_structure)
275 }
276 };
277
278 Ok(output)
279 }
280
281 pub fn finish(self, token_supply: u64) -> Result<AliasOutput, Error> {
283 let output = self.finish_unverified()?;
284
285 verify_output_amount::<true>(&output.amount, &token_supply)?;
286
287 Ok(output)
288 }
289
290 pub fn finish_output(self, token_supply: u64) -> Result<Output, Error> {
292 Ok(Output::Alias(self.finish(token_supply)?))
293 }
294}
295
296impl From<&AliasOutput> for AliasOutputBuilder {
297 fn from(output: &AliasOutput) -> Self {
298 Self {
299 amount: OutputBuilderAmount::Amount(output.amount),
300 native_tokens: output.native_tokens.to_vec(),
301 alias_id: output.alias_id,
302 state_index: Some(output.state_index),
303 state_metadata: output.state_metadata.to_vec(),
304 foundry_counter: Some(output.foundry_counter),
305 unlock_conditions: output.unlock_conditions.to_vec(),
306 features: output.features.to_vec(),
307 immutable_features: output.immutable_features.to_vec(),
308 }
309 }
310}
311
312pub(crate) type StateMetadataLength = BoundedU16<0, { AliasOutput::STATE_METADATA_LENGTH_MAX }>;
313
314#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
316#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
317pub struct AliasOutput {
318 amount: u64,
320 native_tokens: NativeTokens,
322 alias_id: AliasId,
324 state_index: u32,
326 state_metadata: BoxedSlicePrefix<u8, StateMetadataLength>,
328 foundry_counter: u32,
330 unlock_conditions: UnlockConditions,
331 features: Features,
333 immutable_features: Features,
335}
336
337impl AliasOutput {
338 pub const KIND: u8 = 4;
340 pub const STATE_METADATA_LENGTH_MAX: u16 = 8192;
342 pub const ALLOWED_UNLOCK_CONDITIONS: UnlockConditionFlags =
344 UnlockConditionFlags::STATE_CONTROLLER_ADDRESS.union(UnlockConditionFlags::GOVERNOR_ADDRESS);
345 pub const ALLOWED_FEATURES: FeatureFlags = FeatureFlags::SENDER.union(FeatureFlags::METADATA);
347 pub const ALLOWED_IMMUTABLE_FEATURES: FeatureFlags = FeatureFlags::ISSUER.union(FeatureFlags::METADATA);
349
350 #[inline(always)]
352 pub fn new_with_amount(amount: u64, alias_id: AliasId, token_supply: u64) -> Result<Self, Error> {
353 AliasOutputBuilder::new_with_amount(amount, alias_id)?.finish(token_supply)
354 }
355
356 #[inline(always)]
359 pub fn new_with_minimum_storage_deposit(
360 alias_id: AliasId,
361 rent_structure: RentStructure,
362 token_supply: u64,
363 ) -> Result<Self, Error> {
364 AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, alias_id)?.finish(token_supply)
365 }
366
367 #[inline(always)]
369 pub fn build_with_amount(amount: u64, alias_id: AliasId) -> Result<AliasOutputBuilder, Error> {
370 AliasOutputBuilder::new_with_amount(amount, alias_id)
371 }
372
373 #[inline(always)]
376 pub fn build_with_minimum_storage_deposit(
377 rent_structure: RentStructure,
378 alias_id: AliasId,
379 ) -> Result<AliasOutputBuilder, Error> {
380 AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, alias_id)
381 }
382
383 #[inline(always)]
385 pub fn amount(&self) -> u64 {
386 self.amount
387 }
388
389 #[inline(always)]
391 pub fn native_tokens(&self) -> &NativeTokens {
392 &self.native_tokens
393 }
394
395 #[inline(always)]
397 pub fn alias_id(&self) -> &AliasId {
398 &self.alias_id
399 }
400
401 #[inline(always)]
403 pub fn alias_id_non_null(&self, output_id: &OutputId) -> AliasId {
404 self.alias_id.or_from_output_id(output_id)
405 }
406
407 #[inline(always)]
409 pub fn state_index(&self) -> u32 {
410 self.state_index
411 }
412
413 #[inline(always)]
415 pub fn state_metadata(&self) -> &[u8] {
416 &self.state_metadata
417 }
418
419 #[inline(always)]
421 pub fn foundry_counter(&self) -> u32 {
422 self.foundry_counter
423 }
424
425 #[inline(always)]
427 pub fn unlock_conditions(&self) -> &UnlockConditions {
428 &self.unlock_conditions
429 }
430
431 #[inline(always)]
433 pub fn features(&self) -> &Features {
434 &self.features
435 }
436
437 #[inline(always)]
439 pub fn immutable_features(&self) -> &Features {
440 &self.immutable_features
441 }
442
443 #[inline(always)]
445 pub fn state_controller_address(&self) -> &Address {
446 self.unlock_conditions
448 .state_controller_address()
449 .map(|unlock_condition| unlock_condition.address())
450 .unwrap()
451 }
452
453 #[inline(always)]
455 pub fn governor_address(&self) -> &Address {
456 self.unlock_conditions
458 .governor_address()
459 .map(|unlock_condition| unlock_condition.address())
460 .unwrap()
461 }
462
463 #[inline(always)]
465 pub fn chain_id(&self) -> ChainId {
466 ChainId::Alias(self.alias_id)
467 }
468
469 pub fn alias_address(&self, output_id: &OutputId) -> AliasAddress {
471 AliasAddress::new(self.alias_id_non_null(output_id))
472 }
473
474 pub fn unlock(
476 &self,
477 output_id: &OutputId,
478 unlock: &Unlock,
479 inputs: &[(OutputId, &Output)],
480 context: &mut ValidationContext<'_>,
481 ) -> Result<(), ConflictReason> {
482 let alias_id = if self.alias_id().is_null() {
483 AliasId::from(output_id)
484 } else {
485 *self.alias_id()
486 };
487 let next_state = context.output_chains.get(&ChainId::from(alias_id));
488
489 match next_state {
490 Some(Output::Alias(next_state)) => {
491 if self.state_index() == next_state.state_index() {
492 self.governor_address().unlock(unlock, inputs, context)?;
493 } else {
494 self.state_controller_address().unlock(unlock, inputs, context)?;
495 context
498 .unlocked_addresses
499 .insert(Address::from(AliasAddress::from(alias_id)));
500 }
501 }
502 None => self.governor_address().unlock(unlock, inputs, context)?,
503 Some(_) => unreachable!(),
505 };
506
507 Ok(())
508 }
509}
510
511impl StateTransitionVerifier for AliasOutput {
512 fn creation(next_state: &Self, context: &ValidationContext<'_>) -> Result<(), StateTransitionError> {
513 if !next_state.alias_id.is_null() {
514 return Err(StateTransitionError::NonZeroCreatedId);
515 }
516
517 if let Some(issuer) = next_state.immutable_features().issuer() {
518 if !context.unlocked_addresses.contains(issuer.address()) {
519 return Err(StateTransitionError::IssuerNotUnlocked);
520 }
521 }
522
523 Ok(())
524 }
525
526 fn transition(
527 current_state: &Self,
528 next_state: &Self,
529 context: &ValidationContext<'_>,
530 ) -> Result<(), StateTransitionError> {
531 if current_state.immutable_features != next_state.immutable_features {
532 return Err(StateTransitionError::MutatedImmutableField);
533 }
534
535 if next_state.state_index == current_state.state_index + 1 {
536 if current_state.state_controller_address() != next_state.state_controller_address()
538 || current_state.governor_address() != next_state.governor_address()
539 || current_state.features.metadata() != next_state.features.metadata()
540 {
541 return Err(StateTransitionError::MutatedFieldWithoutRights);
542 }
543
544 let created_foundries = context.essence.outputs().iter().filter_map(|output| {
545 if let Output::Foundry(foundry) = output {
546 if foundry.alias_address().alias_id() == &next_state.alias_id
547 && !context.input_chains.contains_key(&foundry.chain_id())
548 {
549 Some(foundry)
550 } else {
551 None
552 }
553 } else {
554 None
555 }
556 });
557
558 let mut created_foundries_count = 0;
559
560 for foundry in created_foundries {
561 created_foundries_count += 1;
562
563 if foundry.serial_number() != current_state.foundry_counter + created_foundries_count {
564 return Err(StateTransitionError::UnsortedCreatedFoundries);
565 }
566 }
567
568 if current_state.foundry_counter + created_foundries_count != next_state.foundry_counter {
569 return Err(StateTransitionError::InconsistentCreatedFoundriesCount);
570 }
571 } else if next_state.state_index == current_state.state_index {
572 if current_state.amount != next_state.amount
574 || current_state.native_tokens != next_state.native_tokens
575 || current_state.state_metadata != next_state.state_metadata
576 || current_state.foundry_counter != next_state.foundry_counter
577 {
578 return Err(StateTransitionError::MutatedFieldWithoutRights);
579 }
580 } else {
581 return Err(StateTransitionError::UnsupportedStateIndexOperation {
582 current_state: current_state.state_index,
583 next_state: next_state.state_index,
584 });
585 }
586
587 Ok(())
588 }
589
590 fn destruction(_current_state: &Self, _context: &ValidationContext<'_>) -> Result<(), StateTransitionError> {
591 Ok(())
592 }
593}
594
595impl Packable for AliasOutput {
596 type UnpackError = Error;
597 type UnpackVisitor = ProtocolParameters;
598
599 fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
600 self.amount.pack(packer)?;
601 self.native_tokens.pack(packer)?;
602 self.alias_id.pack(packer)?;
603 self.state_index.pack(packer)?;
604 self.state_metadata.pack(packer)?;
605 self.foundry_counter.pack(packer)?;
606 self.unlock_conditions.pack(packer)?;
607 self.features.pack(packer)?;
608 self.immutable_features.pack(packer)?;
609
610 Ok(())
611 }
612
613 fn unpack<U: Unpacker, const VERIFY: bool>(
614 unpacker: &mut U,
615 visitor: &Self::UnpackVisitor,
616 ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
617 let amount = u64::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
618
619 verify_output_amount::<VERIFY>(&amount, &visitor.token_supply()).map_err(UnpackError::Packable)?;
620
621 let native_tokens = NativeTokens::unpack::<_, VERIFY>(unpacker, &())?;
622 let alias_id = AliasId::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
623 let state_index = u32::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
624 let state_metadata = BoxedSlicePrefix::<u8, StateMetadataLength>::unpack::<_, VERIFY>(unpacker, &())
625 .map_packable_err(|err| Error::InvalidStateMetadataLength(err.into_prefix_err().into()))?;
626
627 let foundry_counter = u32::unpack::<_, VERIFY>(unpacker, &()).coerce()?;
628
629 if VERIFY {
630 verify_index_counter(&alias_id, state_index, foundry_counter).map_err(UnpackError::Packable)?;
631 }
632
633 let unlock_conditions = UnlockConditions::unpack::<_, VERIFY>(unpacker, visitor)?;
634
635 if VERIFY {
636 verify_unlock_conditions(&unlock_conditions, &alias_id).map_err(UnpackError::Packable)?;
637 }
638
639 let features = Features::unpack::<_, VERIFY>(unpacker, &())?;
640
641 if VERIFY {
642 verify_allowed_features(&features, Self::ALLOWED_FEATURES).map_err(UnpackError::Packable)?;
643 }
644
645 let immutable_features = Features::unpack::<_, VERIFY>(unpacker, &())?;
646
647 if VERIFY {
648 verify_allowed_features(&immutable_features, Self::ALLOWED_IMMUTABLE_FEATURES)
649 .map_err(UnpackError::Packable)?;
650 }
651
652 Ok(Self {
653 amount,
654 native_tokens,
655 alias_id,
656 state_index,
657 state_metadata,
658 foundry_counter,
659 unlock_conditions,
660 features,
661 immutable_features,
662 })
663 }
664}
665
666#[inline]
667fn verify_index_counter(alias_id: &AliasId, state_index: u32, foundry_counter: u32) -> Result<(), Error> {
668 if alias_id.is_null() && (state_index != 0 || foundry_counter != 0) {
669 Err(Error::NonZeroStateIndexOrFoundryCounter)
670 } else {
671 Ok(())
672 }
673}
674
675fn verify_unlock_conditions(unlock_conditions: &UnlockConditions, alias_id: &AliasId) -> Result<(), Error> {
676 if let Some(unlock_condition) = unlock_conditions.state_controller_address() {
677 if let Address::Alias(alias_address) = unlock_condition.address() {
678 if alias_address.alias_id() == alias_id {
679 return Err(Error::SelfControlledAliasOutput(*alias_id));
680 }
681 }
682 } else {
683 return Err(Error::MissingStateControllerUnlockCondition);
684 }
685
686 if let Some(unlock_condition) = unlock_conditions.governor_address() {
687 if let Address::Alias(alias_address) = unlock_condition.address() {
688 if alias_address.alias_id() == alias_id {
689 return Err(Error::SelfControlledAliasOutput(*alias_id));
690 }
691 }
692 } else {
693 return Err(Error::MissingGovernorUnlockCondition);
694 }
695
696 verify_allowed_unlock_conditions(unlock_conditions, AliasOutput::ALLOWED_UNLOCK_CONDITIONS)
697}
698
699#[cfg(feature = "dto")]
700#[allow(missing_docs)]
701pub mod dto {
702 use serde::{Deserialize, Serialize};
703
704 use super::*;
705 use crate::block::{
706 error::dto::DtoError,
707 output::{
708 alias_id::dto::AliasIdDto, dto::OutputBuilderAmountDto, feature::dto::FeatureDto,
709 native_token::dto::NativeTokenDto, unlock_condition::dto::UnlockConditionDto,
710 },
711 };
712
713 #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
715 pub struct AliasOutputDto {
716 #[serde(rename = "type")]
717 pub kind: u8,
718 pub amount: String,
720 #[serde(rename = "nativeTokens", skip_serializing_if = "Vec::is_empty", default)]
722 pub native_tokens: Vec<NativeTokenDto>,
723 #[serde(rename = "aliasId")]
725 pub alias_id: AliasIdDto,
726 #[serde(rename = "stateIndex")]
728 pub state_index: u32,
729 #[serde(rename = "stateMetadata", skip_serializing_if = "String::is_empty", default)]
731 pub state_metadata: String,
732 #[serde(rename = "foundryCounter")]
734 pub foundry_counter: u32,
735 #[serde(rename = "unlockConditions")]
737 pub unlock_conditions: Vec<UnlockConditionDto>,
738 #[serde(skip_serializing_if = "Vec::is_empty", default)]
740 pub features: Vec<FeatureDto>,
741 #[serde(rename = "immutableFeatures", skip_serializing_if = "Vec::is_empty", default)]
743 pub immutable_features: Vec<FeatureDto>,
744 }
745
746 impl From<&AliasOutput> for AliasOutputDto {
747 fn from(value: &AliasOutput) -> Self {
748 Self {
749 kind: AliasOutput::KIND,
750 amount: value.amount().to_string(),
751 native_tokens: value.native_tokens().iter().map(Into::into).collect::<_>(),
752 alias_id: AliasIdDto(value.alias_id().to_string()),
753 state_index: value.state_index(),
754 state_metadata: prefix_hex::encode(value.state_metadata()),
755 foundry_counter: value.foundry_counter(),
756 unlock_conditions: value.unlock_conditions().iter().map(Into::into).collect::<_>(),
757 features: value.features().iter().map(Into::into).collect::<_>(),
758 immutable_features: value.immutable_features().iter().map(Into::into).collect::<_>(),
759 }
760 }
761 }
762
763 impl AliasOutput {
764 fn _try_from_dto(value: &AliasOutputDto) -> Result<AliasOutputBuilder, DtoError> {
765 let mut builder = AliasOutputBuilder::new_with_amount(
766 value
767 .amount
768 .parse::<u64>()
769 .map_err(|_| DtoError::InvalidField("amount"))?,
770 (&value.alias_id).try_into()?,
771 )?;
772
773 builder = builder.with_state_index(value.state_index);
774
775 if !value.state_metadata.is_empty() {
776 builder = builder.with_state_metadata(
777 prefix_hex::decode(&value.state_metadata).map_err(|_| DtoError::InvalidField("state_metadata"))?,
778 );
779 }
780
781 builder = builder.with_foundry_counter(value.foundry_counter);
782
783 for t in &value.native_tokens {
784 builder = builder.add_native_token(t.try_into()?);
785 }
786
787 for b in &value.features {
788 builder = builder.add_feature(b.try_into()?);
789 }
790
791 for b in &value.immutable_features {
792 builder = builder.add_immutable_feature(b.try_into()?);
793 }
794
795 Ok(builder)
796 }
797
798 pub fn try_from_dto(value: &AliasOutputDto, token_supply: u64) -> Result<Self, DtoError> {
799 let mut builder = Self::_try_from_dto(value)?;
800
801 for u in &value.unlock_conditions {
802 builder = builder.add_unlock_condition(UnlockCondition::try_from_dto(u, token_supply)?);
803 }
804
805 Ok(builder.finish(token_supply)?)
806 }
807
808 pub fn try_from_dto_unverified(value: &AliasOutputDto) -> Result<Self, DtoError> {
809 let mut builder = Self::_try_from_dto(value)?;
810
811 for u in &value.unlock_conditions {
812 builder = builder.add_unlock_condition(UnlockCondition::try_from_dto_unverified(u)?);
813 }
814
815 Ok(builder.finish_unverified()?)
816 }
817
818 #[allow(clippy::too_many_arguments)]
819 pub fn try_from_dtos(
820 amount: OutputBuilderAmountDto,
821 native_tokens: Option<Vec<NativeTokenDto>>,
822 alias_id: &AliasIdDto,
823 state_index: Option<u32>,
824 state_metadata: Option<Vec<u8>>,
825 foundry_counter: Option<u32>,
826 unlock_conditions: Vec<UnlockConditionDto>,
827 features: Option<Vec<FeatureDto>>,
828 immutable_features: Option<Vec<FeatureDto>>,
829 token_supply: u64,
830 ) -> Result<Self, DtoError> {
831 let alias_id = AliasId::try_from(alias_id)?;
832
833 let mut builder = match amount {
834 OutputBuilderAmountDto::Amount(amount) => AliasOutputBuilder::new_with_amount(
835 amount.parse().map_err(|_| DtoError::InvalidField("amount"))?,
836 alias_id,
837 )?,
838 OutputBuilderAmountDto::MinimumStorageDeposit(rent_structure) => {
839 AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, alias_id)?
840 }
841 };
842
843 if let Some(native_tokens) = native_tokens {
844 let native_tokens = native_tokens
845 .iter()
846 .map(NativeToken::try_from)
847 .collect::<Result<Vec<NativeToken>, DtoError>>()?;
848 builder = builder.with_native_tokens(native_tokens);
849 }
850
851 if let Some(state_index) = state_index {
852 builder = builder.with_state_index(state_index);
853 }
854
855 if let Some(state_metadata) = state_metadata {
856 builder = builder.with_state_metadata(state_metadata);
857 }
858
859 if let Some(foundry_counter) = foundry_counter {
860 builder = builder.with_foundry_counter(foundry_counter);
861 }
862
863 let unlock_conditions = unlock_conditions
864 .iter()
865 .map(|u| UnlockCondition::try_from_dto(u, token_supply))
866 .collect::<Result<Vec<UnlockCondition>, DtoError>>()?;
867 builder = builder.with_unlock_conditions(unlock_conditions);
868
869 if let Some(features) = features {
870 let features = features
871 .iter()
872 .map(Feature::try_from)
873 .collect::<Result<Vec<Feature>, DtoError>>()?;
874 builder = builder.with_features(features);
875 }
876
877 if let Some(immutable_features) = immutable_features {
878 let immutable_features = immutable_features
879 .iter()
880 .map(Feature::try_from)
881 .collect::<Result<Vec<Feature>, DtoError>>()?;
882 builder = builder.with_immutable_features(immutable_features);
883 }
884
885 Ok(builder.finish(token_supply)?)
886 }
887 }
888}