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 return Err(anyhow!(
112 "{n} not allowed. Number must be unsigned integer of at most u32"
113 ));
114 }
115 }
116 JsonValue::Array(a) => {
118 check_valid_homogeneous(&JsonValue::Array(a.to_vec()))?
120 }
121 JsonValue::Object(v) => {
122 for (_, value) in v {
123 Self::check_value(value)?;
124 }
125 }
126 JsonValue::Null => bail!("Null not allowed."),
127 };
128 Ok(())
129 }
130
131 pub fn from_object_id(id: ObjectID) -> IotaJsonValue {
132 Self(JsonValue::String(id.to_hex_uncompressed()))
133 }
134
135 pub fn to_bcs_bytes(&self, ty: &MoveTypeLayout) -> Result<Vec<u8>, anyhow::Error> {
136 let move_value = Self::to_move_value(&self.0, ty)?;
137 R::MoveValue::simple_serialize(&move_value)
138 .ok_or_else(|| anyhow!("Unable to serialize {:?}. Expected {}", move_value, ty))
139 }
140
141 pub fn from_bcs_bytes(
142 layout: Option<&MoveTypeLayout>,
143 bytes: &[u8],
144 ) -> Result<Self, anyhow::Error> {
145 let json = if let Some(layout) = layout {
146 fn try_parse_string(layout: &MoveTypeLayout, bytes: &[u8]) -> Option<String> {
148 if let MoveTypeLayout::Vector(t) = layout {
149 if let MoveTypeLayout::U8 = **t {
150 return bcs::from_bytes::<String>(bytes).ok();
151 }
152 }
153 None
154 }
155 if let Some(s) = try_parse_string(layout, bytes) {
156 json!(s)
157 } else {
158 let result = BoundedVisitor::deserialize_value(bytes, layout).map_or_else(
159 |_| {
160 JsonValue::Array(
162 bytes
163 .iter()
164 .map(|b| JsonValue::Number(Number::from(*b)))
165 .collect(),
166 )
167 },
168 |move_value| {
169 move_value_to_json(&move_value).unwrap_or_else(|| {
170 JsonValue::Array(
172 bytes
173 .iter()
174 .map(|b| JsonValue::Number(Number::from(*b)))
175 .collect(),
176 )
177 })
178 },
179 );
180 result
181 }
182 } else {
183 json!(bytes)
184 };
185 IotaJsonValue::new(json)
186 }
187
188 pub fn to_json_value(&self) -> JsonValue {
189 self.0.clone()
190 }
191
192 pub fn to_iota_address(&self) -> anyhow::Result<IotaAddress> {
193 json_value_to_iota_address(&self.0)
194 }
195
196 fn handle_inner_struct_layout(
197 inner_vec: &[MoveFieldLayout],
198 val: &JsonValue,
199 ty: &MoveTypeLayout,
200 s: &String,
201 ) -> Result<R::MoveValue, anyhow::Error> {
202 debug_assert!(matches!(val, JsonValue::String(_)));
206
207 if inner_vec.len() != 1 {
208 bail!(
209 "Cannot convert string arg {s} to {ty} which is expected \
210 to be a struct with one field"
211 );
212 }
213
214 match &inner_vec[0].layout {
215 MoveTypeLayout::Vector(inner) => match **inner {
216 MoveTypeLayout::U8 => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
217 Self::to_move_value(val, &inner_vec[0].layout.clone())?,
218 ]))),
219 MoveTypeLayout::Address => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
220 Self::to_move_value(val, &MoveTypeLayout::Address)?,
221 ]))),
222 _ => bail!(
223 "Cannot convert string arg {s} to {ty} \
224 which is expected to be a struct \
225 with one field of address or u8 vector type"
226 ),
227 },
228 MoveTypeLayout::Struct(struct_layout) if struct_layout.type_ == ID::type_() => {
229 Ok(R::MoveValue::Struct(R::MoveStruct(vec![
230 Self::to_move_value(val, &inner_vec[0].layout.clone())?,
231 ])))
232 }
233 _ => bail!(
234 "Cannot convert string arg {s} to {ty} which is expected \
235 to be a struct with one field of a vector type"
236 ),
237 }
238 }
239
240 pub fn to_move_value(
241 val: &JsonValue,
242 ty: &MoveTypeLayout,
243 ) -> Result<R::MoveValue, anyhow::Error> {
244 Ok(match (val, ty) {
245 (JsonValue::Bool(b), MoveTypeLayout::Bool) => R::MoveValue::Bool(*b),
247
248 (JsonValue::Number(n), MoveTypeLayout::U8) => match n.as_u64() {
251 Some(x) => R::MoveValue::U8(u8::try_from(x)?),
252 None => return Err(anyhow!("{} is not a valid number. Only u8 allowed.", n)),
253 },
254 (JsonValue::Number(n), MoveTypeLayout::U16) => match n.as_u64() {
255 Some(x) => R::MoveValue::U16(u16::try_from(x)?),
256 None => return Err(anyhow!("{} is not a valid number. Only u16 allowed.", n)),
257 },
258 (JsonValue::Number(n), MoveTypeLayout::U32) => match n.as_u64() {
259 Some(x) => R::MoveValue::U32(u32::try_from(x)?),
260 None => return Err(anyhow!("{} is not a valid number. Only u32 allowed.", n)),
261 },
262
263 (JsonValue::String(s), MoveTypeLayout::U8) => {
265 R::MoveValue::U8(u8::try_from(convert_string_to_u256(s.as_str())?)?)
266 }
267 (JsonValue::String(s), MoveTypeLayout::U16) => {
268 R::MoveValue::U16(u16::try_from(convert_string_to_u256(s.as_str())?)?)
269 }
270 (JsonValue::String(s), MoveTypeLayout::U32) => {
271 R::MoveValue::U32(u32::try_from(convert_string_to_u256(s.as_str())?)?)
272 }
273 (JsonValue::String(s), MoveTypeLayout::U64) => {
274 R::MoveValue::U64(u64::try_from(convert_string_to_u256(s.as_str())?)?)
275 }
276 (JsonValue::String(s), MoveTypeLayout::U128) => {
277 R::MoveValue::U128(u128::try_from(convert_string_to_u256(s.as_str())?)?)
278 }
279 (JsonValue::String(s), MoveTypeLayout::U256) => {
280 R::MoveValue::U256(convert_string_to_u256(s.as_str())?)
281 }
282 (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
284 if is_move_string_type(&struct_layout.type_) =>
285 {
286 R::MoveValue::Vector(s.as_bytes().iter().copied().map(R::MoveValue::U8).collect())
287 }
288 (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
290 if struct_layout.type_ == ID::type_() =>
291 {
292 if struct_layout.fields.len() != 1 {
293 bail!(
294 "Cannot convert string arg {s} to {} which is expected to be a struct with one field",
295 struct_layout.type_
296 );
297 };
298 let addr = IotaAddress::from_str(s)?;
299 R::MoveValue::Address(addr.into())
300 }
301 (JsonValue::Object(o), MoveTypeLayout::Struct(struct_layout)) => {
302 let mut field_values = vec![];
303 for layout in struct_layout.fields.iter() {
304 let field = o
305 .get(layout.name.as_str())
306 .ok_or_else(|| anyhow!("Missing field {} for struct {ty}", layout.name))?;
307 field_values.push(Self::to_move_value(field, &layout.layout)?);
308 }
309 R::MoveValue::Struct(R::MoveStruct(field_values))
310 }
311 (value, MoveTypeLayout::Struct(struct_layout)) if struct_layout.fields.len() == 1 => {
313 Self::to_move_value(value, &struct_layout.fields[0].layout)?
314 }
315 (JsonValue::String(s), MoveTypeLayout::Vector(t)) => {
316 match &**t {
317 MoveTypeLayout::U8 => {
318 let vec = if s.starts_with(HEX_PREFIX) {
328 Hex::decode(s).map_err(|e| anyhow!(e))?
330 } else {
331 s.as_bytes().to_vec()
333 };
334 R::MoveValue::Vector(vec.iter().copied().map(R::MoveValue::U8).collect())
335 }
336 MoveTypeLayout::Struct(struct_layout) => {
337 Self::handle_inner_struct_layout(&struct_layout.fields, val, ty, s)?
338 }
339 _ => bail!("Cannot convert string arg {s} to {ty}"),
340 }
341 }
342
343 (JsonValue::Array(a), MoveTypeLayout::Vector(inner)) => {
345 R::MoveValue::Vector(
347 a.iter()
348 .map(|i| Self::to_move_value(i, inner))
349 .collect::<Result<Vec<_>, _>>()?,
350 )
351 }
352
353 (v, MoveTypeLayout::Address) => {
354 let addr = json_value_to_iota_address(v)?;
355 R::MoveValue::Address(addr.into())
356 }
357
358 _ => bail!("Unexpected arg {val:?} for expected type {ty:?}"),
359 })
360 }
361}
362
363impl Debug for IotaJsonValue {
364 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
365 write!(f, "{}", self.0)
366 }
367}
368
369fn json_value_to_iota_address(value: &JsonValue) -> anyhow::Result<IotaAddress> {
370 match value {
371 JsonValue::String(s) => {
372 let s = s.trim().to_lowercase();
373 if !s.starts_with(HEX_PREFIX) {
374 bail!("Address hex string must start with 0x.",);
375 }
376 Ok(IotaAddress::from_str(&s)?)
377 }
378 JsonValue::Array(bytes) => {
379 fn value_to_byte_array(v: &Vec<JsonValue>) -> Option<Vec<u8>> {
380 let mut bytes = vec![];
381 for b in v {
382 let b = b.as_u64()?;
383 if b <= u8::MAX as u64 {
384 bytes.push(b as u8);
385 } else {
386 return None;
387 }
388 }
389 Some(bytes)
390 }
391 let bytes = value_to_byte_array(bytes)
392 .ok_or_else(|| anyhow!("Invalid input: Cannot parse input into IotaAddress."))?;
393 Ok(IotaAddress::try_from(bytes)?)
394 }
395 v => bail!("Unexpected arg {v} for expected type address"),
396 }
397}
398
399fn move_value_to_json(move_value: &MoveValue) -> Option<JsonValue> {
400 Some(match move_value {
401 MoveValue::Vector(values) => JsonValue::Array(
402 values
403 .iter()
404 .map(move_value_to_json)
405 .collect::<Option<_>>()?,
406 ),
407 MoveValue::Bool(v) => json!(v),
408 MoveValue::Signer(v) | MoveValue::Address(v) => json!(IotaAddress::from(*v).to_string()),
409 MoveValue::U8(v) => json!(v),
410 MoveValue::U64(v) => json!(v.to_string()),
411 MoveValue::U128(v) => json!(v.to_string()),
412 MoveValue::U16(v) => json!(v),
413 MoveValue::U32(v) => json!(v),
414 MoveValue::U256(v) => json!(v.to_string()),
415 MoveValue::Struct(move_struct) => match move_struct {
416 MoveStruct { fields, type_ } if is_move_string_type(type_) => {
417 let (_, v) = fields.first()?;
419 let string: String = bcs::from_bytes(&v.simple_serialize()?).ok()?;
420 json!(string)
421 }
422 MoveStruct { fields, type_ } if is_move_option_type(type_) => {
423 let (_, v) = fields.first()?;
425 if let MoveValue::Vector(v) = v {
426 JsonValue::Array(v.iter().filter_map(move_value_to_json).collect::<Vec<_>>())
427 } else {
428 return None;
429 }
430 }
431 MoveStruct { fields, type_ } if type_ == &ID::type_() => {
432 let (_, v) = fields.first()?;
434 if let MoveValue::Address(address) = v {
435 json!(IotaAddress::from(*address))
436 } else {
437 return None;
438 }
439 }
440 MoveStruct { fields, .. } => {
443 let fields = fields
444 .iter()
445 .map(|(key, value)| (key, move_value_to_json(value)))
446 .collect::<BTreeMap<_, _>>();
447 json!(fields)
448 }
449 },
450 MoveValue::Variant(MoveVariant {
452 type_: _,
453 tag: _,
454 variant_name,
455 fields,
456 }) => {
457 let fields = fields
458 .iter()
459 .map(|(key, value)| (key, move_value_to_json(value)))
460 .collect::<BTreeMap<_, _>>();
461 json!({
462 "variant": variant_name.to_string(),
463 "fields": fields,
464 })
465 }
466 })
467}
468
469fn is_move_string_type(tag: &StructTag) -> bool {
470 (tag.address == MOVE_STDLIB_ADDRESS
471 && tag.module.as_ident_str() == STD_UTF8_MODULE_NAME
472 && tag.name.as_ident_str() == STD_UTF8_STRUCT_NAME)
473 || (tag.address == MOVE_STDLIB_ADDRESS
474 && tag.module.as_ident_str() == STD_ASCII_MODULE_NAME
475 && tag.name.as_ident_str() == STD_ASCII_STRUCT_NAME)
476}
477fn is_move_option_type(tag: &StructTag) -> bool {
478 tag.address == MOVE_STDLIB_ADDRESS
479 && tag.module.as_ident_str() == STD_OPTION_MODULE_NAME
480 && tag.name.as_ident_str() == STD_OPTION_STRUCT_NAME
481}
482
483impl FromStr for IotaJsonValue {
484 type Err = anyhow::Error;
485 fn from_str(s: &str) -> Result<Self, anyhow::Error> {
486 fn try_escape_array(s: &str) -> JsonValue {
487 let s = s.trim();
488 if s.starts_with('[') && s.ends_with(']') {
489 if let Some(s) = s.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
490 return JsonValue::Array(s.split(',').map(try_escape_array).collect());
491 }
492 }
493 json!(s)
494 }
495 IotaJsonValue::new(serde_json::from_str(s).unwrap_or_else(|_| try_escape_array(s)))
498 }
499}
500
501#[derive(Eq, PartialEq, Debug, Clone, Hash)]
502enum ValidJsonType {
503 Bool,
504 Number,
505 String,
506 Array,
507 Any,
509}
510
511pub fn check_valid_homogeneous(val: &JsonValue) -> Result<(), IotaJsonValueError> {
515 let mut deq: VecDeque<&JsonValue> = VecDeque::new();
516 deq.push_back(val);
517 check_valid_homogeneous_rec(&mut deq)
518}
519
520fn check_valid_homogeneous_rec(
524 curr_q: &mut VecDeque<&JsonValue>,
525) -> Result<(), IotaJsonValueError> {
526 if curr_q.is_empty() {
527 return Ok(());
529 }
530 let mut next_q = VecDeque::new();
532 let mut level_type = ValidJsonType::Any;
534
535 while let Some(v) = curr_q.pop_front() {
537 let curr = match v {
538 JsonValue::Bool(_) => ValidJsonType::Bool,
539 JsonValue::Number(x) if x.is_u64() => ValidJsonType::Number,
540 JsonValue::String(_) => ValidJsonType::String,
541 JsonValue::Array(w) => {
542 w.iter().for_each(|t| next_q.push_back(t));
544 ValidJsonType::Array
545 }
546 _ => {
548 return Err(IotaJsonValueError::new(
549 v,
550 IotaJsonValueErrorKind::ValueTypeNotAllowed,
551 ));
552 }
553 };
554
555 if level_type == ValidJsonType::Any {
556 level_type = curr;
558 } else if level_type != curr {
559 return Err(IotaJsonValueError::new(
561 v,
562 IotaJsonValueErrorKind::ArrayNotHomogeneous,
563 ));
564 }
565 }
566 check_valid_homogeneous_rec(&mut next_q)
568}
569
570pub fn primitive_type(
577 view: &CompiledModule,
578 type_args: &[TypeTag],
579 param: &SignatureToken,
580) -> Option<MoveTypeLayout> {
581 Some(match param {
582 SignatureToken::Bool => MoveTypeLayout::Bool,
583 SignatureToken::U8 => MoveTypeLayout::U8,
584 SignatureToken::U16 => MoveTypeLayout::U16,
585 SignatureToken::U32 => MoveTypeLayout::U32,
586 SignatureToken::U64 => MoveTypeLayout::U64,
587 SignatureToken::U128 => MoveTypeLayout::U128,
588 SignatureToken::U256 => MoveTypeLayout::U256,
589 SignatureToken::Address => MoveTypeLayout::Address,
590 SignatureToken::Vector(inner) => {
591 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?))
592 }
593 SignatureToken::Datatype(struct_handle_idx) => {
594 let resolved_struct = resolve_struct(view, *struct_handle_idx);
595 if resolved_struct == RESOLVED_ASCII_STR {
596 MoveTypeLayout::Struct(Box::new(move_ascii_str_layout()))
597 } else if resolved_struct == RESOLVED_UTF8_STR {
598 MoveTypeLayout::Struct(Box::new(move_utf8_str_layout()))
601 } else if resolved_struct == RESOLVED_IOTA_ID {
602 MoveTypeLayout::Struct(Box::new(id::ID::layout()))
603 } else {
604 return None;
605 }
606 }
607 SignatureToken::DatatypeInstantiation(struct_inst) => {
608 let (idx, targs) = &**struct_inst;
609 let resolved_struct = resolve_struct(view, *idx);
610 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
612 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?))
614 } else {
615 return None;
616 }
617 }
618 SignatureToken::TypeParameter(idx) => {
619 layout_of_primitive_typetag(type_args.get(*idx as usize)?)?
620 }
621 SignatureToken::Signer
622 | SignatureToken::Reference(_)
623 | SignatureToken::MutableReference(_) => return None,
624 })
625}
626
627fn layout_of_primitive_typetag(tag: &TypeTag) -> Option<MoveTypeLayout> {
628 use MoveTypeLayout as MTL;
629 if !is_primitive_type_tag(tag) {
630 return None;
631 }
632
633 Some(match tag {
634 TypeTag::Bool => MTL::Bool,
635 TypeTag::U8 => MTL::U8,
636 TypeTag::U16 => MTL::U16,
637 TypeTag::U32 => MTL::U32,
638 TypeTag::U64 => MTL::U64,
639 TypeTag::U128 => MTL::U128,
640 TypeTag::U256 => MTL::U256,
641 TypeTag::Address => MTL::Address,
642 TypeTag::Signer => return None,
643 TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)),
644 TypeTag::Struct(stag) => {
645 let StructTag {
646 address,
647 module,
648 name,
649 type_params: type_args,
650 } = &**stag;
651 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
652 if resolved_struct == RESOLVED_IOTA_ID {
654 MTL::Struct(Box::new(id::ID::layout()))
655 } else if resolved_struct == RESOLVED_ASCII_STR {
656 MTL::Struct(Box::new(move_ascii_str_layout()))
657 } else if resolved_struct == RESOLVED_UTF8_STR {
658 MTL::Struct(Box::new(move_utf8_str_layout()))
659 } else if resolved_struct == RESOLVED_STD_OPTION && type_args.len() == 1
661 && is_primitive_type_tag(&type_args[0])
662 {
663 MTL::Vector(Box::new(
664 layout_of_primitive_typetag(&type_args[0]).unwrap(),
665 ))
666 } else {
667 return None;
668 }
669 }
670 })
671}
672
673fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectID, anyhow::Error> {
674 match arg {
676 JsonValue::String(s) => {
677 let s = s.trim().to_lowercase();
678 if !s.starts_with(HEX_PREFIX) {
679 bail!("ObjectID hex string must start with 0x.",);
680 }
681 Ok(ObjectID::from_hex_literal(&s)?)
682 }
683 _ => bail!(
684 "Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?}-byte hex string \
685 prefixed with 0x.",
686 arg,
687 idx,
688 ObjectID::LENGTH,
689 ),
690 }
691}
692
693fn resolve_object_vec_arg(idx: usize, arg: &IotaJsonValue) -> Result<Vec<ObjectID>, anyhow::Error> {
694 match arg.to_json_value() {
696 JsonValue::Array(a) => {
697 let mut object_ids = vec![];
698 for id in a {
699 object_ids.push(resolve_object_arg(idx, &id)?);
700 }
701 Ok(object_ids)
702 }
703 JsonValue::String(s) if s.starts_with('[') && s.ends_with(']') => {
704 let mut object_ids = vec![];
708 for tok in s[1..s.len() - 1].split(',') {
709 let id = JsonValue::String(tok.to_string());
710 object_ids.push(resolve_object_arg(idx, &id)?);
711 }
712 Ok(object_ids)
713 }
714 _ => bail!(
715 "Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
716 Expected a vector of {:?}-byte hex strings prefixed with 0x.\n\
717 Consider escaping your curly braces with a backslash (as in \\[0x42,0x7\\]) \
718 or enclosing the whole vector in single quotes (as in '[0x42,0x7]')",
719 arg.to_json_value(),
720 idx,
721 ObjectID::LENGTH,
722 ),
723 }
724}
725
726fn resolve_call_arg(
727 view: &CompiledModule,
728 type_args: &[TypeTag],
729 idx: usize,
730 arg: &IotaJsonValue,
731 param: &SignatureToken,
732) -> Result<ResolvedCallArg, anyhow::Error> {
733 if let Some(layout) = primitive_type(view, type_args, param) {
734 return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err(
735 |e| {
736 anyhow!(
737 "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}",
738 param,
739 idx,
740 layout,
741 e
742 )
743 },
744 )?));
745 }
746
747 match param {
751 SignatureToken::Datatype(_)
752 | SignatureToken::DatatypeInstantiation(_)
753 | SignatureToken::TypeParameter(_)
754 | SignatureToken::Reference(_)
755 | SignatureToken::MutableReference(_) => Ok(ResolvedCallArg::Object(resolve_object_arg(
756 idx,
757 &arg.to_json_value(),
758 )?)),
759 SignatureToken::Vector(inner) => match &**inner {
760 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
761 Ok(ResolvedCallArg::ObjVec(resolve_object_vec_arg(idx, arg)?))
762 }
763 _ => {
764 bail!(
765 "Unexpected non-primitive vector arg {:?} at {} with value {:?}",
766 param,
767 idx,
768 arg
769 );
770 }
771 },
772 _ => bail!(
773 "Unexpected non-primitive arg {:?} at {} with value {:?}",
774 param,
775 idx,
776 arg
777 ),
778 }
779}
780
781pub fn is_receiving_argument(view: &CompiledModule, arg_type: &SignatureToken) -> bool {
782 use SignatureToken as ST;
783
784 let mut token = arg_type;
787 while let ST::Reference(inner) | ST::MutableReference(inner) = token {
788 token = inner;
789 }
790
791 matches!(
792 token,
793 ST::DatatypeInstantiation(inst) if resolve_struct(view, inst.0) == RESOLVED_RECEIVING_STRUCT && inst.1.len() == 1
794 )
795}
796
797fn resolve_call_args(
798 view: &CompiledModule,
799 type_args: &[TypeTag],
800 json_args: &[IotaJsonValue],
801 parameter_types: &[SignatureToken],
802) -> Result<Vec<ResolvedCallArg>, anyhow::Error> {
803 json_args
804 .iter()
805 .zip(parameter_types)
806 .enumerate()
807 .map(|(idx, (arg, param))| resolve_call_arg(view, type_args, idx, arg, param))
808 .collect()
809}
810
811pub fn resolve_move_function_args(
815 package: &MovePackage,
816 module_ident: Identifier,
817 function: Identifier,
818 type_args: &[TypeTag],
819 combined_args_json: Vec<IotaJsonValue>,
820) -> Result<Vec<(ResolvedCallArg, SignatureToken)>, anyhow::Error> {
821 let module = package.deserialize_module(&module_ident, &BinaryConfig::standard())?;
823 let function_str = function.as_ident_str();
824 let fdef = module
825 .function_defs
826 .iter()
827 .find(|fdef| {
828 module.identifier_at(module.function_handle_at(fdef.function).name) == function_str
829 })
830 .ok_or_else(|| {
831 anyhow!(
832 "Could not resolve function {} in module {}",
833 function,
834 module_ident
835 )
836 })?;
837 let function_signature = module.function_handle_at(fdef.function);
838 let parameters = &module.signature_at(function_signature.parameters).0;
839
840 let expected_len = match parameters.last() {
842 Some(param) if TxContext::kind(&module, param) != TxContextKind::None => {
843 parameters.len() - 1
844 }
845 _ => parameters.len(),
846 };
847 if combined_args_json.len() != expected_len {
848 bail!(
849 "Expected {} args, found {}",
850 expected_len,
851 combined_args_json.len()
852 );
853 }
854 let call_args = resolve_call_args(&module, type_args, &combined_args_json, parameters)?;
856 let tupled_call_args = call_args
857 .into_iter()
858 .zip(parameters.iter())
859 .map(|(arg, expected_type)| (arg, expected_type.clone()))
860 .collect::<Vec<_>>();
861 Ok(tupled_call_args)
862}
863
864fn convert_string_to_u256(s: &str) -> Result<U256, anyhow::Error> {
865 if let Ok(v) = s.parse::<U256>() {
867 return Ok(v);
868 }
869
870 let s = s.trim().to_lowercase();
875 if !s.starts_with(HEX_PREFIX) {
876 bail!("Unable to convert {s} to unsigned int.",);
877 }
878 U256::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
879}
880
881#[macro_export]
882macro_rules! call_args {
883 ($($value:expr),*) => {
884 Ok::<_, anyhow::Error>(vec![$(iota_json::call_arg!($value)?,)*])
885 };
886 }
887
888#[macro_export]
889macro_rules! call_arg {
890 ($value:expr) => {{
891 use iota_json::IotaJsonValue;
892 trait IotaJsonArg {
893 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue>;
894 }
895 impl IotaJsonArg for &str {
897 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
898 IotaJsonValue::from_str(self)
899 }
900 }
901 impl IotaJsonArg for String {
902 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
903 IotaJsonValue::from_str(&self)
904 }
905 }
906 impl IotaJsonArg for iota_types::base_types::ObjectID {
907 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
908 IotaJsonValue::from_str(&self.to_string())
909 }
910 }
911 impl IotaJsonArg for iota_types::base_types::IotaAddress {
912 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
913 IotaJsonValue::from_str(&self.to_string())
914 }
915 }
916 impl IotaJsonArg for u64 {
917 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
918 IotaJsonValue::from_bcs_bytes(
919 Some(&iota_json::MoveTypeLayout::U64),
920 &bcs::to_bytes(self)?,
921 )
922 }
923 }
924 impl IotaJsonArg for Vec<u8> {
925 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
926 IotaJsonValue::from_bcs_bytes(None, &self)
927 }
928 }
929 impl IotaJsonArg for &[u8] {
930 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
931 IotaJsonValue::from_bcs_bytes(None, self)
932 }
933 }
934 $value.to_iota_json()
935 }};
936}
937
938#[macro_export]
939macro_rules! type_args {
940 ($($value:expr), *) => {{
941 use iota_json_rpc_types::IotaTypeTag;
942 use iota_types::TypeTag;
943 trait IotaJsonTypeArg {
944 fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag>;
945 }
946 impl <T: core::fmt::Display> IotaJsonTypeArg for T {
947 fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag> {
948 Ok(iota_types::parse_iota_type_tag(&self.to_string())?.into())
949 }
950 }
951 Ok::<_, anyhow::Error>(vec![$($value.to_iota_json()?,)*])
952 }};
953 }