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 af 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 af 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 af 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 /// * Length of the argument name in bytes (u8, except for [`Self::EnvRo`] and [`Self::EnvRw`])
66 /// * Argument name as UTF-8 bytes (except for [`Self::EnvRo`] and [`Self::EnvRw`])
67 /// * Only for [`Self::Input`] and [`Self::Output`] recursive metadata of argument's type as
68 /// described in [`IoTypeMetadataKind`] with following exception:
69 /// * For last [`Self::Output`] this is skipped if method is [`Self::Init`] (since it is
70 /// statically known to be `Self`) and present otherwise
71 ///
72 /// [`IoTypeMetadataKind`]: ab_io_type::metadata::IoTypeMetadataKind
73 ///
74 /// NOTE: [`Self::Output`], regardless of whether it is a return type or explicit `#[output]`
75 /// argument is encoded as a separate argument and counts towards number of arguments. At the
76 /// same time, `self` doesn't count towards the number of arguments as it is implicitly defined
77 /// 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 or `T` of [`Result<T, ContractError>`] return type or simply
132 /// return type if it is not fallible.
133 ///
134 /// NOTE: Skipped if return type's `T` is `()`.
135 ///
136 /// Example: `#[output] out: &mut VariableBytes<1024>,`
137 Output,
138}
139
140impl ContractMetadataKind {
141 // TODO: Implement `TryFrom` once it is available in const environment
142 /// Try to create an instance from its `u8` representation
143 #[inline(always)]
144 pub const fn try_from_u8(byte: u8) -> Option<Self> {
145 Some(match byte {
146 0 => Self::Contract,
147 1 => Self::Trait,
148 2 => Self::Init,
149 3 => Self::UpdateStateless,
150 4 => Self::UpdateStatefulRo,
151 5 => Self::UpdateStatefulRw,
152 6 => Self::ViewStateless,
153 7 => Self::ViewStateful,
154 8 => Self::EnvRo,
155 9 => Self::EnvRw,
156 10 => Self::TmpRo,
157 11 => Self::TmpRw,
158 12 => Self::SlotRo,
159 13 => Self::SlotRw,
160 14 => Self::Input,
161 15 => Self::Output,
162 _ => {
163 return None;
164 }
165 })
166 }
167
168 // TODO: Create wrapper type for metadata bytes and move this method there
169 /// Produce compact metadata.
170 ///
171 /// Compact metadata retains the shape, but throws some details. Specifically following
172 /// transformations are applied to metadata (crucially, method names are retained!):
173 /// * Struct, trait, enum and enum variant names removed (replaced with 0 bytes names)
174 /// * Structs and enum variants turned into tuple variants (removing field names)
175 /// * Method argument names removed (removing argument names)
176 ///
177 /// This means that two methods with different argument names or struct field names, but the
178 /// same shape otherwise are considered identical, allowing for limited future refactoring
179 /// opportunities without changing compact metadata shape, which is important for
180 /// [`MethodFingerprint`].
181 ///
182 /// [`MethodFingerprint`]: crate::method::MethodFingerprint
183 ///
184 /// Returns `None` if input is invalid or too long.
185 pub const fn compact(metadata: &[u8]) -> Option<([u8; MAX_METADATA_CAPACITY], usize)> {
186 compact_metadata(metadata, false)
187 }
188
189 // TODO: Create wrapper type for metadata bytes and move this method there
190 /// Produce compact metadata for `ExternalArgs`.
191 ///
192 /// Similar to [`Self::compact()`] arguments that are not reflected in `ExternalArgs` will be
193 /// skipped since they don't impact `ExternalArgs` API. This is used for `MethodFingerprint`
194 /// derivation.
195 ///
196 /// Returns `None` if input is invalid or too long.
197 pub const fn compact_external_args(
198 metadata: &[u8],
199 ) -> Option<([u8; MAX_METADATA_CAPACITY], usize)> {
200 compact_metadata(metadata, true)
201 }
202}