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 split_top_level_commas(s: &str) -> Vec<&str> {
462 let mut parts = Vec::new();
463 let mut depth = 0usize;
464 let mut start = 0;
465 for (i, ch) in s.char_indices() {
466 match ch {
467 '[' => depth += 1,
468 ']' => depth = depth.saturating_sub(1),
469 ',' if depth == 0 => {
470 parts.push(&s[start..i]);
471 start = i + 1;
472 }
473 _ => {}
474 }
475 }
476 parts.push(&s[start..]);
477 parts
478 }
479
480 fn try_escape_array(s: &str) -> JsonValue {
481 let s = s.trim();
482 if s.starts_with('[') && s.ends_with(']') {
483 if let Some(inner) = s.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
484 return JsonValue::Array(
485 split_top_level_commas(inner)
486 .into_iter()
487 .map(try_escape_array)
488 .collect(),
489 );
490 }
491 }
492 json!(s)
493 }
494 IotaJsonValue::new(serde_json::from_str(s).unwrap_or_else(|_| try_escape_array(s)))
497 }
498}
499
500#[derive(Eq, PartialEq, Debug, Clone, Hash)]
501enum ValidJsonType {
502 Bool,
503 Number,
504 String,
505 Array,
506 Any,
508}
509
510pub fn check_valid_homogeneous(val: &JsonValue) -> Result<(), IotaJsonValueError> {
514 let mut deq: VecDeque<&JsonValue> = VecDeque::new();
515 deq.push_back(val);
516 check_valid_homogeneous_rec(&mut deq)
517}
518
519fn check_valid_homogeneous_rec(
523 curr_q: &mut VecDeque<&JsonValue>,
524) -> Result<(), IotaJsonValueError> {
525 if curr_q.is_empty() {
526 return Ok(());
528 }
529 let mut next_q = VecDeque::new();
531 let mut level_type = ValidJsonType::Any;
533
534 while let Some(v) = curr_q.pop_front() {
536 let curr = match v {
537 JsonValue::Bool(_) => ValidJsonType::Bool,
538 JsonValue::Number(x) if x.is_u64() => ValidJsonType::Number,
539 JsonValue::String(_) => ValidJsonType::String,
540 JsonValue::Array(w) => {
541 w.iter().for_each(|t| next_q.push_back(t));
543 ValidJsonType::Array
544 }
545 _ => {
547 return Err(IotaJsonValueError::new(
548 v,
549 IotaJsonValueErrorKind::ValueTypeNotAllowed,
550 ));
551 }
552 };
553
554 if level_type == ValidJsonType::Any {
555 level_type = curr;
557 } else if level_type != curr {
558 return Err(IotaJsonValueError::new(
560 v,
561 IotaJsonValueErrorKind::ArrayNotHomogeneous,
562 ));
563 }
564 }
565 check_valid_homogeneous_rec(&mut next_q)
567}
568
569pub fn primitive_type(
576 view: &CompiledModule,
577 type_args: &[TypeTag],
578 param: &SignatureToken,
579) -> Option<MoveTypeLayout> {
580 Some(match param {
581 SignatureToken::Bool => MoveTypeLayout::Bool,
582 SignatureToken::U8 => MoveTypeLayout::U8,
583 SignatureToken::U16 => MoveTypeLayout::U16,
584 SignatureToken::U32 => MoveTypeLayout::U32,
585 SignatureToken::U64 => MoveTypeLayout::U64,
586 SignatureToken::U128 => MoveTypeLayout::U128,
587 SignatureToken::U256 => MoveTypeLayout::U256,
588 SignatureToken::Address => MoveTypeLayout::Address,
589 SignatureToken::Vector(inner) => {
590 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?))
591 }
592 SignatureToken::Datatype(struct_handle_idx) => {
593 let resolved_struct = resolve_struct(view, *struct_handle_idx);
594 if resolved_struct == RESOLVED_ASCII_STR {
595 MoveTypeLayout::Struct(Box::new(move_ascii_str_layout()))
596 } else if resolved_struct == RESOLVED_UTF8_STR {
597 MoveTypeLayout::Struct(Box::new(move_utf8_str_layout()))
600 } else if resolved_struct == RESOLVED_IOTA_ID {
601 MoveTypeLayout::Struct(Box::new(id::ID::layout()))
602 } else {
603 return None;
604 }
605 }
606 SignatureToken::DatatypeInstantiation(struct_inst) => {
607 let (idx, targs) = &**struct_inst;
608 let resolved_struct = resolve_struct(view, *idx);
609 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
611 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?))
613 } else {
614 return None;
615 }
616 }
617 SignatureToken::TypeParameter(idx) => {
618 layout_of_primitive_typetag(type_args.get(*idx as usize)?)?
619 }
620 SignatureToken::Signer
621 | SignatureToken::Reference(_)
622 | SignatureToken::MutableReference(_) => return None,
623 })
624}
625
626fn layout_of_primitive_typetag(tag: &TypeTag) -> Option<MoveTypeLayout> {
627 use MoveTypeLayout as MTL;
628 if !is_primitive_type_tag(tag) {
629 return None;
630 }
631
632 Some(match tag {
633 TypeTag::Bool => MTL::Bool,
634 TypeTag::U8 => MTL::U8,
635 TypeTag::U16 => MTL::U16,
636 TypeTag::U32 => MTL::U32,
637 TypeTag::U64 => MTL::U64,
638 TypeTag::U128 => MTL::U128,
639 TypeTag::U256 => MTL::U256,
640 TypeTag::Address => MTL::Address,
641 TypeTag::Signer => return None,
642 TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)),
643 TypeTag::Struct(stag) => {
644 let StructTag {
645 address,
646 module,
647 name,
648 type_params: type_args,
649 } = &**stag;
650 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
651 if resolved_struct == RESOLVED_IOTA_ID {
653 MTL::Struct(Box::new(id::ID::layout()))
654 } else if resolved_struct == RESOLVED_ASCII_STR {
655 MTL::Struct(Box::new(move_ascii_str_layout()))
656 } else if resolved_struct == RESOLVED_UTF8_STR {
657 MTL::Struct(Box::new(move_utf8_str_layout()))
658 } else if resolved_struct == RESOLVED_STD_OPTION && type_args.len() == 1
660 && is_primitive_type_tag(&type_args[0])
661 {
662 MTL::Vector(Box::new(
663 layout_of_primitive_typetag(&type_args[0]).unwrap(),
664 ))
665 } else {
666 return None;
667 }
668 }
669 })
670}
671
672fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectID, anyhow::Error> {
673 match arg {
675 JsonValue::String(s) => {
676 let s = s.trim().to_lowercase();
677 if !s.starts_with(HEX_PREFIX) {
678 bail!("ObjectID hex string must start with 0x.",);
679 }
680 Ok(ObjectID::from_hex_literal(&s)?)
681 }
682 _ => bail!(
683 "Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?}-byte hex string \
684 prefixed with 0x.",
685 arg,
686 idx,
687 ObjectID::LENGTH,
688 ),
689 }
690}
691
692fn resolve_object_vec_arg(idx: usize, arg: &IotaJsonValue) -> Result<Vec<ObjectID>, anyhow::Error> {
693 match arg.to_json_value() {
695 JsonValue::Array(a) => {
696 let mut object_ids = vec![];
697 for id in a {
698 object_ids.push(resolve_object_arg(idx, &id)?);
699 }
700 Ok(object_ids)
701 }
702 JsonValue::String(s) if s.starts_with('[') && s.ends_with(']') => {
703 let mut object_ids = vec![];
707 for tok in s[1..s.len() - 1].split(',') {
708 let id = JsonValue::String(tok.to_string());
709 object_ids.push(resolve_object_arg(idx, &id)?);
710 }
711 Ok(object_ids)
712 }
713 _ => bail!(
714 "Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
715 Expected a vector of {:?}-byte hex strings prefixed with 0x.\n\
716 Consider escaping your curly braces with a backslash (as in \\[0x42,0x7\\]) \
717 or enclosing the whole vector in single quotes (as in '[0x42,0x7]')",
718 arg.to_json_value(),
719 idx,
720 ObjectID::LENGTH,
721 ),
722 }
723}
724
725fn resolve_call_arg(
726 view: &CompiledModule,
727 type_args: &[TypeTag],
728 idx: usize,
729 arg: &IotaJsonValue,
730 param: &SignatureToken,
731) -> Result<ResolvedCallArg, anyhow::Error> {
732 if let Some(layout) = primitive_type(view, type_args, param) {
733 return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err(
734 |e| {
735 anyhow!(
736 "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}",
737 param,
738 idx,
739 layout,
740 e
741 )
742 },
743 )?));
744 }
745
746 match param {
750 SignatureToken::Reference(inner) | SignatureToken::MutableReference(inner) => {
751 resolve_call_arg(view, type_args, idx, arg, inner)
752 }
753 SignatureToken::Vector(inner) => match &**inner {
754 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
755 Ok(ResolvedCallArg::ObjVec(resolve_object_vec_arg(idx, arg)?))
756 }
757 _ => {
758 bail!(
759 "Unexpected non-primitive vector arg {:?} at {} with value {:?}",
760 param,
761 idx,
762 arg
763 );
764 }
765 },
766 SignatureToken::Datatype(_)
767 | SignatureToken::DatatypeInstantiation(_)
768 | SignatureToken::TypeParameter(_) => Ok(ResolvedCallArg::Object(resolve_object_arg(
769 idx,
770 &arg.to_json_value(),
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
797pub fn 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
855 let call_args = resolve_call_args(&module, type_args, &combined_args_json, parameters)?;
857 let tupled_call_args = call_args
858 .into_iter()
859 .zip(parameters.iter())
860 .map(|(arg, expected_type)| (arg, expected_type.clone()))
861 .collect::<Vec<_>>();
862 Ok(tupled_call_args)
863}
864
865fn convert_string_to_u256(s: &str) -> Result<U256, anyhow::Error> {
866 if let Ok(v) = s.parse::<U256>() {
868 return Ok(v);
869 }
870
871 let s = s.trim().to_lowercase();
876 if !s.starts_with(HEX_PREFIX) {
877 bail!("Unable to convert {s} to unsigned int.",);
878 }
879 U256::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
880}
881
882#[macro_export]
883macro_rules! call_args {
884 ($($value:expr),*) => {
885 Ok::<_, anyhow::Error>(vec![$(iota_json::call_arg!($value)?,)*])
886 };
887 }
888
889#[macro_export]
890macro_rules! call_arg {
891 ($value:expr) => {{
892 use iota_json::IotaJsonValue;
893 trait IotaJsonArg {
894 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue>;
895 }
896 impl IotaJsonArg for &str {
898 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
899 IotaJsonValue::from_str(self)
900 }
901 }
902 impl IotaJsonArg for String {
903 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
904 IotaJsonValue::from_str(&self)
905 }
906 }
907 impl IotaJsonArg for iota_types::base_types::ObjectID {
908 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
909 IotaJsonValue::from_str(&self.to_string())
910 }
911 }
912 impl IotaJsonArg for iota_types::base_types::IotaAddress {
913 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
914 IotaJsonValue::from_str(&self.to_string())
915 }
916 }
917 impl IotaJsonArg for u64 {
918 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
919 IotaJsonValue::from_bcs_bytes(
920 Some(&iota_json::MoveTypeLayout::U64),
921 &bcs::to_bytes(self)?,
922 )
923 }
924 }
925 impl IotaJsonArg for Vec<u8> {
926 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
927 IotaJsonValue::from_bcs_bytes(None, &self)
928 }
929 }
930 impl IotaJsonArg for &[u8] {
931 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
932 IotaJsonValue::from_bcs_bytes(None, self)
933 }
934 }
935 $value.to_iota_json()
936 }};
937}
938
939#[macro_export]
940macro_rules! type_args {
941 ($($value:expr), *) => {{
942 use iota_json_rpc_types::IotaTypeTag;
943 use iota_types::TypeTag;
944 trait IotaJsonTypeArg {
945 fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag>;
946 }
947 impl <T: core::fmt::Display> IotaJsonTypeArg for T {
948 fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag> {
949 Ok(iota_types::parse_iota_type_tag(&self.to_string())?.into())
950 }
951 }
952 Ok::<_, anyhow::Error>(vec![$($value.to_iota_json()?,)*])
953 }};
954 }