iota_stardust_types/block/output/
mod.rs1mod alias_id;
5mod chain_id;
6mod foundry_id;
7mod native_token;
8mod nft_id;
9mod output_id;
10mod token_id;
11mod token_scheme;
12mod treasury;
13
14pub mod alias;
16pub mod basic;
18pub mod feature;
20pub mod foundry;
22pub mod nft;
24pub mod unlock_condition;
26
27use core::ops::RangeInclusive;
28
29use derive_more::From;
30use packable::{
31 Packable,
32 error::{UnpackError, UnpackErrorExt},
33 packer::Packer,
34 unpacker::Unpacker,
35};
36
37pub(crate) use self::{
38 alias::StateMetadataLength,
39 feature::{MetadataFeatureLength, TagFeatureLength},
40 native_token::NativeTokenCount,
41 output_id::OutputIndex,
42};
43pub use self::{
44 alias::{AliasOutput, AliasOutputBuilder, AliasTransition},
45 alias_id::AliasId,
46 basic::{BasicOutput, BasicOutputBuilder},
47 chain_id::ChainId,
48 feature::{Feature, Features},
49 foundry::{FoundryOutput, FoundryOutputBuilder},
50 foundry_id::FoundryId,
51 native_token::{NativeToken, NativeTokens, NativeTokensBuilder},
52 nft::{NftOutput, NftOutputBuilder},
53 nft_id::NftId,
54 output_id::OutputId,
55 token_id::TokenId,
56 token_scheme::{SimpleTokenScheme, TokenScheme},
57 treasury::TreasuryOutput,
58 unlock_condition::{UnlockCondition, UnlockConditions},
59};
60use crate::block::{Error, address::Address};
61
62pub const OUTPUT_COUNT_MAX: u16 = 128;
64pub const OUTPUT_COUNT_RANGE: RangeInclusive<u16> = 1..=OUTPUT_COUNT_MAX; pub const OUTPUT_INDEX_MAX: u16 = OUTPUT_COUNT_MAX - 1; pub const OUTPUT_INDEX_RANGE: RangeInclusive<u16> = 0..=OUTPUT_INDEX_MAX; #[derive(Clone)]
71pub(crate) enum OutputBuilderAmount {
72 Amount(u64),
73}
74
75#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From)]
78pub enum Output {
79 Treasury(TreasuryOutput),
81 Basic(BasicOutput),
83 Alias(AliasOutput),
85 Foundry(FoundryOutput),
87 Nft(NftOutput),
89}
90
91impl core::fmt::Debug for Output {
92 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
93 match self {
94 Self::Treasury(output) => output.fmt(f),
95 Self::Basic(output) => output.fmt(f),
96 Self::Alias(output) => output.fmt(f),
97 Self::Foundry(output) => output.fmt(f),
98 Self::Nft(output) => output.fmt(f),
99 }
100 }
101}
102
103impl Output {
104 pub const AMOUNT_MIN: u64 = 1;
106
107 pub fn kind(&self) -> u8 {
109 match self {
110 Self::Treasury(_) => TreasuryOutput::KIND,
111 Self::Basic(_) => BasicOutput::KIND,
112 Self::Alias(_) => AliasOutput::KIND,
113 Self::Foundry(_) => FoundryOutput::KIND,
114 Self::Nft(_) => NftOutput::KIND,
115 }
116 }
117
118 pub fn kind_str(&self) -> &str {
120 match self {
121 Self::Alias(_) => "Alias",
122 Self::Basic(_) => "Basic",
123 Self::Foundry(_) => "Foundry",
124 Self::Nft(_) => "Nft",
125 Self::Treasury(_) => "Treasury",
126 }
127 }
128
129 pub fn amount(&self) -> u64 {
131 match self {
132 Self::Treasury(output) => output.amount(),
133 Self::Basic(output) => output.amount(),
134 Self::Alias(output) => output.amount(),
135 Self::Foundry(output) => output.amount(),
136 Self::Nft(output) => output.amount(),
137 }
138 }
139
140 pub fn native_tokens(&self) -> Option<&NativeTokens> {
142 match self {
143 Self::Treasury(_) => None,
144 Self::Basic(output) => Some(output.native_tokens()),
145 Self::Alias(output) => Some(output.native_tokens()),
146 Self::Foundry(output) => Some(output.native_tokens()),
147 Self::Nft(output) => Some(output.native_tokens()),
148 }
149 }
150
151 pub fn unlock_conditions(&self) -> Option<&UnlockConditions> {
153 match self {
154 Self::Treasury(_) => None,
155 Self::Basic(output) => Some(output.unlock_conditions()),
156 Self::Alias(output) => Some(output.unlock_conditions()),
157 Self::Foundry(output) => Some(output.unlock_conditions()),
158 Self::Nft(output) => Some(output.unlock_conditions()),
159 }
160 }
161
162 pub fn features(&self) -> Option<&Features> {
164 match self {
165 Self::Treasury(_) => None,
166 Self::Basic(output) => Some(output.features()),
167 Self::Alias(output) => Some(output.features()),
168 Self::Foundry(output) => Some(output.features()),
169 Self::Nft(output) => Some(output.features()),
170 }
171 }
172
173 pub fn immutable_features(&self) -> Option<&Features> {
175 match self {
176 Self::Treasury(_) => None,
177 Self::Basic(_) => None,
178 Self::Alias(output) => Some(output.immutable_features()),
179 Self::Foundry(output) => Some(output.immutable_features()),
180 Self::Nft(output) => Some(output.immutable_features()),
181 }
182 }
183
184 pub fn chain_id(&self) -> Option<ChainId> {
186 match self {
187 Self::Treasury(_) => None,
188 Self::Basic(_) => None,
189 Self::Alias(output) => Some(output.chain_id()),
190 Self::Foundry(output) => Some(output.chain_id()),
191 Self::Nft(output) => Some(output.chain_id()),
192 }
193 }
194
195 pub fn is_treasury(&self) -> bool {
197 matches!(self, Self::Treasury(_))
198 }
199
200 pub fn as_treasury(&self) -> &TreasuryOutput {
203 if let Self::Treasury(output) = self {
204 output
205 } else {
206 panic!("as_treasury called on a non-treasury output");
207 }
208 }
209
210 pub fn is_basic(&self) -> bool {
212 matches!(self, Self::Basic(_))
213 }
214
215 pub fn as_basic(&self) -> &BasicOutput {
218 if let Self::Basic(output) = self {
219 output
220 } else {
221 panic!("as_basic called on a non-basic output");
222 }
223 }
224
225 pub fn is_alias(&self) -> bool {
227 matches!(self, Self::Alias(_))
228 }
229
230 pub fn as_alias(&self) -> &AliasOutput {
233 if let Self::Alias(output) = self {
234 output
235 } else {
236 panic!("as_alias called on a non-alias output");
237 }
238 }
239
240 pub fn is_foundry(&self) -> bool {
242 matches!(self, Self::Foundry(_))
243 }
244
245 pub fn as_foundry(&self) -> &FoundryOutput {
248 if let Self::Foundry(output) = self {
249 output
250 } else {
251 panic!("as_foundry called on a non-foundry output");
252 }
253 }
254
255 pub fn is_nft(&self) -> bool {
257 matches!(self, Self::Nft(_))
258 }
259
260 pub fn as_nft(&self) -> &NftOutput {
263 if let Self::Nft(output) = self {
264 output
265 } else {
266 panic!("as_nft called on a non-nft output");
267 }
268 }
269
270 pub fn required_and_unlocked_address(
275 &self,
276 current_time: u32,
277 output_id: &OutputId,
278 alias_transition: Option<AliasTransition>,
279 ) -> Result<(Address, Option<Address>), Error> {
280 match self {
281 Self::Alias(output) => {
282 if alias_transition.unwrap_or(AliasTransition::State) == AliasTransition::State {
283 Ok((
285 *output.state_controller_address(),
286 Some(Address::Alias(output.alias_address(output_id))),
287 ))
288 } else {
289 Ok((*output.governor_address(), None))
290 }
291 }
292 Self::Basic(output) => Ok((
293 *output
294 .unlock_conditions()
295 .locked_address(output.address(), current_time),
296 None,
297 )),
298 Self::Nft(output) => Ok((
299 *output
300 .unlock_conditions()
301 .locked_address(output.address(), current_time),
302 Some(Address::Nft(output.nft_address(output_id))),
303 )),
304 Self::Foundry(output) => Ok((Address::Alias(*output.alias_address()), None)),
305 Self::Treasury(_) => Err(Error::UnsupportedOutputKind(TreasuryOutput::KIND)),
306 }
307 }
308}
309
310impl Packable for Output {
311 type UnpackVisitor = ();
312 type UnpackError = Error;
313
314 fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
315 match self {
316 Self::Treasury(output) => {
317 TreasuryOutput::KIND.pack(packer)?;
318 output.pack(packer)
319 }
320 Self::Basic(output) => {
321 BasicOutput::KIND.pack(packer)?;
322 output.pack(packer)
323 }
324 Self::Alias(output) => {
325 AliasOutput::KIND.pack(packer)?;
326 output.pack(packer)
327 }
328 Self::Foundry(output) => {
329 FoundryOutput::KIND.pack(packer)?;
330 output.pack(packer)
331 }
332 Self::Nft(output) => {
333 NftOutput::KIND.pack(packer)?;
334 output.pack(packer)
335 }
336 }
337 }
338
339 fn unpack<U: Unpacker, const VERIFY: bool>(
340 unpacker: &mut U,
341 visitor: &Self::UnpackVisitor,
342 ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
343 let kind = u8::unpack::<_, VERIFY>(unpacker, visitor).coerce()?;
344
345 match kind {
346 TreasuryOutput::KIND => Ok(Self::Treasury(
347 TreasuryOutput::unpack::<_, VERIFY>(unpacker, visitor).coerce()?,
348 )),
349 BasicOutput::KIND => Ok(Self::Basic(
350 BasicOutput::unpack::<_, VERIFY>(unpacker, visitor).coerce()?,
351 )),
352 AliasOutput::KIND => Ok(Self::Alias(
353 AliasOutput::unpack::<_, VERIFY>(unpacker, visitor).coerce()?,
354 )),
355 FoundryOutput::KIND => Ok(Self::Foundry(
356 FoundryOutput::unpack::<_, VERIFY>(unpacker, visitor).coerce()?,
357 )),
358 NftOutput::KIND => Ok(Self::Nft(
359 NftOutput::unpack::<_, VERIFY>(unpacker, visitor).coerce()?,
360 )),
361 _ => Err(UnpackError::Packable(Error::InvalidOutputKind(kind))),
362 }
363 }
364}
365
366pub(crate) fn verify_output_amount(amount: &u64, token_supply: &u64) -> Result<(), Error> {
367 if *amount < Output::AMOUNT_MIN || amount > token_supply {
368 Err(Error::InvalidOutputAmount(*amount))
369 } else {
370 Ok(())
371 }
372}