1use async_graphql::*;
6use iota_sdk_types::{StructTag, TypeTag};
7use iota_types::base_types::MoveObjectType;
8use move_binary_format::file_format::AbilitySet;
9use move_core_types::annotated_value as A;
10use serde::{Deserialize, Serialize};
11
12use crate::{
13 data::package_resolver::PackageResolver, error::Error, types::open_move_type::MoveAbility,
14};
15
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub(crate) struct MoveType {
18 pub native: TypeTag,
19}
20
21scalar!(
22 MoveTypeSignature,
23 "MoveTypeSignature",
24 "The signature of a concrete Move Type (a type with all its type parameters instantiated with \
25 concrete types, that contains no references), corresponding to the following recursive type:
26
27type MoveTypeSignature =
28 \"address\"
29 | \"bool\"
30 | \"u8\" | \"u16\" | ... | \"u256\"
31 | { vector: MoveTypeSignature }
32 | {
33 datatype: {
34 package: string,
35 module: string,
36 type: string,
37 typeParameters: [MoveTypeSignature],
38 }
39 }"
40);
41
42scalar!(
43 MoveTypeLayout,
44 "MoveTypeLayout",
45 "The shape of a concrete Move Type (a type with all its type parameters instantiated with \
46 concrete types), corresponding to the following recursive type:
47
48type MoveTypeLayout =
49 \"address\"
50 | \"bool\"
51 | \"u8\" | \"u16\" | ... | \"u256\"
52 | { vector: MoveTypeLayout }
53 | {
54 struct: {
55 type: string,
56 fields: [{ name: string, layout: MoveTypeLayout }],
57 }
58 }
59 | { enum: [{
60 type: string,
61 variants: [{
62 name: string,
63 fields: [{ name: string, layout: MoveTypeLayout }],
64 }]
65 }]
66 }"
67);
68
69#[derive(Serialize, Deserialize, Debug)]
70#[serde(rename_all = "camelCase")]
71pub(crate) enum MoveTypeSignature {
72 Address,
73 Bool,
74 U8,
75 U16,
76 U32,
77 U64,
78 U128,
79 U256,
80 Vector(Box<MoveTypeSignature>),
81 Datatype {
82 package: String,
83 module: String,
84 #[serde(rename = "type")]
85 type_: String,
86 #[serde(rename = "typeParameters")]
87 type_parameters: Vec<MoveTypeSignature>,
88 },
89}
90
91#[derive(Serialize, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub(crate) enum MoveTypeLayout {
94 Address,
95 Bool,
96 U8,
97 U16,
98 U32,
99 U64,
100 U128,
101 U256,
102 Vector(Box<MoveTypeLayout>),
103 Struct(MoveStructLayout),
104 Enum(MoveEnumLayout),
105 InvalidType,
106}
107
108#[derive(Serialize, Deserialize)]
109pub(crate) struct MoveStructLayout {
110 #[serde(rename = "type")]
111 type_: String,
112 fields: Vec<MoveFieldLayout>,
113}
114
115#[derive(Serialize, Deserialize)]
116pub(crate) struct MoveEnumLayout {
117 variants: Vec<MoveVariantLayout>,
118}
119
120#[derive(Serialize, Deserialize)]
121pub(crate) struct MoveVariantLayout {
122 name: String,
123 layout: Vec<MoveFieldLayout>,
124}
125
126#[derive(Serialize, Deserialize)]
127pub(crate) struct MoveFieldLayout {
128 name: String,
129 layout: MoveTypeLayout,
130}
131
132#[Object]
134impl MoveType {
135 async fn repr(&self) -> String {
137 self.native.to_canonical_string(true)
138 }
139
140 async fn signature(&self) -> Result<MoveTypeSignature> {
142 self.signature_impl().extend()
145 }
146
147 async fn layout(&self, ctx: &Context<'_>) -> Result<MoveTypeLayout> {
150 let resolver: &PackageResolver = ctx
151 .data()
152 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
153 .extend()?;
154
155 let Some(layout) = self.layout_impl(resolver).await.extend()? else {
156 return Ok(MoveTypeLayout::InvalidType);
157 };
158
159 MoveTypeLayout::try_from(layout).extend()
160 }
161
162 async fn abilities(&self, ctx: &Context<'_>) -> Result<Vec<MoveAbility>> {
165 let resolver: &PackageResolver = ctx
166 .data()
167 .map_err(|_| Error::Internal("Unable to fetch Package Cache.".to_string()))
168 .extend()?;
169
170 let Some(abilities) = self.abilities_impl(resolver).await.extend()? else {
171 return Ok(vec![]);
172 };
173
174 Ok(abilities.into_iter().map(MoveAbility::from).collect())
175 }
176}
177
178impl MoveType {
179 fn signature_impl(&self) -> Result<MoveTypeSignature, Error> {
180 MoveTypeSignature::try_from(self.native.clone())
181 }
182
183 pub(crate) async fn layout_impl(
184 &self,
185 resolver: &PackageResolver,
186 ) -> Result<Option<A::MoveTypeLayout>, Error> {
187 Ok(Some(
188 resolver
189 .type_layout(self.native.clone())
190 .await
191 .map_err(|e| {
192 Error::Internal(format!(
193 "Error calculating layout for {}: {e}",
194 self.native.to_canonical_string(true),
195 ))
196 })?,
197 ))
198 }
199
200 pub(crate) async fn abilities_impl(
201 &self,
202 resolver: &PackageResolver,
203 ) -> Result<Option<AbilitySet>, Error> {
204 Ok(Some(
205 resolver.abilities(self.native.clone()).await.map_err(|e| {
206 Error::Internal(format!(
207 "Error calculating abilities for {}: {e}",
208 self.native.to_canonical_string(true),
209 ))
210 })?,
211 ))
212 }
213}
214
215impl From<MoveObjectType> for MoveType {
216 fn from(obj: MoveObjectType) -> Self {
217 obj.into_inner().into()
218 }
219}
220
221impl From<StructTag> for MoveType {
222 fn from(tag: StructTag) -> Self {
223 let tag: TypeTag = tag.into();
224 Self { native: tag }
225 }
226}
227
228impl From<TypeTag> for MoveType {
229 fn from(native: TypeTag) -> Self {
230 Self { native }
231 }
232}
233
234impl TryFrom<TypeTag> for MoveTypeSignature {
235 type Error = Error;
236
237 fn try_from(tag: TypeTag) -> Result<Self, Error> {
238 use TypeTag as T;
239
240 Ok(match tag {
241 T::Signer => return Err(unexpected_signer_error()),
242
243 T::U8 => Self::U8,
244 T::U16 => Self::U16,
245 T::U32 => Self::U32,
246 T::U64 => Self::U64,
247 T::U128 => Self::U128,
248 T::U256 => Self::U256,
249
250 T::Bool => Self::Bool,
251 T::Address => Self::Address,
252
253 T::Vector(v) => Self::Vector(Box::new(Self::try_from(*v)?)),
254
255 T::Struct(s) => Self::Datatype {
256 package: s.address().to_canonical_string(true),
257 module: s.module().as_str().to_owned(),
258 type_: s.name().as_str().to_owned(),
259 type_parameters: s
260 .type_params()
261 .iter()
262 .cloned()
263 .map(Self::try_from)
264 .collect::<Result<Vec<_>, _>>()?,
265 },
266 })
267 }
268}
269
270impl TryFrom<A::MoveTypeLayout> for MoveTypeLayout {
271 type Error = Error;
272
273 fn try_from(layout: A::MoveTypeLayout) -> Result<Self, Error> {
274 use A::MoveTypeLayout as TL;
275
276 Ok(match layout {
277 TL::Signer => return Err(unexpected_signer_error()),
278
279 TL::U8 => Self::U8,
280 TL::U16 => Self::U16,
281 TL::U32 => Self::U32,
282 TL::U64 => Self::U64,
283 TL::U128 => Self::U128,
284 TL::U256 => Self::U256,
285
286 TL::Bool => Self::Bool,
287 TL::Address => Self::Address,
288
289 TL::Vector(v) => Self::Vector(Box::new(Self::try_from(*v)?)),
290 TL::Struct(s) => Self::Struct((*s).try_into()?),
291 TL::Enum(e) => Self::Enum((*e).try_into()?),
292 })
293 }
294}
295
296impl TryFrom<A::MoveEnumLayout> for MoveEnumLayout {
297 type Error = Error;
298
299 fn try_from(layout: A::MoveEnumLayout) -> Result<Self, Error> {
300 let A::MoveEnumLayout { variants, .. } = layout;
301 let mut variant_layouts = Vec::new();
302 for ((name, _), variant_fields) in variants {
303 let mut field_layouts = Vec::new();
304 for field in variant_fields {
305 field_layouts.push(MoveFieldLayout::try_from(field)?);
306 }
307 variant_layouts.push(MoveVariantLayout {
308 name: name.to_string(),
309 layout: field_layouts,
310 });
311 }
312
313 Ok(MoveEnumLayout {
314 variants: variant_layouts,
315 })
316 }
317}
318
319impl TryFrom<A::MoveStructLayout> for MoveStructLayout {
320 type Error = Error;
321
322 fn try_from(layout: A::MoveStructLayout) -> Result<Self, Error> {
323 Ok(Self {
324 type_: layout.type_.to_canonical_string(true),
325 fields: layout
326 .fields
327 .into_iter()
328 .map(MoveFieldLayout::try_from)
329 .collect::<Result<_, _>>()?,
330 })
331 }
332}
333
334impl TryFrom<A::MoveFieldLayout> for MoveFieldLayout {
335 type Error = Error;
336
337 fn try_from(layout: A::MoveFieldLayout) -> Result<Self, Error> {
338 Ok(Self {
339 name: layout.name.to_string(),
340 layout: layout.layout.try_into()?,
341 })
342 }
343}
344
345pub(crate) fn unexpected_signer_error() -> Error {
348 Error::Internal("Unexpected value of type: signer.".to_string())
349}
350
351#[cfg(test)]
352mod tests {
353 use std::str::FromStr;
354
355 use expect_test::expect;
356
357 use super::*;
358
359 fn signature(repr: impl Into<String>) -> Result<MoveTypeSignature, Error> {
360 let tag = TypeTag::from_str(repr.into().as_str()).unwrap();
361 MoveType::from(tag).signature_impl()
362 }
363
364 #[test]
365 fn complex_type() {
366 let sig = signature("vector<0x42::foo::Bar<address, u32, bool, u256>>").unwrap();
367 let expect = expect![[r#"
368 Vector(
369 Datatype {
370 package: "0x0000000000000000000000000000000000000000000000000000000000000042",
371 module: "foo",
372 type_: "Bar",
373 type_parameters: [
374 Address,
375 U32,
376 Bool,
377 U256,
378 ],
379 },
380 )"#]];
381 expect.assert_eq(&format!("{sig:#?}"));
382 }
383
384 #[test]
385 fn signer_type() {
386 let err = signature("signer").unwrap_err();
387 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
388 expect.assert_eq(&format!("{err:?}"));
389 }
390
391 #[test]
392 fn nested_signer_type() {
393 let err = signature("0x42::baz::Qux<u32, vector<signer>>").unwrap_err();
394 let expect = expect![[r#"Internal("Unexpected value of type: signer.")"#]];
395 expect.assert_eq(&format!("{err:?}"));
396 }
397}