iota_json/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{
6    collections::{BTreeMap, VecDeque},
7    fmt::{self, Debug, Formatter},
8    str::FromStr,
9};
10
11use anyhow::{anyhow, bail};
12use fastcrypto::encoding::{Encoding, Hex};
13use iota_types::{
14    base_types::{
15        Identifier, IotaAddress, ObjectID, RESOLVED_ASCII_STR, RESOLVED_STD_OPTION,
16        RESOLVED_UTF8_STR, StructTag, TxContext, TxContextKind, TypeTag, is_primitive_type_tag,
17        move_ascii_str_layout, move_utf8_str_layout,
18    },
19    id::{self, RESOLVED_IOTA_ID},
20    iota_sdk_types_conversions::struct_tag_core_to_sdk,
21    move_package::MovePackage,
22    object::bounded_visitor::BoundedVisitor,
23    transfer::RESOLVED_RECEIVING_STRUCT,
24};
25use move_binary_format::{
26    CompiledModule, binary_config::BinaryConfig, file_format::SignatureToken,
27};
28use move_bytecode_utils::resolve_struct;
29pub use move_core_types::annotated_value::MoveTypeLayout;
30use move_core_types::{
31    account_address::AccountAddress,
32    annotated_value::{MoveFieldLayout, MoveStruct, MoveValue, MoveVariant},
33    runtime_value as R,
34    u256::U256,
35};
36use schemars::JsonSchema;
37use serde::{Deserialize, Serialize};
38use serde_json::{Number, Value as JsonValue, json};
39
40const HEX_PREFIX: &str = "0x";
41
42#[cfg(test)]
43mod tests;
44
45/// A list of error categories encountered when parsing numbers.
46#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
47pub enum IotaJsonValueErrorKind {
48    /// JSON value must be of specific types.
49    ValueTypeNotAllowed,
50
51    /// JSON arrays must be homogeneous.
52    ArrayNotHomogeneous,
53}
54
55#[derive(Debug)]
56pub struct IotaJsonValueError {
57    kind: IotaJsonValueErrorKind,
58    val: JsonValue,
59}
60
61impl IotaJsonValueError {
62    pub fn new(val: &JsonValue, kind: IotaJsonValueErrorKind) -> Self {
63        Self {
64            kind,
65            val: val.clone(),
66        }
67    }
68}
69
70impl std::error::Error for IotaJsonValueError {}
71
72impl fmt::Display for IotaJsonValueError {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        let err_str = match self.kind {
75            IotaJsonValueErrorKind::ValueTypeNotAllowed => {
76                format!("JSON value type {} not allowed.", self.val)
77            }
78            IotaJsonValueErrorKind::ArrayNotHomogeneous => {
79                format!("Array not homogeneous. Mismatched value: {}.", self.val)
80            }
81        };
82        write!(f, "{err_str}")
83    }
84}
85
86// Intermediate type to hold resolved args
87#[derive(Eq, PartialEq, Debug)]
88pub enum ResolvedCallArg {
89    Object(ObjectID),
90    Pure(Vec<u8>),
91    ObjVec(Vec<ObjectID>),
92}
93
94#[derive(Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
95pub struct IotaJsonValue(JsonValue);
96impl IotaJsonValue {
97    pub fn new(json_value: JsonValue) -> Result<IotaJsonValue, anyhow::Error> {
98        Self::check_value(&json_value)?;
99        Ok(Self(json_value))
100    }
101
102    fn check_value(json_value: &JsonValue) -> Result<(), anyhow::Error> {
103        match json_value {
104            // No checks needed for Bool and String
105            JsonValue::Bool(_) | JsonValue::String(_) => (),
106            JsonValue::Number(n) => {
107                // Must be castable to u64
108                if !n.is_u64() {
109                    bail!("{n} not allowed. Number must be unsigned integer of at most u32");
110                }
111            }
112            // Must be homogeneous
113            JsonValue::Array(a) => {
114                // Fail if not homogeneous
115                check_valid_homogeneous(&JsonValue::Array(a.to_vec()))?
116            }
117            JsonValue::Object(v) => {
118                for (_, value) in v {
119                    Self::check_value(value)?;
120                }
121            }
122            JsonValue::Null => bail!("Null not allowed."),
123        };
124        Ok(())
125    }
126
127    pub fn from_object_id(id: ObjectID) -> IotaJsonValue {
128        Self(JsonValue::String(id.to_hex()))
129    }
130
131    pub fn to_bcs_bytes(&self, ty: &MoveTypeLayout) -> Result<Vec<u8>, anyhow::Error> {
132        let move_value = Self::to_move_value(&self.0, ty)?;
133        R::MoveValue::simple_serialize(&move_value)
134            .ok_or_else(|| anyhow!("Unable to serialize {:?}. Expected {}", move_value, ty))
135    }
136
137    pub fn from_bcs_bytes(
138        layout: Option<&MoveTypeLayout>,
139        bytes: &[u8],
140    ) -> Result<Self, anyhow::Error> {
141        let Some(layout) = layout else {
142            return IotaJsonValue::new(json!(bytes));
143        };
144        if let Ok(Some(value)) = BoundedVisitor::deserialize_value(bytes, layout)
145            .map(|move_value| move_value_to_json(&move_value))
146        {
147            return IotaJsonValue::new(value);
148        }
149        let value = JsonValue::Array(
150            bytes
151                .iter()
152                .map(|b| JsonValue::Number(Number::from(*b)))
153                .collect(),
154        );
155        IotaJsonValue::new(value)
156    }
157
158    pub fn to_json_value(&self) -> JsonValue {
159        self.0.clone()
160    }
161
162    pub fn to_iota_address(&self) -> anyhow::Result<IotaAddress> {
163        json_value_to_iota_address(&self.0)
164    }
165
166    fn handle_inner_struct_layout(
167        inner_vec: &[MoveFieldLayout],
168        val: &JsonValue,
169        ty: &MoveTypeLayout,
170        s: &String,
171    ) -> Result<R::MoveValue, anyhow::Error> {
172        // delegate MoveValue construction to the case when JsonValue::String and
173        // MoveTypeLayout::Vector are handled to get an address (with 0x string
174        // prefix) or a vector of u8s (no prefix)
175        debug_assert!(matches!(val, JsonValue::String(_)));
176
177        if inner_vec.len() != 1 {
178            bail!(
179                "Cannot convert string arg {s} to {ty} which is expected \
180                 to be a struct with one field"
181            );
182        }
183
184        match &inner_vec[0].layout {
185            MoveTypeLayout::Vector(inner) => match **inner {
186                MoveTypeLayout::U8 => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
187                    Self::to_move_value(val, &inner_vec[0].layout.clone())?,
188                ]))),
189                MoveTypeLayout::Address => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
190                    Self::to_move_value(val, &MoveTypeLayout::Address)?,
191                ]))),
192                _ => bail!(
193                    "Cannot convert string arg {s} to {ty} \
194                             which is expected to be a struct \
195                             with one field of address or u8 vector type"
196                ),
197            },
198            MoveTypeLayout::Struct(struct_layout)
199                if struct_tag_core_to_sdk(&struct_layout.type_).is_id() =>
200            {
201                Ok(R::MoveValue::Struct(R::MoveStruct(vec![
202                    Self::to_move_value(val, &inner_vec[0].layout.clone())?,
203                ])))
204            }
205            _ => bail!(
206                "Cannot convert string arg {s} to {ty} which is expected \
207                 to be a struct with one field of a vector type"
208            ),
209        }
210    }
211
212    pub fn to_move_value(
213        val: &JsonValue,
214        ty: &MoveTypeLayout,
215    ) -> Result<R::MoveValue, anyhow::Error> {
216        Ok(match (val, ty) {
217            // Bool to Bool is simple
218            (JsonValue::Bool(b), MoveTypeLayout::Bool) => R::MoveValue::Bool(*b),
219
220            // In constructor, we have already checked that the JSON number is unsigned int of at
221            // most U32
222            (JsonValue::Number(n), MoveTypeLayout::U8) => match n.as_u64() {
223                Some(x) => R::MoveValue::U8(u8::try_from(x)?),
224                None => bail!("{} is not a valid number. Only u8 allowed.", n),
225            },
226            (JsonValue::Number(n), MoveTypeLayout::U16) => match n.as_u64() {
227                Some(x) => R::MoveValue::U16(u16::try_from(x)?),
228                None => bail!("{} is not a valid number. Only u16 allowed.", n),
229            },
230            (JsonValue::Number(n), MoveTypeLayout::U32) => match n.as_u64() {
231                Some(x) => R::MoveValue::U32(u32::try_from(x)?),
232                None => bail!("{} is not a valid number. Only u32 allowed.", n),
233            },
234
235            // u8, u16, u32, u64, u128, u256 can be encoded as String
236            (JsonValue::String(s), MoveTypeLayout::U8) => {
237                R::MoveValue::U8(u8::try_from(convert_string_to_u256(s.as_str())?)?)
238            }
239            (JsonValue::String(s), MoveTypeLayout::U16) => {
240                R::MoveValue::U16(u16::try_from(convert_string_to_u256(s.as_str())?)?)
241            }
242            (JsonValue::String(s), MoveTypeLayout::U32) => {
243                R::MoveValue::U32(u32::try_from(convert_string_to_u256(s.as_str())?)?)
244            }
245            (JsonValue::String(s), MoveTypeLayout::U64) => {
246                R::MoveValue::U64(u64::try_from(convert_string_to_u256(s.as_str())?)?)
247            }
248            (JsonValue::String(s), MoveTypeLayout::U128) => {
249                R::MoveValue::U128(u128::try_from(convert_string_to_u256(s.as_str())?)?)
250            }
251            (JsonValue::String(s), MoveTypeLayout::U256) => {
252                R::MoveValue::U256(convert_string_to_u256(s.as_str())?)
253            }
254            // For ascii and utf8 strings
255            (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
256                if is_move_string_type(&struct_tag_core_to_sdk(&struct_layout.type_)) =>
257            {
258                R::MoveValue::Vector(s.as_bytes().iter().copied().map(R::MoveValue::U8).collect())
259            }
260            // For ID
261            (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
262                if struct_tag_core_to_sdk(&struct_layout.type_).is_id() =>
263            {
264                if struct_layout.fields.len() != 1 {
265                    bail!(
266                        "Cannot convert string arg {s} to {} which is expected to be a struct with one field",
267                        struct_layout.type_
268                    );
269                };
270                let addr = IotaAddress::from_str(s)?;
271                R::MoveValue::Address(AccountAddress::new(addr.into_bytes()))
272            }
273            (JsonValue::Object(o), MoveTypeLayout::Struct(struct_layout)) => {
274                let mut field_values = vec![];
275                for layout in struct_layout.fields.iter() {
276                    let field = o
277                        .get(layout.name.as_str())
278                        .ok_or_else(|| anyhow!("Missing field {} for struct {ty}", layout.name))?;
279                    field_values.push(Self::to_move_value(field, &layout.layout)?);
280                }
281                R::MoveValue::Struct(R::MoveStruct(field_values))
282            }
283            // Unnest fields
284            (value, MoveTypeLayout::Struct(struct_layout)) if struct_layout.fields.len() == 1 => {
285                Self::to_move_value(value, &struct_layout.fields[0].layout)?
286            }
287            (JsonValue::String(s), MoveTypeLayout::Vector(t)) => {
288                match &**t {
289                    MoveTypeLayout::U8 => {
290                        // We can encode U8 Vector as string in 2 ways
291                        // 1. If it starts with 0x, we treat it as hex strings, where each pair is a
292                        //    byte
293                        // 2. If it does not start with 0x, we treat each character as an ASCII
294                        //    encoded byte
295                        // We have to support both for the convenience of the user. This is because
296                        // sometime we need Strings as arg Other times we need vec of hex bytes for
297                        // address. Issue is both Address and Strings are represented as Vec<u8> in
298                        // Move call
299                        let vec = if s.starts_with(HEX_PREFIX) {
300                            // If starts with 0x, treat as hex vector
301                            Hex::decode(s).map_err(|e| anyhow!(e))?
302                        } else {
303                            // Else raw bytes
304                            s.as_bytes().to_vec()
305                        };
306                        R::MoveValue::Vector(vec.iter().copied().map(R::MoveValue::U8).collect())
307                    }
308                    MoveTypeLayout::Struct(struct_layout) => {
309                        Self::handle_inner_struct_layout(&struct_layout.fields, val, ty, s)?
310                    }
311                    _ => bail!("Cannot convert string arg {s} to {ty}"),
312                }
313            }
314
315            // We have already checked that the array is homogeneous in the constructor
316            (JsonValue::Array(a), MoveTypeLayout::Vector(inner)) => {
317                // Recursively build an IntermediateValue array
318                R::MoveValue::Vector(
319                    a.iter()
320                        .map(|i| Self::to_move_value(i, inner))
321                        .collect::<Result<Vec<_>, _>>()?,
322                )
323            }
324
325            (v, MoveTypeLayout::Address) => {
326                let addr = json_value_to_iota_address(v)?;
327                R::MoveValue::Address(AccountAddress::new(addr.into_bytes()))
328            }
329
330            _ => bail!("Unexpected arg {val:?} for expected type {ty:?}"),
331        })
332    }
333}
334
335impl Debug for IotaJsonValue {
336    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
337        write!(f, "{}", self.0)
338    }
339}
340
341fn json_value_to_iota_address(value: &JsonValue) -> anyhow::Result<IotaAddress> {
342    match value {
343        JsonValue::String(s) => {
344            let s = s.trim().to_lowercase();
345            if !s.starts_with(HEX_PREFIX) {
346                bail!("Address hex string must start with 0x.",);
347            }
348            Ok(IotaAddress::from_str(&s)?)
349        }
350        JsonValue::Array(bytes) => {
351            fn value_to_byte_array(v: &Vec<JsonValue>) -> Option<Vec<u8>> {
352                let mut bytes = vec![];
353                for b in v {
354                    let b = b.as_u64()?;
355                    if b <= u8::MAX as u64 {
356                        bytes.push(b as u8);
357                    } else {
358                        return None;
359                    }
360                }
361                Some(bytes)
362            }
363            let bytes = value_to_byte_array(bytes)
364                .ok_or_else(|| anyhow!("Invalid input: Cannot parse input into IotaAddress."))?;
365            Ok(IotaAddress::from_bytes(bytes)?)
366        }
367        v => bail!("Unexpected arg {v} for expected type address"),
368    }
369}
370
371fn move_value_to_json(move_value: &MoveValue) -> Option<JsonValue> {
372    Some(match move_value {
373        MoveValue::Vector(values) => JsonValue::Array(
374            values
375                .iter()
376                .map(move_value_to_json)
377                .collect::<Option<_>>()?,
378        ),
379        MoveValue::Bool(v) => json!(v),
380        MoveValue::Signer(v) | MoveValue::Address(v) => {
381            json!(IotaAddress::new(v.into_bytes()).to_string())
382        }
383        MoveValue::U8(v) => json!(v),
384        MoveValue::U64(v) => json!(v.to_string()),
385        MoveValue::U128(v) => json!(v.to_string()),
386        MoveValue::U16(v) => json!(v),
387        MoveValue::U32(v) => json!(v),
388        MoveValue::U256(v) => json!(v.to_string()),
389        MoveValue::Struct(move_struct) => match move_struct {
390            MoveStruct { fields, type_ } if is_move_string_type(&struct_tag_core_to_sdk(type_)) => {
391                // ascii::string and utf8::string has a single bytes field.
392                let (_, v) = fields.first()?;
393                let string: String = bcs::from_bytes(&v.simple_serialize()?).ok()?;
394                json!(string)
395            }
396            MoveStruct { fields, type_ } if struct_tag_core_to_sdk(type_).is_option() => {
397                // option has a single vec field.
398                let (_, v) = fields.first()?;
399                if let MoveValue::Vector(v) = v {
400                    JsonValue::Array(v.iter().filter_map(move_value_to_json).collect::<Vec<_>>())
401                } else {
402                    return None;
403                }
404            }
405            MoveStruct { fields, type_ } if struct_tag_core_to_sdk(type_).is_id() => {
406                // option has a single vec field.
407                let (_, v) = fields.first()?;
408                if let MoveValue::Address(address) = v {
409                    json!(IotaAddress::new(address.into_bytes()))
410                } else {
411                    return None;
412                }
413            }
414            // We only care about values here, assuming struct type information is known at the
415            // client side.
416            MoveStruct { fields, .. } => {
417                let fields = fields
418                    .iter()
419                    .map(|(key, value)| (key, move_value_to_json(value)))
420                    .collect::<BTreeMap<_, _>>();
421                json!(fields)
422            }
423        },
424        // Don't return the type assuming type information is known at the client side.
425        MoveValue::Variant(MoveVariant {
426            type_: _,
427            tag: _,
428            variant_name,
429            fields,
430        }) => {
431            let fields = fields
432                .iter()
433                .map(|(key, value)| (key, move_value_to_json(value)))
434                .collect::<BTreeMap<_, _>>();
435            json!({
436                "variant": variant_name.to_string(),
437                "fields": fields,
438            })
439        }
440    })
441}
442
443fn is_move_string_type(tag: &StructTag) -> bool {
444    tag.is_string() || tag.is_ascii_string()
445}
446
447impl FromStr for IotaJsonValue {
448    type Err = anyhow::Error;
449    fn from_str(s: &str) -> Result<Self, anyhow::Error> {
450        /// Split a string by commas, but only at the top level (not inside
451        /// brackets). This allows nested arrays like `[[a,b],[c,d]]` to be
452        /// split correctly into `[a,b]` and `[c,d]`.
453        fn split_top_level_commas(s: &str) -> Vec<&str> {
454            let mut parts = Vec::new();
455            let mut depth = 0usize;
456            let mut start = 0;
457            for (i, ch) in s.char_indices() {
458                match ch {
459                    '[' => depth += 1,
460                    ']' => depth = depth.saturating_sub(1),
461                    ',' if depth == 0 => {
462                        parts.push(&s[start..i]);
463                        start = i + 1;
464                    }
465                    _ => {}
466                }
467            }
468            parts.push(&s[start..]);
469            parts
470        }
471
472        fn try_escape_array(s: &str) -> JsonValue {
473            let s = s.trim();
474            if s.starts_with('[') && s.ends_with(']') {
475                if let Some(inner) = s.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
476                    return JsonValue::Array(
477                        split_top_level_commas(inner)
478                            .into_iter()
479                            .map(try_escape_array)
480                            .collect(),
481                    );
482                }
483            }
484            json!(s)
485        }
486        // if serde_json fails, the failure usually cause by missing quote escapes, try
487        // parse array manually.
488        IotaJsonValue::new(serde_json::from_str(s).unwrap_or_else(|_| try_escape_array(s)))
489    }
490}
491
492#[derive(Eq, PartialEq, Debug, Clone, Hash)]
493enum ValidJsonType {
494    Bool,
495    Number,
496    String,
497    Array,
498    // Matches any type
499    Any,
500}
501
502/// Check via BFS
503/// The invariant is that all types at a given level must be the same or be
504/// empty, and all must be valid
505pub fn check_valid_homogeneous(val: &JsonValue) -> Result<(), IotaJsonValueError> {
506    let mut deq: VecDeque<&JsonValue> = VecDeque::new();
507    deq.push_back(val);
508    check_valid_homogeneous_rec(&mut deq)
509}
510
511/// Check via BFS
512/// The invariant is that all types at a given level must be the same or be
513/// empty
514fn check_valid_homogeneous_rec(
515    curr_q: &mut VecDeque<&JsonValue>,
516) -> Result<(), IotaJsonValueError> {
517    if curr_q.is_empty() {
518        // Nothing to do
519        return Ok(());
520    }
521    // Queue for the next level
522    let mut next_q = VecDeque::new();
523    // The types at this level must be the same
524    let mut level_type = ValidJsonType::Any;
525
526    // Process all in this queue/level
527    while let Some(v) = curr_q.pop_front() {
528        let curr = match v {
529            JsonValue::Bool(_) => ValidJsonType::Bool,
530            JsonValue::Number(x) if x.is_u64() => ValidJsonType::Number,
531            JsonValue::String(_) => ValidJsonType::String,
532            JsonValue::Array(w) => {
533                // Add to the next level
534                w.iter().for_each(|t| next_q.push_back(t));
535                ValidJsonType::Array
536            }
537            // Not valid
538            _ => {
539                return Err(IotaJsonValueError::new(
540                    v,
541                    IotaJsonValueErrorKind::ValueTypeNotAllowed,
542                ));
543            }
544        };
545
546        if level_type == ValidJsonType::Any {
547            // Update the level with the first found type
548            level_type = curr;
549        } else if level_type != curr {
550            // Mismatch in the level
551            return Err(IotaJsonValueError::new(
552                v,
553                IotaJsonValueErrorKind::ArrayNotHomogeneous,
554            ));
555        }
556    }
557    // Process the next level
558    check_valid_homogeneous_rec(&mut next_q)
559}
560
561/// Checks if a give SignatureToken represents a primitive type and, if so,
562/// returns MoveTypeLayout for this type (if available). The reason we need to
563/// return both information about whether a SignatureToken represents a
564/// primitive and an Option representing MoveTypeLayout is that there
565/// can be signature tokens that represent primitives but that do not have
566/// corresponding MoveTypeLayout (e.g., SignatureToken::DatatypeInstantiation).
567pub fn primitive_type(
568    view: &CompiledModule,
569    type_args: &[TypeTag],
570    param: &SignatureToken,
571) -> Option<MoveTypeLayout> {
572    Some(match param {
573        SignatureToken::Bool => MoveTypeLayout::Bool,
574        SignatureToken::U8 => MoveTypeLayout::U8,
575        SignatureToken::U16 => MoveTypeLayout::U16,
576        SignatureToken::U32 => MoveTypeLayout::U32,
577        SignatureToken::U64 => MoveTypeLayout::U64,
578        SignatureToken::U128 => MoveTypeLayout::U128,
579        SignatureToken::U256 => MoveTypeLayout::U256,
580        SignatureToken::Address => MoveTypeLayout::Address,
581        SignatureToken::Vector(inner) => {
582            MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?))
583        }
584        SignatureToken::Datatype(struct_handle_idx) => {
585            let resolved_struct = resolve_struct(view, *struct_handle_idx);
586            if resolved_struct == RESOLVED_ASCII_STR {
587                MoveTypeLayout::Struct(Box::new(move_ascii_str_layout()))
588            } else if resolved_struct == RESOLVED_UTF8_STR {
589                // both structs structs representing strings have one field - a vector of type
590                // u8
591                MoveTypeLayout::Struct(Box::new(move_utf8_str_layout()))
592            } else if resolved_struct == RESOLVED_IOTA_ID {
593                MoveTypeLayout::Struct(Box::new(id::ID::layout()))
594            } else {
595                return None;
596            }
597        }
598        SignatureToken::DatatypeInstantiation(struct_inst) => {
599            let (idx, targs) = &**struct_inst;
600            let resolved_struct = resolve_struct(view, *idx);
601            // is option of a primitive
602            if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
603                // there is no MoveLayout for this so the type is not a primitive.
604                MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?))
605            } else {
606                return None;
607            }
608        }
609        SignatureToken::TypeParameter(idx) => {
610            layout_of_primitive_typetag(type_args.get(*idx as usize)?)?
611        }
612        SignatureToken::Signer
613        | SignatureToken::Reference(_)
614        | SignatureToken::MutableReference(_) => return None,
615    })
616}
617
618fn layout_of_primitive_typetag(tag: &TypeTag) -> Option<MoveTypeLayout> {
619    use MoveTypeLayout as MTL;
620    if !is_primitive_type_tag(tag) {
621        return None;
622    }
623
624    Some(match tag {
625        TypeTag::Bool => MTL::Bool,
626        TypeTag::U8 => MTL::U8,
627        TypeTag::U16 => MTL::U16,
628        TypeTag::U32 => MTL::U32,
629        TypeTag::U64 => MTL::U64,
630        TypeTag::U128 => MTL::U128,
631        TypeTag::U256 => MTL::U256,
632        TypeTag::Address => MTL::Address,
633        TypeTag::Signer => return None,
634        TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)),
635        TypeTag::Struct(stag) => {
636            let resolved_struct = (
637                &AccountAddress::new(stag.address().into_bytes()),
638                move_core_types::identifier::IdentStr::new(stag.module().as_str()).unwrap(),
639                move_core_types::identifier::IdentStr::new(stag.name().as_str()).unwrap(),
640            );
641            // is id or..
642            if resolved_struct == RESOLVED_IOTA_ID {
643                MTL::Struct(Box::new(id::ID::layout()))
644            } else if resolved_struct == RESOLVED_ASCII_STR {
645                MTL::Struct(Box::new(move_ascii_str_layout()))
646            } else if resolved_struct == RESOLVED_UTF8_STR {
647                MTL::Struct(Box::new(move_utf8_str_layout()))
648            } else if resolved_struct == RESOLVED_STD_OPTION // is option of a primitive
649                && stag.type_params().len() == 1
650                && is_primitive_type_tag(&stag.type_params()[0])
651            {
652                MTL::Vector(Box::new(
653                    layout_of_primitive_typetag(&stag.type_params()[0]).unwrap(),
654                ))
655            } else {
656                return None;
657            }
658        }
659    })
660}
661
662fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectID, anyhow::Error> {
663    // Every elem has to be a string convertible to a ObjectID
664    match arg {
665        JsonValue::String(s) => {
666            let s = s.trim().to_lowercase();
667            Ok(ObjectID::from_prefixed_short_hex(&s)?)
668        }
669        _ => bail!(
670            "Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?}-byte hex string \
671                prefixed with 0x.",
672            arg,
673            idx,
674            ObjectID::LENGTH,
675        ),
676    }
677}
678
679fn resolve_object_vec_arg(idx: usize, arg: &IotaJsonValue) -> Result<Vec<ObjectID>, anyhow::Error> {
680    // Every elem has to be a string convertible to a ObjectID
681    match arg.to_json_value() {
682        JsonValue::Array(a) => {
683            let mut object_ids = vec![];
684            for id in a {
685                object_ids.push(resolve_object_arg(idx, &id)?);
686            }
687            Ok(object_ids)
688        }
689        JsonValue::String(s) if s.starts_with('[') && s.ends_with(']') => {
690            // Due to how escaping of square bracket works, we may be dealing with a JSON
691            // string representing a JSON array rather than with the array
692            // itself ("[0x42,0x7]" rather than [0x42,0x7]).
693            let mut object_ids = vec![];
694            for tok in s[1..s.len() - 1].split(',') {
695                let id = JsonValue::String(tok.to_string());
696                object_ids.push(resolve_object_arg(idx, &id)?);
697            }
698            Ok(object_ids)
699        }
700        _ => bail!(
701            "Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
702             Expected a vector of {:?}-byte hex strings prefixed with 0x.\n\
703             Consider escaping your curly braces with a backslash (as in \\[0x42,0x7\\]) \
704             or enclosing the whole vector in single quotes (as in '[0x42,0x7]')",
705            arg.to_json_value(),
706            idx,
707            ObjectID::LENGTH,
708        ),
709    }
710}
711
712fn resolve_call_arg(
713    view: &CompiledModule,
714    type_args: &[TypeTag],
715    idx: usize,
716    arg: &IotaJsonValue,
717    param: &SignatureToken,
718) -> Result<ResolvedCallArg, anyhow::Error> {
719    if let Some(layout) = primitive_type(view, type_args, param) {
720        return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err(
721            |e| {
722                anyhow!(
723                    "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}",
724                    param,
725                    idx,
726                    layout,
727                    e
728                )
729            },
730        )?));
731    }
732
733    // in terms of non-primitives we only currently support objects and "flat"
734    // (depth == 1) vectors of objects (but not, for example, vectors of
735    // references)
736    match param {
737        SignatureToken::Reference(inner) | SignatureToken::MutableReference(inner) => {
738            resolve_call_arg(view, type_args, idx, arg, inner)
739        }
740        SignatureToken::Vector(inner) => match &**inner {
741            SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
742                Ok(ResolvedCallArg::ObjVec(resolve_object_vec_arg(idx, arg)?))
743            }
744            _ => {
745                bail!(
746                    "Unexpected non-primitive vector arg {:?} at {} with value {:?}",
747                    param,
748                    idx,
749                    arg
750                );
751            }
752        },
753        SignatureToken::Datatype(_)
754        | SignatureToken::DatatypeInstantiation(_)
755        | SignatureToken::TypeParameter(_) => Ok(ResolvedCallArg::Object(resolve_object_arg(
756            idx,
757            &arg.to_json_value(),
758        )?)),
759        _ => bail!(
760            "Unexpected non-primitive arg {:?} at {} with value {:?}",
761            param,
762            idx,
763            arg
764        ),
765    }
766}
767
768pub fn is_receiving_argument(view: &CompiledModule, arg_type: &SignatureToken) -> bool {
769    use SignatureToken as ST;
770
771    // Progress down into references to determine if the underlying type is a
772    // receiving type or not.
773    let mut token = arg_type;
774    while let ST::Reference(inner) | ST::MutableReference(inner) = token {
775        token = inner;
776    }
777
778    matches!(
779        token,
780        ST::DatatypeInstantiation(inst) if resolve_struct(view, inst.0) == RESOLVED_RECEIVING_STRUCT && inst.1.len() == 1
781    )
782}
783
784pub fn resolve_call_args(
785    view: &CompiledModule,
786    type_args: &[TypeTag],
787    json_args: &[IotaJsonValue],
788    parameter_types: &[SignatureToken],
789) -> Result<Vec<ResolvedCallArg>, anyhow::Error> {
790    json_args
791        .iter()
792        .zip(parameter_types)
793        .enumerate()
794        .map(|(idx, (arg, param))| resolve_call_arg(view, type_args, idx, arg, param))
795        .collect()
796}
797
798/// Resolve the JSON args of a function into the expected formats to make them
799/// usable by Move call This is because we have special types which we need to
800/// specify in other formats
801pub fn resolve_move_function_args(
802    package: &MovePackage,
803    module_ident: Identifier,
804    function: Identifier,
805    type_args: &[TypeTag],
806    combined_args_json: Vec<IotaJsonValue>,
807) -> Result<Vec<(ResolvedCallArg, SignatureToken)>, anyhow::Error> {
808    // Extract the expected function signature
809    let module = package.deserialize_module(&module_ident, &BinaryConfig::standard())?;
810    let fdef = module
811        .function_defs
812        .iter()
813        .find(|fdef| {
814            module
815                .identifier_at(module.function_handle_at(fdef.function).name)
816                .as_str()
817                == function.as_str()
818        })
819        .ok_or_else(|| {
820            anyhow!(
821                "Could not resolve function {} in module {}",
822                function,
823                module_ident
824            )
825        })?;
826    let function_signature = module.function_handle_at(fdef.function);
827    let parameters = &module.signature_at(function_signature.parameters).0;
828
829    // Lengths have to match, less one, due to TxContext
830    let expected_len = match parameters.last() {
831        Some(param) if TxContext::kind(&module, param) != TxContextKind::None => {
832            parameters.len() - 1
833        }
834        _ => parameters.len(),
835    };
836    if combined_args_json.len() != expected_len {
837        bail!(
838            "Expected {} args, found {}",
839            expected_len,
840            combined_args_json.len()
841        );
842    }
843
844    // Check that the args are valid and convert to the correct format
845    let call_args = resolve_call_args(&module, type_args, &combined_args_json, parameters)?;
846    let tupled_call_args = call_args
847        .into_iter()
848        .zip(parameters.iter())
849        .map(|(arg, expected_type)| (arg, expected_type.clone()))
850        .collect::<Vec<_>>();
851    Ok(tupled_call_args)
852}
853
854fn convert_string_to_u256(s: &str) -> Result<U256, anyhow::Error> {
855    // Try as normal number
856    if let Ok(v) = s.parse::<U256>() {
857        return Ok(v);
858    }
859
860    // Check prefix
861    // For now only Hex supported
862    // TODO: add support for bin and octal?
863
864    let s = s.trim().to_lowercase();
865    if !s.starts_with(HEX_PREFIX) {
866        bail!("Unable to convert {s} to unsigned int.",);
867    }
868    U256::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
869}
870
871#[macro_export]
872macro_rules! call_args {
873        ($($value:expr),*) => {
874        Ok::<_, anyhow::Error>(vec![$(iota_json::call_arg!($value)?,)*])
875    };
876    }
877
878#[macro_export]
879macro_rules! call_arg {
880    ($value:expr) => {{
881        use iota_json::IotaJsonValue;
882        trait IotaJsonArg {
883            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue>;
884        }
885        // TODO: anyway to condense this?
886        impl IotaJsonArg for &str {
887            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
888                IotaJsonValue::from_str(self)
889            }
890        }
891        impl IotaJsonArg for String {
892            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
893                IotaJsonValue::from_str(&self)
894            }
895        }
896        impl IotaJsonArg for iota_types::base_types::ObjectID {
897            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
898                IotaJsonValue::from_str(&self.to_string())
899            }
900        }
901        impl IotaJsonArg for iota_types::base_types::IotaAddress {
902            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
903                IotaJsonValue::from_str(&self.to_string())
904            }
905        }
906        impl IotaJsonArg for u64 {
907            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
908                IotaJsonValue::from_bcs_bytes(
909                    Some(&iota_json::MoveTypeLayout::U64),
910                    &bcs::to_bytes(self)?,
911                )
912            }
913        }
914        impl IotaJsonArg for Vec<u8> {
915            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
916                IotaJsonValue::from_bcs_bytes(None, &self)
917            }
918        }
919        impl IotaJsonArg for &[u8] {
920            fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
921                IotaJsonValue::from_bcs_bytes(None, self)
922            }
923        }
924        $value.to_iota_json()
925    }};
926}
927
928#[macro_export]
929macro_rules! type_args {
930    ($($value:expr), *) => {{
931        use iota_json_rpc_types::IotaTypeTag;
932        use iota_types::base_types::TypeTag;
933        trait IotaJsonTypeArg {
934            fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag>;
935        }
936        impl <T: core::fmt::Display> IotaJsonTypeArg for T {
937            fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag> {
938                Ok(iota_types::parse_iota_type_tag(&self.to_string())?.into())
939            }
940        }
941        Ok::<_, anyhow::Error>(vec![$($value.to_iota_json()?,)*])
942    }};
943    }