Skip to main content

ab_contracts_common/
metadata.rs

1mod compact;
2pub mod decode;
3#[cfg(test)]
4mod tests;
5
6use crate::metadata::compact::compact_metadata;
7use ab_io_type::metadata::MAX_METADATA_CAPACITY;
8
9/// Metadata for smart contact methods.
10///
11/// Metadata encoding consists of this enum variant treated as `u8` followed by optional metadata
12/// encoding rules specific to metadata type variant (see variant's description).
13///
14/// This metadata is enough to fully reconstruct the hierarchy of the type to generate language
15/// bindings, auto-generate UI forms, etc.
16#[derive(Debug, Copy, Clone, Eq, PartialEq)]
17#[repr(u8)]
18pub enum ContractMetadataKind {
19    /// Main contract metadata.
20    ///
21    /// Contracts are encoded as follows:
22    /// * Encoding of the state type as described in [`IoTypeMetadataKind`]
23    /// * Encoding of the `#[slot]` type as described in [`IoTypeMetadataKind`]
24    /// * Encoding of the `#[tmp]` type as described in [`IoTypeMetadataKind`]
25    /// * Number of methods (u8)
26    /// * Recursive metadata of methods as defined in one of:
27    ///   * [`Self::Init`]
28    ///   * [`Self::UpdateStateless`]
29    ///   * [`Self::UpdateStatefulRo`]
30    ///   * [`Self::UpdateStatefulRw`]
31    ///   * [`Self::ViewStateless`]
32    ///   * [`Self::ViewStateful`]
33    ///
34    /// [`IoTypeMetadataKind`]: ab_io_type::metadata::IoTypeMetadataKind
35    Contract,
36    /// Trait metadata.
37    ///
38    /// Traits are encoded as follows:
39    /// * Length of trait name in bytes (u8)
40    /// * Trait name as UTF-8 bytes
41    /// * Number of methods (u8)
42    /// * Recursive metadata of methods as defined in one of:
43    ///   * [`Self::UpdateStateless`]
44    ///   * [`Self::ViewStateless`]
45    Trait,
46
47    /// `#[init]` method.
48    ///
49    /// Initializers are encoded as follows:
50    /// * Length of method name in bytes (u8)
51    /// * Method name as UTF-8 bytes
52    /// * Number of named arguments (u8, excluding state argument `&self` or `&mut self`, but
53    ///   including return type that is not `()` or `Result<(), ContactError>`)
54    ///
55    /// Each argument is encoded as follows:
56    /// * Argument type as u8, one of:
57    ///   * [`Self::EnvRo`]
58    ///   * [`Self::EnvRw`]
59    ///   * [`Self::TmpRo`]
60    ///   * [`Self::TmpRw`]
61    ///   * [`Self::SlotRo`]
62    ///   * [`Self::SlotRw`]
63    ///   * [`Self::Input`]
64    ///   * [`Self::Output`]
65    ///   * [`Self::Return`] (yes, this is considered to be an argument)
66    /// * Length of the argument name in bytes (u8, except for [`Self::EnvRo`] and [`Self::EnvRw`])
67    /// * Argument name as UTF-8 bytes (except for [`Self::EnvRo`] and [`Self::EnvRw`])
68    /// * Only for [`Self::Input`] and [`Self::Output`] recursive metadata of argument's type as
69    ///   described in [`IoTypeMetadataKind`] with the following exception:
70    ///   * For last [`Self::Output`] or [`Self::Return`] this is skipped if the method is
71    ///     [`Self::Init`] since it is statically known to be `Self` and present otherwise
72    ///
73    /// [`IoTypeMetadataKind`]: ab_io_type::metadata::IoTypeMetadataKind
74    ///
75    /// NOTE: [`Self::Return`] is encoded as a separate argument and counts towards the number of
76    /// arguments. At the same time, `self` doesn't count towards the number of arguments as it
77    /// is implicitly defined by the variant of this struct.
78    Init,
79    /// Stateless `#[update]` method (doesn't have `self` in its arguments).
80    ///
81    /// Encoding is the same as [`Self::Init`].
82    UpdateStateless,
83    /// Stateful read-only `#[update]` method (has `&self` in its arguments).
84    ///
85    /// Encoding is the same as [`Self::Init`].
86    UpdateStatefulRo,
87    /// Stateful read-write `#[update]` method (has `&mut self` in its arguments).
88    ///
89    /// Encoding is the same as [`Self::Init`].
90    UpdateStatefulRw,
91    /// Stateless `#[view]` method (doesn't have `self` in its arguments).
92    ///
93    /// Encoding is the same as [`Self::Init`].
94    ViewStateless,
95    /// Stateful read-only `#[view]` method (has `&self` in its arguments).
96    ///
97    /// Encoding is the same as [`Self::Init`].
98    ViewStateful,
99
100    // TODO: `#[env] can be made implicit assuming the name is of the struct is always the same
101    /// Read-only `#[env]` argument.
102    ///
103    /// Example: `#[env] env: &Env,`
104    EnvRo,
105    /// Read-write `#[env]` argument.
106    ///
107    /// Example: `#[env] env: &mut Env<'_>,`
108    EnvRw,
109    /// Read-only `#[tmp]` argument.
110    ///
111    /// Example: `#[tmp] tmp: &MaybeData<Tmp>,`
112    TmpRo,
113    /// Read-write `#[tmp]` argument.
114    ///
115    /// Example: `#[tmp] tmp: &mut MaybeData<Tmp>,`
116    TmpRw,
117    // TODO: What if address is mandatory for slots? Then it would be possible to make `#[slot]`
118    //  implicit
119    /// Read-only `#[slot]` argument with an address.
120    ///
121    /// Example: `#[slot] (from_address, from): (&Address, &MaybeData<Slot>),`
122    SlotRo,
123    /// Read-write `#[slot]` argument with an address.
124    ///
125    /// Example: `#[slot] (from_address, from): (&Address, &mut MaybeData<Slot>),`
126    SlotRw,
127    /// `#[input]` argument.
128    ///
129    /// Example: `#[input] balance: &Balance,`
130    Input,
131    /// Explicit `#[output]` argument.
132    ///
133    /// NOTE: Skipped if return type's `T` is `()`.
134    ///
135    /// Example: `#[output] out: &mut VariableBytes<1024>,`
136    Output,
137    /// `T` of [`Result<T, ContractError>`] return type or simply return type if it is not fallible.
138    ///
139    /// NOTE: Skipped if return type's `T` is `()`.
140    ///
141    /// Example: `-> Balance`
142    Return,
143}
144
145impl ContractMetadataKind {
146    // TODO: Implement `TryFrom` once it is available in const environment
147    /// Try to create an instance from its `u8` representation
148    #[inline(always)]
149    pub const fn try_from_u8(byte: u8) -> Option<Self> {
150        Some(match byte {
151            0 => Self::Contract,
152            1 => Self::Trait,
153            2 => Self::Init,
154            3 => Self::UpdateStateless,
155            4 => Self::UpdateStatefulRo,
156            5 => Self::UpdateStatefulRw,
157            6 => Self::ViewStateless,
158            7 => Self::ViewStateful,
159            8 => Self::EnvRo,
160            9 => Self::EnvRw,
161            10 => Self::TmpRo,
162            11 => Self::TmpRw,
163            12 => Self::SlotRo,
164            13 => Self::SlotRw,
165            14 => Self::Input,
166            15 => Self::Output,
167            16 => Self::Return,
168            _ => {
169                return None;
170            }
171        })
172    }
173
174    // TODO: Create wrapper type for metadata bytes and move this method there
175    /// Produce compact metadata.
176    ///
177    /// Compact metadata retains the shape but throws some details. Specifically, the following
178    /// transformations are applied to metadata (crucially, method names are retained!):
179    /// * Struct, trait, enum, and enum variant names removed (replaced with 0 bytes names)
180    /// * Structs and enum variants turned into tuple variants (removing field names)
181    /// * Method argument names removed
182    ///
183    /// This means that two methods with different argument names or struct field names, but the
184    /// same shape otherwise are considered identical, allowing for limited future refactoring
185    /// opportunities without changing compact metadata shape, which is important for
186    /// [`MethodFingerprint`] (though [`Self::compact_external_args()`] is used by it internally).
187    ///
188    /// [`MethodFingerprint`]: crate::method::MethodFingerprint
189    ///
190    /// Returns `None` if the input is invalid or too long.
191    pub const fn compact(metadata: &[u8]) -> Option<([u8; MAX_METADATA_CAPACITY], usize)> {
192        compact_metadata(metadata, false)
193    }
194
195    // TODO: Create wrapper type for metadata bytes and move this method there
196    /// Produce compact metadata for `ExternalArgs`.
197    ///
198    /// Similar to [`Self::compact()`], but arguments that are not reflected in `ExternalArgs` will
199    /// be skipped since they don't impact `ExternalArgs` API. This is used for
200    /// [`MethodFingerprint`] derivation.
201    ///
202    /// [`MethodFingerprint`]: crate::method::MethodFingerprint
203    ///
204    /// Returns `None` if the input is invalid or too long.
205    pub const fn compact_external_args(
206        metadata: &[u8],
207    ) -> Option<([u8; MAX_METADATA_CAPACITY], usize)> {
208        compact_metadata(metadata, true)
209    }
210}