Skip to main content

iota_sdk_types/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Core type definitions for the IOTA blockchain.
6//!
7//! [IOTA] is a next-generation smart contract platform with high throughput,
8//! low latency, and an asset-oriented programming model powered by the Move
9//! programming language. This crate provides type definitions for working with
10//! the data that makes up the IOTA blockchain.
11//!
12//! [IOTA]: https://iota.org
13//!
14//! # Feature flags
15//!
16//! This library uses a set of [feature flags] to reduce the number of
17//! dependencies and amount of compiled code. By default, no features are
18//! enabled which allows one to enable a subset specifically for their use case.
19//! Below is a list of the available feature flags.
20//!
21//! - `serde`: Enables support for serializing and deserializing types to/from
22//!   BCS utilizing [serde] library. Note: JSON serialization is NOT guaranteed
23//!   to match the IOTA monorepo's JSON-RPC format.
24//! - `rand`: Enables support for generating random instances of a number of
25//!   types via the [rand] library.
26//! - `hash`: Enables support for hashing, which is required for deriving
27//!   addresses and calculating digests for various types.
28//! - `proptest`: Enables support for the [proptest] library by providing
29//!   implementations of [proptest::arbitrary::Arbitrary] for many types.
30//!
31//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
32//! [serde]: https://docs.rs/serde
33//! [rand]: https://docs.rs/rand
34//! [proptest]: https://docs.rs/proptest
35//! [proptest::arbitrary::Arbitrary]: https://docs.rs/proptest/latest/proptest/arbitrary/trait.Arbitrary.html
36//!
37//! # BCS
38//!
39//! [BCS] is the serialization format used to represent the state of the
40//! blockchain and is used extensively throughout the IOTA ecosystem. In
41//! particular the BCS format is leveraged because it _"guarantees canonical
42//! serialization, meaning that for any given data type, there is a one-to-one
43//! correspondence between in-memory values and valid byte representations."_
44//! One benefit of this property of having a canonical serialized representation
45//! is to allow different entities in the ecosystem to all agree on how a
46//! particular type should be interpreted and more importantly define a
47//! deterministic representation for hashing and signing.
48//!
49//! This library strives to guarantee that the types defined are fully
50//! BCS-compatible with the data that the network produces. The one caveat to
51//! this would be that as the IOTA protocol evolves, new type variants are added
52//! and older versions of this library may not support those newly
53//! added variants. The expectation is that the most recent release of this
54//! library will support new variants and types as they are released to IOTA's
55//! `testnet` network.
56//!
57//! See the documentation for the various types defined by this crate for a
58//! specification of their BCS serialized representation which will be defined
59//! using ABNF notation as described by [RFC-5234]. In addition to the format
60//! itself, some types have an extra layer of verification and may impose
61//! additional restrictions on valid byte representations above and beyond those
62//! already provided by BCS. In these instances the documentation for those
63//! types will clearly specify these additional restrictions.
64//!
65//! Here are some common rules:
66//!
67//! ```text
68//! ; --- BCS Value ---
69//! bcs-value           = bcs-struct / bcs-enum / bcs-length-prefixed / bcs-fixed-length
70//! bcs-length-prefixed = bytes / string / vector / option
71//! bcs-fixed-length    = u8 / u16 / u32 / u64 / u128 /
72//!                       i8 / i16 / i32 / i64 / i128 /
73//!                       bool
74//! bcs-struct          = *bcs-value          ; Sequence of serialized fields
75//! bcs-enum            = uleb128 bcs-value   ; Variant index (ULEB128) + associated value
76//!
77//! ; --- Named primitives ---
78//! uleb128 = *(%x80-FF) %x00-7F   ; Variable-length unsigned integer
79//! size    = uleb128               ; BCS sequence / string length
80//! opt     = %d00                  ; None — no value follows
81//!         / %d01                  ; Some — value follows
82//!
83//! ; --- Length-prefixed types ---
84//! bytes   = size *OCTET          ; Raw bytes
85//! string  = size *OCTET          ; UTF-8 string
86//! vector  = size *bcs-value      ; Length-prefixed list of values
87//! option  = %d00 / (%d01 bcs-value)  ; Optional value
88//!
89//! ; --- Fixed-length types ---
90//! u8      = 1OCTET               ; 1-byte unsigned integer
91//! u16     = 2OCTET               ; 2-byte unsigned integer, little-endian
92//! u32     = 4OCTET               ; 4-byte unsigned integer, little-endian
93//! u64     = 8OCTET               ; 8-byte unsigned integer, little-endian
94//! u128    = 16OCTET              ; 16-byte unsigned integer, little-endian
95//! i8      = 1OCTET               ; 1-byte signed integer
96//! i16     = 2OCTET               ; 2-byte signed integer, little-endian
97//! i32     = 4OCTET               ; 4-byte signed integer, little-endian
98//! i64     = 8OCTET               ; 8-byte signed integer, little-endian
99//! i128    = 16OCTET              ; 16-byte signed integer, little-endian
100//! bool    = %d00                 ; false
101//!         / %d01                 ; true
102//! array   = *(bcs-value)         ; Fixed-length array (no length prefix)
103//! ```
104//!
105//! [BCS]: https://docs.rs/bcs
106//! [RFC-5234]: https://datatracker.ietf.org/doc/html/rfc5234
107
108#![cfg_attr(doc_cfg, feature(doc_cfg))]
109
110mod tree_display;
111pub(crate) use tree_display::{TreeDisplay, TreeWriter, impl_tree_display};
112
113#[cfg(feature = "hash")]
114#[cfg_attr(doc_cfg, doc(cfg(feature = "hash")))]
115pub mod hash;
116
117pub mod address;
118pub mod checkpoint;
119pub mod crypto;
120pub mod digest;
121pub mod effects;
122pub mod events;
123pub mod execution_status;
124pub mod framework;
125pub mod gas;
126pub mod iota_names;
127pub mod move_core;
128pub mod move_package;
129pub mod object;
130pub mod object_id;
131pub mod transaction;
132pub mod u256;
133pub mod utils;
134pub mod validator;
135pub mod version;
136
137pub use address::{Address, AddressParseError};
138pub use checkpoint::{
139    CheckpointCommitment, CheckpointContents, CheckpointContentsV1, CheckpointData,
140    CheckpointSequenceNumber, CheckpointSummary, CheckpointTimestamp, CheckpointTransaction,
141    CheckpointTransactionInfo, EndOfEpochData, EpochId, ProtocolVersion, SignedCheckpointSummary,
142    StakeUnit,
143};
144pub use crypto::{
145    Bls12381PublicKey, Bls12381Signature, Ed25519PublicKey, Ed25519Signature, HashingIntentScope,
146    INTENT_PREFIX_LENGTH, Intent, IntentAppId, IntentError, IntentMessage, IntentScope,
147    IntentVersion, InvalidSignatureScheme, MoveAuthenticator, MoveAuthenticatorV1,
148    MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberSignature,
149    PasskeyAuthenticator, PasskeyPublicKey, PersonalMessage, PublicKey, PublicKeyError,
150    PublicKeyExt, Secp256k1PublicKey, Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature,
151    SignatureScheme, SimpleSignature, UserSignature,
152};
153pub use digest::{
154    CertificateDigest, CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest,
155    DigestParseError, EffectsAuxDataDigest, MisbehaviorReportDigest, MoveAuthenticatorDigest,
156    ObjectDigest, SenderSignedDataDigest, SigningDigest, TransactionDigest,
157    TransactionEffectsDigest, TransactionEventsDigest,
158};
159pub use effects::{
160    ChangedObject, IdOperation, InputSharedObject, ObjectChange, ObjectIn, ObjectOut,
161    ObjectRemoveKind, TransactionEffects, TransactionEffectsV1, UnchangedSharedKind,
162    UnchangedSharedObject, WriteKind,
163};
164pub use events::{Event, TransactionEvents};
165pub use execution_status::{
166    CommandArgumentError, ExecutionError, ExecutionStatus, MoveLocation, PackageUpgradeError,
167    TypeArgumentError,
168};
169pub use framework::Coin;
170pub use gas::GasCostSummary;
171pub use move_core::{
172    Identifier, MAX_IDENTIFIER_LENGTH, MAX_TYPE_TAG_NESTING, StructTag, TypeParseError, TypeTag,
173};
174pub use move_package::{MovePackage, MovePackageData, TypeOrigin, UpgradeInfo, UpgradePolicy};
175pub use object::{
176    GenesisObject, MoveObjectType, MoveStruct, MoveStructContentsError, Object, ObjectData,
177    ObjectReference, ObjectType, ObjectVersion, OwnedObjectReference, Owner,
178};
179pub use object_id::ObjectId;
180#[cfg(feature = "serde")]
181#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
182pub(crate) use transaction::SignedTransactionWithIntentMessage;
183pub use transaction::{
184    Argument, CanceledTransaction, ChangeEpoch, ChangeEpochV2, ChangeEpochV3, ChangeEpochV4,
185    Command, ConsensusCommitPrologueV1, ConsensusDeterminedVersionAssignments, DenyRuleSet,
186    EndOfEpochTransactionKind, GasPayment, GenesisTransaction, Input, MakeMoveVector, MergeCoins,
187    MoveCall, ProgrammableTransaction, Publish, RandomnessRound, RandomnessStateUpdate,
188    SenderSignedTransaction, SharedObjectReference, SignedTransaction, SplitCoins, SystemPackage,
189    Transaction, TransactionDenyRulesUpdate, TransactionExpiration, TransactionKind, TransactionV1,
190    TransferObjects, Upgrade, VersionAssignment,
191};
192pub use validator::{
193    ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature,
194};
195pub use version::Version;
196
197#[cfg(all(test, feature = "serde", feature = "proptest"))]
198mod serialization_proptests;
199
200#[cfg(feature = "serde")]
201#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
202mod bcs_base64 {
203    use base64ct::Encoding;
204
205    macro_rules! impl_bcs_base64 {
206        ($($type:ident),* $(,)?) => {
207            $(
208            impl crate::$type {
209                paste::paste! {
210                    #[doc = "Serialize this `" $type "` as a `Vec<u8>` of BCS bytes."]
211                    pub fn to_bcs(&self) -> Vec<u8> {
212                        bcs::to_bytes(self).expect("bcs serialization failed")
213                    }
214
215                    #[doc = "Serialize this `" $type "` as a base64-encoded string of its BCS bytes."]
216                    pub fn to_base64(&self) -> String {
217                        base64ct::Base64::encode_string(&self.to_bcs())
218                    }
219
220                    #[doc = "Deserialize a `" $type "` from BCS bytes."]
221                    pub fn from_bcs(bytes: &[u8]) -> Result<Self, bcs::Error> {
222                        bcs::from_bytes::<Self>(bytes)
223                    }
224
225                    #[doc = "Deserialize a `" $type "` from a base64-encoded string of its BCS bytes."]
226                    pub fn from_base64(bytes: &str) -> Result<Self, bcs::Error> {
227                        let decoded = base64ct::Base64::decode_vec(bytes)
228                            .map_err(|e| bcs::Error::Custom(e.to_string()))?;
229                        Self::from_bcs(&decoded)
230                    }
231                }
232            }
233            )*
234        };
235    }
236
237    impl_bcs_base64!(
238        Object,
239        SenderSignedTransaction,
240        Transaction,
241        TransactionEffects,
242        TransactionKind,
243        TransactionV1,
244    );
245}
246
247/// Returns the next array in byte-increasing order.
248pub const fn next_lexicographical_array<const N: usize>(array: &[u8; N]) -> [u8; N] {
249    match next_lexicographical_array_opt(array) {
250        Some(next) => next,
251        None => [0; N],
252    }
253}
254
255/// Returns the next array in byte-increasing order, or `None` if the result
256/// would overflow.
257pub const fn next_lexicographical_array_opt<const N: usize>(array: &[u8; N]) -> Option<[u8; N]> {
258    let mut next = *array;
259    let mut i = N;
260
261    while i > 0 {
262        i -= 1;
263        let (new_byte, overflow) = next[i].overflowing_add(1);
264        next[i] = new_byte;
265
266        if !overflow {
267            return Some(next);
268        }
269    }
270
271    None
272}
273
274#[macro_export]
275macro_rules! def_is {
276    ($($variant:ident),* $(,)?) => {
277        paste::paste! {$(
278        #[doc = "Checks if this is a " $variant:snake " variant."]
279        #[inline]
280        pub fn [< is_ $variant:snake >](&self) -> bool {
281            matches!(self, Self::$variant { .. })
282        }
283        )*}
284    };
285}
286
287#[macro_export]
288macro_rules! def_is_as_into_opt {
289    (@into $variant:ident ($rename:ident) [Box<$inner:ty>]) => {
290        paste::paste! {
291        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or returns `None` otherwise."]
292        #[inline]
293        pub fn [< into_opt_ $rename >](self) -> Option<$inner> {
294            #[allow(irrefutable_let_patterns)]
295            if let Self::$variant(inner) = self {
296                Some(*inner)
297            } else {
298                None
299            }
300        }
301
302        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or panics otherwise."]
303        #[inline]
304        pub fn [< into_ $rename >](self) -> $inner {
305            self.[< into_opt_ $rename >]().expect(&format!("not a {}", stringify!($rename)))
306        }
307        }
308    };
309    (@into $variant:ident ($rename:ident) [$inner:ty]) => {
310        paste::paste! {
311        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or returns `None` otherwise."]
312        #[inline]
313        pub fn [< into_opt_ $rename >](self) -> Option<$inner> {
314            #[allow(irrefutable_let_patterns)]
315            if let Self::$variant(inner) = self {
316                Some(inner)
317            } else {
318                None
319            }
320        }
321
322        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or panics otherwise."]
323        #[inline]
324        pub fn [< into_ $rename >](self) -> $inner {
325            self.[< into_opt_ $rename >]().expect(&format!("not a {}", stringify!($variant)))
326        }
327        }
328    };
329    (@impl $variant:ident ($rename:ident) [Box<$inner:ty>]) => {
330        paste::paste! {
331        #[doc = "Checks if this is a " $rename:snake " variant."]
332        #[inline]
333        pub fn [< is_ $rename >](&self) -> bool {
334            matches!(self, Self::$variant(_))
335        }
336
337        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or panics otherwise."]
338        #[inline]
339        pub fn [< as_ $rename >](&self) -> &$inner {
340            self.[< as_opt_ $rename >]().expect(&format!("not a {}", stringify!($variant)))
341        }
342
343        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or returns `None` otherwise."]
344        #[inline]
345        pub fn [< as_opt_ $rename >](&self) -> Option<&$inner> {
346            #[allow(irrefutable_let_patterns)]
347            if let Self::$variant(inner) = self {
348                Some(inner)
349            } else {
350                None
351            }
352        }
353        }
354
355        $crate::def_is_as_into_opt!{@into $variant($rename) [Box<$inner>]}
356    };
357    (@impl $variant:ident ($rename:ident) [$inner:ty]) => {
358        paste::paste! {
359        #[doc = "Checks if this is a " $rename:snake " variant."]
360        #[inline]
361        pub fn [< is_ $rename >](&self) -> bool {
362            matches!(self, Self::$variant(_))
363        }
364
365        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or panics otherwise."]
366        #[inline]
367        pub fn [< as_ $rename >](&self) -> &$inner {
368            self.[< as_opt_ $rename >]().expect(&format!("not a {}", stringify!($variant)))
369        }
370
371        #[doc = "Converts this into a mut " $rename:snake " if it is a " $variant:snake " variant, or panics otherwise."]
372        #[inline]
373        pub fn [< as_mut_ $rename >](&mut self) -> &mut $inner {
374            self.[< as_opt_mut_ $rename >]().expect(&format!("not a {}", stringify!($variant)))
375        }
376
377        #[doc = "Converts this into a " $rename:snake " if it is a " $variant:snake " variant, or returns `None` otherwise."]
378        #[inline]
379        pub fn [< as_opt_ $rename >](&self) -> Option<&$inner> {
380            #[allow(irrefutable_let_patterns)]
381            if let Self::$variant(inner) = self {
382                Some(inner)
383            } else {
384                None
385            }
386        }
387
388        #[doc = "Converts this into a mut " $rename:snake " if it is a " $variant:snake " variant, or returns `None` otherwise."]
389        #[inline]
390        pub fn [< as_opt_mut_ $rename >](&mut self) -> Option<&mut $inner> {
391            #[allow(irrefutable_let_patterns)]
392            if let Self::$variant(inner) = self {
393                Some(inner)
394            } else {
395                None
396            }
397        }
398        }
399
400        $crate::def_is_as_into_opt!{@into $variant($rename) [$inner]}
401    };
402    (@parse $variant:ident ($rename:ident) [$($inner:tt)*]) => {
403        $crate::def_is_as_into_opt!{@impl $variant($rename) [$($inner)*]}
404    };
405    (@parse $variant:ident ($rename:ident)) => {
406        $crate::def_is_as_into_opt!{@impl $variant($rename) [$variant]}
407    };
408    (@parse $variant:ident [$($inner:tt)*]) => {
409        paste::paste! { $crate::def_is_as_into_opt!{@impl $variant ([< $variant:snake >]) [$($inner)*]} }
410    };
411    (@parse $variant:ident) => {
412        paste::paste! { $crate::def_is_as_into_opt!{@impl $variant ([< $variant:snake >]) [$variant]} }
413    };
414    ($($variant:ident $( as $rename:ident)? $(($($inner:tt)*))?),* $(,)?) => {
415        $(
416        $crate::def_is_as_into_opt!{@parse $variant $(($rename))? $([$($inner)*])?}
417        )*
418    };
419}
420
421#[cfg(feature = "serde")]
422mod _serde {
423    use std::borrow::Cow;
424
425    use base64ct::{Base64, Encoding};
426    use serde::{Deserialize, Deserializer, Serialize, Serializer};
427    use serde_with::{Bytes, DeserializeAs, SerializeAs};
428
429    pub(crate) type ReadableDisplay =
430        ::serde_with::As<::serde_with::IfIsHumanReadable<::serde_with::DisplayFromStr>>;
431
432    pub(crate) type OptionReadableDisplay =
433        ::serde_with::As<Option<::serde_with::IfIsHumanReadable<::serde_with::DisplayFromStr>>>;
434
435    pub(crate) type ReadableBase64Encoded =
436        ::serde_with::As<::serde_with::IfIsHumanReadable<Base64Encoded, ::serde_with::Bytes>>;
437
438    pub(crate) struct Base64Encoded;
439
440    impl<T: AsRef<[u8]>> SerializeAs<T> for Base64Encoded {
441        fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
442        where
443            S: Serializer,
444        {
445            let bytes = source.as_ref();
446            let b64 = Base64::encode_string(bytes);
447            b64.serialize(serializer)
448        }
449    }
450
451    impl<'de, T: TryFrom<Vec<u8>>> DeserializeAs<'de, T> for Base64Encoded {
452        fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
453        where
454            D: Deserializer<'de>,
455        {
456            let b64: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
457            let bytes = Base64::decode_vec(&b64).map_err(serde::de::Error::custom)?;
458            let length = bytes.len();
459            T::try_from(bytes).map_err(|_| {
460                serde::de::Error::custom(format_args!(
461                    "Can't convert a Byte Vector of length {length} to the output type."
462                ))
463            })
464        }
465    }
466
467    /// Serializes a bitmap according to the roaring bitmap on-disk standard.
468    /// <https://github.com/RoaringBitmap/RoaringFormatSpec>
469    pub(crate) struct BinaryRoaringBitmap;
470
471    impl SerializeAs<roaring::RoaringBitmap> for BinaryRoaringBitmap {
472        fn serialize_as<S>(
473            source: &roaring::RoaringBitmap,
474            serializer: S,
475        ) -> Result<S::Ok, S::Error>
476        where
477            S: Serializer,
478        {
479            let mut bytes = vec![];
480
481            source
482                .serialize_into(&mut bytes)
483                .map_err(serde::ser::Error::custom)?;
484            Bytes::serialize_as(&bytes, serializer)
485        }
486    }
487
488    impl<'de> DeserializeAs<'de, roaring::RoaringBitmap> for BinaryRoaringBitmap {
489        fn deserialize_as<D>(deserializer: D) -> Result<roaring::RoaringBitmap, D::Error>
490        where
491            D: Deserializer<'de>,
492        {
493            let bytes: Cow<'de, [u8]> = Bytes::deserialize_as(deserializer)?;
494            roaring::RoaringBitmap::deserialize_from(&bytes[..]).map_err(serde::de::Error::custom)
495        }
496    }
497
498    pub(crate) struct Base64RoaringBitmap;
499
500    impl SerializeAs<roaring::RoaringBitmap> for Base64RoaringBitmap {
501        fn serialize_as<S>(
502            source: &roaring::RoaringBitmap,
503            serializer: S,
504        ) -> Result<S::Ok, S::Error>
505        where
506            S: Serializer,
507        {
508            let mut bytes = vec![];
509
510            source
511                .serialize_into(&mut bytes)
512                .map_err(serde::ser::Error::custom)?;
513            let b64 = Base64::encode_string(&bytes);
514            b64.serialize(serializer)
515        }
516    }
517
518    impl<'de> DeserializeAs<'de, roaring::RoaringBitmap> for Base64RoaringBitmap {
519        fn deserialize_as<D>(deserializer: D) -> Result<roaring::RoaringBitmap, D::Error>
520        where
521            D: Deserializer<'de>,
522        {
523            let b64: Cow<'de, str> = Deserialize::deserialize(deserializer)?;
524            let bytes = Base64::decode_vec(&b64).map_err(serde::de::Error::custom)?;
525            roaring::RoaringBitmap::deserialize_from(&bytes[..]).map_err(serde::de::Error::custom)
526        }
527    }
528
529    pub(crate) use super::SignedTransactionWithIntentMessage;
530}
531
532#[cfg(test)]
533mod test {
534    use super::{next_lexicographical_array, next_lexicographical_array_opt};
535
536    #[test]
537    fn test_lexical_order() {
538        fn array_from_str(s: &str) -> [u8; 32] {
539            hex::decode(s).unwrap().try_into().unwrap()
540        }
541        assert_eq!(
542            next_lexicographical_array(&array_from_str(
543                "0000000000000000000000000000000000000000000000000000000000000000"
544            )),
545            array_from_str("0000000000000000000000000000000000000000000000000000000000000001"),
546        );
547        assert_eq!(
548            next_lexicographical_array(&array_from_str(
549                "000000000000000000000000000000000000000000000000000000000000ffff"
550            )),
551            array_from_str("0000000000000000000000000000000000000000000000000000000000010000"),
552        );
553        assert_eq!(
554            next_lexicographical_array(&array_from_str(
555                "000000000000000000000000000000000000000000000000000000000001002c"
556            )),
557            array_from_str("000000000000000000000000000000000000000000000000000000000001002d"),
558        );
559        assert_eq!(
560            next_lexicographical_array(&array_from_str(
561                "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
562            )),
563            array_from_str("0000000000000000000000000000000000000000000000000000000000000000"),
564        );
565    }
566
567    #[test]
568    fn test_lexical_order_opt() {
569        fn array_from_str(s: &str) -> [u8; 32] {
570            hex::decode(s).unwrap().try_into().unwrap()
571        }
572        assert_eq!(
573            next_lexicographical_array_opt(&array_from_str(
574                "0000000000000000000000000000000000000000000000000000000000000000"
575            )),
576            Some(array_from_str(
577                "0000000000000000000000000000000000000000000000000000000000000001"
578            )),
579        );
580        assert_eq!(
581            next_lexicographical_array_opt(&array_from_str(
582                "000000000000000000000000000000000000000000000000000000000000ffff"
583            )),
584            Some(array_from_str(
585                "0000000000000000000000000000000000000000000000000000000000010000"
586            )),
587        );
588        assert_eq!(
589            next_lexicographical_array_opt(&array_from_str(
590                "000000000000000000000000000000000000000000000000000000000001002c"
591            )),
592            Some(array_from_str(
593                "000000000000000000000000000000000000000000000000000000000001002d"
594            )),
595        );
596        assert_eq!(
597            next_lexicographical_array_opt(&array_from_str(
598                "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
599            )),
600            None,
601        );
602    }
603}