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