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 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#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
47pub enum IotaJsonValueErrorKind {
48 ValueTypeNotAllowed,
50
51 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#[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 JsonValue::Bool(_) | JsonValue::String(_) => (),
106 JsonValue::Number(n) => {
107 if !n.is_u64() {
109 bail!("{n} not allowed. Number must be unsigned integer of at most u32");
110 }
111 }
112 JsonValue::Array(a) => {
114 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 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 (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_tag_core_to_sdk(&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_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 (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(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 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 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 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 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 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 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 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 Any,
500}
501
502pub 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
511fn check_valid_homogeneous_rec(
515 curr_q: &mut VecDeque<&JsonValue>,
516) -> Result<(), IotaJsonValueError> {
517 if curr_q.is_empty() {
518 return Ok(());
520 }
521 let mut next_q = VecDeque::new();
523 let mut level_type = ValidJsonType::Any;
525
526 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 w.iter().for_each(|t| next_q.push_back(t));
535 ValidJsonType::Array
536 }
537 _ => {
539 return Err(IotaJsonValueError::new(
540 v,
541 IotaJsonValueErrorKind::ValueTypeNotAllowed,
542 ));
543 }
544 };
545
546 if level_type == ValidJsonType::Any {
547 level_type = curr;
549 } else if level_type != curr {
550 return Err(IotaJsonValueError::new(
552 v,
553 IotaJsonValueErrorKind::ArrayNotHomogeneous,
554 ));
555 }
556 }
557 check_valid_homogeneous_rec(&mut next_q)
559}
560
561pub 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 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 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
603 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 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 && 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 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 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 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 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 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
798pub 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 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 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 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 if let Ok(v) = s.parse::<U256>() {
857 return Ok(v);
858 }
859
860 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 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 }