1use 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#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
49pub enum IotaJsonValueErrorKind {
50 ValueTypeNotAllowed,
52
53 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#[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 JsonValue::Bool(_) | JsonValue::String(_) => (),
108 JsonValue::Number(n) => {
109 if !n.is_u64() {
111 bail!("{n} not allowed. Number must be unsigned integer of at most u32");
112 }
113 }
114 JsonValue::Array(a) => {
116 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 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 (JsonValue::Bool(b), MoveTypeLayout::Bool) => R::MoveValue::Bool(*b),
219
220 (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 (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 (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 (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 (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 let vec = if s.starts_with(HEX_PREFIX) {
300 Hex::decode(s).map_err(|e| anyhow!(e))?
302 } else {
303 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 (JsonValue::Array(a), MoveTypeLayout::Vector(inner)) => {
317 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 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 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 let (_, v) = fields.first()?;
406 if let MoveValue::Address(address) = v {
407 json!(IotaAddress::from(*address))
408 } else {
409 return None;
410 }
411 }
412 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 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 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 Any,
481}
482
483pub 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
492fn check_valid_homogeneous_rec(
496 curr_q: &mut VecDeque<&JsonValue>,
497) -> Result<(), IotaJsonValueError> {
498 if curr_q.is_empty() {
499 return Ok(());
501 }
502 let mut next_q = VecDeque::new();
504 let mut level_type = ValidJsonType::Any;
506
507 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 w.iter().for_each(|t| next_q.push_back(t));
516 ValidJsonType::Array
517 }
518 _ => {
520 return Err(IotaJsonValueError::new(
521 v,
522 IotaJsonValueErrorKind::ValueTypeNotAllowed,
523 ));
524 }
525 };
526
527 if level_type == ValidJsonType::Any {
528 level_type = curr;
530 } else if level_type != curr {
531 return Err(IotaJsonValueError::new(
533 v,
534 IotaJsonValueErrorKind::ArrayNotHomogeneous,
535 ));
536 }
537 }
538 check_valid_homogeneous_rec(&mut next_q)
540}
541
542pub 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 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 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
584 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 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 && 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 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 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 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 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 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
783pub 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 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 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 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 if let Ok(v) = s.parse::<U256>() {
839 return Ok(v);
840 }
841
842 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 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 }