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_sdk_types::{Address, Identifier, ObjectId, StructTag, TypeTag};
14use iota_types::{
15 base_types::{
16 RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, TxContext, TxContextKind,
17 is_primitive_type_tag, move_ascii_str_layout, move_utf8_str_layout,
18 },
19 error::IotaError,
20 id::{self, RESOLVED_IOTA_ID},
21 iota_sdk_types_conversions::struct_tag_core_to_sdk,
22 move_package::{
23 IotaAttributeV2, ProtocolBuildConfig, RuntimeModuleMetadata, RuntimeModuleMetadataWrapper,
24 },
25 object::bounded_visitor::BoundedVisitor,
26 transfer::RESOLVED_RECEIVING_STRUCT,
27};
28use move_binary_format::{
29 CompiledModule, file_format::SignatureToken, file_format_common::IOTA_METADATA_KEY,
30};
31use move_bytecode_utils::resolve_struct;
32pub use move_core_types::annotated_value::MoveTypeLayout;
33use move_core_types::{
34 account_address::AccountAddress,
35 annotated_value::{MoveFieldLayout, MoveStruct, MoveValue, MoveVariant},
36 runtime_value as R,
37 u256::U256,
38};
39use schemars::JsonSchema;
40use serde::{Deserialize, Serialize};
41use serde_json::{Number, Value as JsonValue, json};
42
43const HEX_PREFIX: &str = "0x";
44
45#[cfg(test)]
46mod tests;
47
48#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
50pub enum IotaJsonValueErrorKind {
51 ValueTypeNotAllowed,
53
54 ArrayNotHomogeneous,
56}
57
58#[derive(Debug)]
59pub struct IotaJsonValueError {
60 kind: IotaJsonValueErrorKind,
61 val: JsonValue,
62}
63
64impl IotaJsonValueError {
65 pub fn new(val: &JsonValue, kind: IotaJsonValueErrorKind) -> Self {
66 Self {
67 kind,
68 val: val.clone(),
69 }
70 }
71}
72
73impl std::error::Error for IotaJsonValueError {}
74
75impl fmt::Display for IotaJsonValueError {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 let err_str = match self.kind {
78 IotaJsonValueErrorKind::ValueTypeNotAllowed => {
79 format!("JSON value type {} not allowed.", self.val)
80 }
81 IotaJsonValueErrorKind::ArrayNotHomogeneous => {
82 format!("Array not homogeneous. Mismatched value: {}.", self.val)
83 }
84 };
85 write!(f, "{err_str}")
86 }
87}
88
89#[derive(Eq, PartialEq, Debug)]
91pub enum ResolvedCallArg {
92 Object(ObjectId),
93 Pure(Vec<u8>),
94 ObjVec(Vec<ObjectId>),
95}
96
97#[derive(Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
98pub struct IotaJsonValue(JsonValue);
99impl IotaJsonValue {
100 pub fn new(json_value: JsonValue) -> Result<IotaJsonValue, anyhow::Error> {
101 Self::check_value(&json_value)?;
102 Ok(Self(json_value))
103 }
104
105 fn check_value(json_value: &JsonValue) -> Result<(), anyhow::Error> {
106 match json_value {
107 JsonValue::Bool(_) | JsonValue::String(_) => (),
109 JsonValue::Number(n) => {
110 if !n.is_u64() {
112 bail!("{n} not allowed. Number must be unsigned integer of at most u32");
113 }
114 }
115 JsonValue::Array(a) => {
117 check_valid_homogeneous(&JsonValue::Array(a.to_vec()))?
119 }
120 JsonValue::Object(v) => {
121 for (_, value) in v {
122 Self::check_value(value)?;
123 }
124 }
125 JsonValue::Null => bail!("Null not allowed."),
126 };
127 Ok(())
128 }
129
130 pub fn from_object_id(id: ObjectId) -> IotaJsonValue {
131 Self(JsonValue::String(id.to_hex()))
132 }
133
134 pub fn to_bcs_bytes(&self, ty: &MoveTypeLayout) -> Result<Vec<u8>, anyhow::Error> {
135 let move_value = Self::to_move_value(&self.0, ty)?;
136 R::MoveValue::simple_serialize(&move_value)
137 .ok_or_else(|| anyhow!("Unable to serialize {move_value:?}. Expected {ty}"))
138 }
139
140 pub fn from_bcs_bytes(
141 layout: Option<&MoveTypeLayout>,
142 bytes: &[u8],
143 ) -> Result<Self, anyhow::Error> {
144 let Some(layout) = layout else {
145 return IotaJsonValue::new(json!(bytes));
146 };
147 if let Ok(Some(value)) = BoundedVisitor::deserialize_value(bytes, layout)
148 .map(|move_value| move_value_to_json(&move_value))
149 {
150 return IotaJsonValue::new(value);
151 }
152 let value = JsonValue::Array(
153 bytes
154 .iter()
155 .map(|b| JsonValue::Number(Number::from(*b)))
156 .collect(),
157 );
158 IotaJsonValue::new(value)
159 }
160
161 pub fn to_json_value(&self) -> JsonValue {
162 self.0.clone()
163 }
164
165 pub fn to_iota_address(&self) -> anyhow::Result<Address> {
166 json_value_to_iota_address(&self.0)
167 }
168
169 fn handle_inner_struct_layout(
170 inner_vec: &[MoveFieldLayout],
171 val: &JsonValue,
172 ty: &MoveTypeLayout,
173 s: &String,
174 ) -> Result<R::MoveValue, anyhow::Error> {
175 debug_assert!(matches!(val, JsonValue::String(_)));
179
180 if inner_vec.len() != 1 {
181 bail!(
182 "Cannot convert string arg {s} to {ty} which is expected \
183 to be a struct with one field"
184 );
185 }
186
187 match &inner_vec[0].layout {
188 MoveTypeLayout::Vector(inner) => match **inner {
189 MoveTypeLayout::U8 => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
190 Self::to_move_value(val, &inner_vec[0].layout.clone())?,
191 ]))),
192 MoveTypeLayout::Address => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
193 Self::to_move_value(val, &MoveTypeLayout::Address)?,
194 ]))),
195 _ => bail!(
196 "Cannot convert string arg {s} to {ty} \
197 which is expected to be a struct \
198 with one field of address or u8 vector type"
199 ),
200 },
201 MoveTypeLayout::Struct(struct_layout)
202 if struct_tag_core_to_sdk(&struct_layout.type_).is_id() =>
203 {
204 Ok(R::MoveValue::Struct(R::MoveStruct(vec![
205 Self::to_move_value(val, &inner_vec[0].layout.clone())?,
206 ])))
207 }
208 _ => bail!(
209 "Cannot convert string arg {s} to {ty} which is expected \
210 to be a struct with one field of a vector type"
211 ),
212 }
213 }
214
215 pub fn to_move_value(
216 val: &JsonValue,
217 ty: &MoveTypeLayout,
218 ) -> Result<R::MoveValue, anyhow::Error> {
219 Ok(match (val, ty) {
220 (JsonValue::Bool(b), MoveTypeLayout::Bool) => R::MoveValue::Bool(*b),
222
223 (JsonValue::Number(n), MoveTypeLayout::U8) => match n.as_u64() {
226 Some(x) => R::MoveValue::U8(u8::try_from(x)?),
227 None => bail!("{n} is not a valid number. Only u8 allowed."),
228 },
229 (JsonValue::Number(n), MoveTypeLayout::U16) => match n.as_u64() {
230 Some(x) => R::MoveValue::U16(u16::try_from(x)?),
231 None => bail!("{n} is not a valid number. Only u16 allowed."),
232 },
233 (JsonValue::Number(n), MoveTypeLayout::U32) => match n.as_u64() {
234 Some(x) => R::MoveValue::U32(u32::try_from(x)?),
235 None => bail!("{n} is not a valid number. Only u32 allowed."),
236 },
237
238 (JsonValue::String(s), MoveTypeLayout::U8) => {
240 R::MoveValue::U8(u8::try_from(convert_string_to_u256(s.as_str())?)?)
241 }
242 (JsonValue::String(s), MoveTypeLayout::U16) => {
243 R::MoveValue::U16(u16::try_from(convert_string_to_u256(s.as_str())?)?)
244 }
245 (JsonValue::String(s), MoveTypeLayout::U32) => {
246 R::MoveValue::U32(u32::try_from(convert_string_to_u256(s.as_str())?)?)
247 }
248 (JsonValue::String(s), MoveTypeLayout::U64) => {
249 R::MoveValue::U64(u64::try_from(convert_string_to_u256(s.as_str())?)?)
250 }
251 (JsonValue::String(s), MoveTypeLayout::U128) => {
252 R::MoveValue::U128(u128::try_from(convert_string_to_u256(s.as_str())?)?)
253 }
254 (JsonValue::String(s), MoveTypeLayout::U256) => {
255 R::MoveValue::U256(convert_string_to_u256(s.as_str())?)
256 }
257 (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
259 if is_move_string_type(&struct_tag_core_to_sdk(&struct_layout.type_)) =>
260 {
261 R::MoveValue::Vector(s.as_bytes().iter().copied().map(R::MoveValue::U8).collect())
262 }
263 (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
265 if struct_tag_core_to_sdk(&struct_layout.type_).is_id() =>
266 {
267 if struct_layout.fields.len() != 1 {
268 bail!(
269 "Cannot convert string arg {s} to {} which is expected to be a struct with one field",
270 struct_layout.type_
271 );
272 };
273 let addr = Address::from_str(s)?;
274 R::MoveValue::Address(AccountAddress::new(addr.into_bytes()))
275 }
276 (JsonValue::Object(o), MoveTypeLayout::Struct(struct_layout)) => {
277 let mut field_values = vec![];
278 for layout in struct_layout.fields.iter() {
279 let field = o
280 .get(layout.name.as_str())
281 .ok_or_else(|| anyhow!("Missing field {} for struct {ty}", layout.name))?;
282 field_values.push(Self::to_move_value(field, &layout.layout)?);
283 }
284 R::MoveValue::Struct(R::MoveStruct(field_values))
285 }
286 (value, MoveTypeLayout::Struct(struct_layout)) if struct_layout.fields.len() == 1 => {
288 Self::to_move_value(value, &struct_layout.fields[0].layout)?
289 }
290 (JsonValue::String(s), MoveTypeLayout::Vector(t)) => {
291 match &**t {
292 MoveTypeLayout::U8 => {
293 let vec = if s.starts_with(HEX_PREFIX) {
303 Hex::decode(s).map_err(|e| anyhow!(e))?
305 } else {
306 s.as_bytes().to_vec()
308 };
309 R::MoveValue::Vector(vec.iter().copied().map(R::MoveValue::U8).collect())
310 }
311 MoveTypeLayout::Struct(struct_layout) => {
312 Self::handle_inner_struct_layout(&struct_layout.fields, val, ty, s)?
313 }
314 _ => bail!("Cannot convert string arg {s} to {ty}"),
315 }
316 }
317
318 (JsonValue::Array(a), MoveTypeLayout::Vector(inner)) => {
320 R::MoveValue::Vector(
322 a.iter()
323 .map(|i| Self::to_move_value(i, inner))
324 .collect::<Result<Vec<_>, _>>()?,
325 )
326 }
327
328 (v, MoveTypeLayout::Address) => {
329 let addr = json_value_to_iota_address(v)?;
330 R::MoveValue::Address(AccountAddress::new(addr.into_bytes()))
331 }
332
333 _ => bail!("Unexpected arg {val:?} for expected type {ty:?}"),
334 })
335 }
336}
337
338impl Debug for IotaJsonValue {
339 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
340 write!(f, "{}", self.0)
341 }
342}
343
344#[derive(Clone, Debug)]
346pub enum IotaMoveCallInputValue {
347 Json(IotaJsonValue),
349 Bcs(Vec<u8>),
351}
352
353impl From<IotaJsonValue> for IotaMoveCallInputValue {
354 fn from(json: IotaJsonValue) -> Self {
355 Self::Json(json)
356 }
357}
358impl From<Vec<u8>> for IotaMoveCallInputValue {
359 fn from(bcs: Vec<u8>) -> Self {
360 Self::Bcs(bcs)
361 }
362}
363
364fn json_value_to_iota_address(value: &JsonValue) -> anyhow::Result<Address> {
365 match value {
366 JsonValue::String(s) => {
367 let s = s.trim().to_lowercase();
368 if !s.starts_with(HEX_PREFIX) {
369 bail!("Address hex string must start with 0x.",);
370 }
371 Ok(Address::from_str(&s)?)
372 }
373 JsonValue::Array(bytes) => {
374 fn value_to_byte_array(v: &Vec<JsonValue>) -> Option<Vec<u8>> {
375 let mut bytes = vec![];
376 for b in v {
377 let b = b.as_u64()?;
378 if b <= u8::MAX as u64 {
379 bytes.push(b as u8);
380 } else {
381 return None;
382 }
383 }
384 Some(bytes)
385 }
386 let bytes = value_to_byte_array(bytes)
387 .ok_or_else(|| anyhow!("Invalid input: Cannot parse input into Address."))?;
388 Ok(Address::from_bytes(bytes)?)
389 }
390 v => bail!("Unexpected arg {v} for expected type address"),
391 }
392}
393
394fn move_value_to_json(move_value: &MoveValue) -> Option<JsonValue> {
395 Some(match move_value {
396 MoveValue::Vector(values) => JsonValue::Array(
397 values
398 .iter()
399 .map(move_value_to_json)
400 .collect::<Option<_>>()?,
401 ),
402 MoveValue::Bool(v) => json!(v),
403 MoveValue::Signer(v) | MoveValue::Address(v) => {
404 json!(Address::new(v.into_bytes()).to_string())
405 }
406 MoveValue::U8(v) => json!(v),
407 MoveValue::U64(v) => json!(v.to_string()),
408 MoveValue::U128(v) => json!(v.to_string()),
409 MoveValue::U16(v) => json!(v),
410 MoveValue::U32(v) => json!(v),
411 MoveValue::U256(v) => json!(v.to_string()),
412 MoveValue::Struct(move_struct) => match move_struct {
413 MoveStruct { fields, type_ } if is_move_string_type(&struct_tag_core_to_sdk(type_)) => {
414 let (_, v) = fields.first()?;
416 let string: String = bcs::from_bytes(&v.simple_serialize()?).ok()?;
417 json!(string)
418 }
419 MoveStruct { fields, type_ } if struct_tag_core_to_sdk(type_).is_option() => {
420 let (_, v) = fields.first()?;
422 if let MoveValue::Vector(v) = v {
423 JsonValue::Array(v.iter().filter_map(move_value_to_json).collect::<Vec<_>>())
424 } else {
425 return None;
426 }
427 }
428 MoveStruct { fields, type_ } if struct_tag_core_to_sdk(type_).is_id() => {
429 let (_, v) = fields.first()?;
431 if let MoveValue::Address(address) = v {
432 json!(Address::new(address.into_bytes()))
433 } else {
434 return None;
435 }
436 }
437 MoveStruct { fields, .. } => {
440 let fields = fields
441 .iter()
442 .map(|(key, value)| (key, move_value_to_json(value)))
443 .collect::<BTreeMap<_, _>>();
444 json!(fields)
445 }
446 },
447 MoveValue::Variant(MoveVariant {
449 type_: _,
450 tag: _,
451 variant_name,
452 fields,
453 }) => {
454 let fields = fields
455 .iter()
456 .map(|(key, value)| (key, move_value_to_json(value)))
457 .collect::<BTreeMap<_, _>>();
458 json!({
459 "variant": variant_name.to_string(),
460 "fields": fields,
461 })
462 }
463 })
464}
465
466fn is_move_string_type(tag: &StructTag) -> bool {
467 tag.is_string() || tag.is_ascii_string()
468}
469
470impl FromStr for IotaJsonValue {
471 type Err = anyhow::Error;
472 fn from_str(s: &str) -> Result<Self, anyhow::Error> {
473 fn split_top_level_commas(s: &str) -> Vec<&str> {
477 let mut parts = Vec::new();
478 let mut depth = 0usize;
479 let mut start = 0;
480 for (i, ch) in s.char_indices() {
481 match ch {
482 '[' => depth += 1,
483 ']' => depth = depth.saturating_sub(1),
484 ',' if depth == 0 => {
485 parts.push(&s[start..i]);
486 start = i + 1;
487 }
488 _ => {}
489 }
490 }
491 parts.push(&s[start..]);
492 parts
493 }
494
495 fn try_escape_array(s: &str) -> JsonValue {
496 let s = s.trim();
497 if s.starts_with('[') && s.ends_with(']') {
498 if let Some(inner) = s.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
499 return JsonValue::Array(
500 split_top_level_commas(inner)
501 .into_iter()
502 .map(try_escape_array)
503 .collect(),
504 );
505 }
506 }
507 json!(s)
508 }
509 IotaJsonValue::new(serde_json::from_str(s).unwrap_or_else(|_| try_escape_array(s)))
512 }
513}
514
515#[derive(Eq, PartialEq, Debug, Clone, Hash)]
516enum ValidJsonType {
517 Bool,
518 Number,
519 String,
520 Array,
521 Any,
523}
524
525pub fn check_valid_homogeneous(val: &JsonValue) -> Result<(), IotaJsonValueError> {
529 let mut deq: VecDeque<&JsonValue> = VecDeque::new();
530 deq.push_back(val);
531 check_valid_homogeneous_rec(&mut deq)
532}
533
534fn check_valid_homogeneous_rec(
538 curr_q: &mut VecDeque<&JsonValue>,
539) -> Result<(), IotaJsonValueError> {
540 if curr_q.is_empty() {
541 return Ok(());
543 }
544 let mut next_q = VecDeque::new();
546 let mut level_type = ValidJsonType::Any;
548
549 while let Some(v) = curr_q.pop_front() {
551 let curr = match v {
552 JsonValue::Bool(_) => ValidJsonType::Bool,
553 JsonValue::Number(x) if x.is_u64() => ValidJsonType::Number,
554 JsonValue::String(_) => ValidJsonType::String,
555 JsonValue::Array(w) => {
556 w.iter().for_each(|t| next_q.push_back(t));
558 ValidJsonType::Array
559 }
560 _ => {
562 return Err(IotaJsonValueError::new(
563 v,
564 IotaJsonValueErrorKind::ValueTypeNotAllowed,
565 ));
566 }
567 };
568
569 if level_type == ValidJsonType::Any {
570 level_type = curr;
572 } else if level_type != curr {
573 return Err(IotaJsonValueError::new(
575 v,
576 IotaJsonValueErrorKind::ArrayNotHomogeneous,
577 ));
578 }
579 }
580 check_valid_homogeneous_rec(&mut next_q)
582}
583
584pub fn primitive_type(
591 view: &CompiledModule,
592 type_args: &[TypeTag],
593 param: &SignatureToken,
594) -> Option<MoveTypeLayout> {
595 Some(match param {
596 SignatureToken::Bool => MoveTypeLayout::Bool,
597 SignatureToken::U8 => MoveTypeLayout::U8,
598 SignatureToken::U16 => MoveTypeLayout::U16,
599 SignatureToken::U32 => MoveTypeLayout::U32,
600 SignatureToken::U64 => MoveTypeLayout::U64,
601 SignatureToken::U128 => MoveTypeLayout::U128,
602 SignatureToken::U256 => MoveTypeLayout::U256,
603 SignatureToken::Address => MoveTypeLayout::Address,
604 SignatureToken::Vector(inner) => {
605 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?))
606 }
607 SignatureToken::Datatype(struct_handle_idx) => {
608 let resolved_struct = resolve_struct(view, *struct_handle_idx);
609 if resolved_struct == RESOLVED_ASCII_STR {
610 MoveTypeLayout::Struct(Box::new(move_ascii_str_layout()))
611 } else if resolved_struct == RESOLVED_UTF8_STR {
612 MoveTypeLayout::Struct(Box::new(move_utf8_str_layout()))
615 } else if resolved_struct == RESOLVED_IOTA_ID {
616 MoveTypeLayout::Struct(Box::new(id::ID::layout()))
617 } else {
618 return None;
619 }
620 }
621 SignatureToken::DatatypeInstantiation(struct_inst) => {
622 let (idx, targs) = &**struct_inst;
623 let resolved_struct = resolve_struct(view, *idx);
624 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
626 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?))
628 } else {
629 return None;
630 }
631 }
632 SignatureToken::TypeParameter(idx) => {
633 layout_of_primitive_typetag(type_args.get(*idx as usize)?)?
634 }
635 SignatureToken::Signer
636 | SignatureToken::Reference(_)
637 | SignatureToken::MutableReference(_) => return None,
638 })
639}
640
641fn layout_of_primitive_typetag(tag: &TypeTag) -> Option<MoveTypeLayout> {
642 use MoveTypeLayout as MTL;
643 if !is_primitive_type_tag(tag) {
644 return None;
645 }
646
647 Some(match tag {
648 TypeTag::Bool => MTL::Bool,
649 TypeTag::U8 => MTL::U8,
650 TypeTag::U16 => MTL::U16,
651 TypeTag::U32 => MTL::U32,
652 TypeTag::U64 => MTL::U64,
653 TypeTag::U128 => MTL::U128,
654 TypeTag::U256 => MTL::U256,
655 TypeTag::Address => MTL::Address,
656 TypeTag::Signer => return None,
657 TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)),
658 TypeTag::Struct(stag) => {
659 let resolved_struct = (
660 &AccountAddress::new(stag.address().into_bytes()),
661 move_core_types::identifier::IdentStr::new(stag.module().as_str()).unwrap(),
662 move_core_types::identifier::IdentStr::new(stag.name().as_str()).unwrap(),
663 );
664 if resolved_struct == RESOLVED_IOTA_ID {
666 MTL::Struct(Box::new(id::ID::layout()))
667 } else if resolved_struct == RESOLVED_ASCII_STR {
668 MTL::Struct(Box::new(move_ascii_str_layout()))
669 } else if resolved_struct == RESOLVED_UTF8_STR {
670 MTL::Struct(Box::new(move_utf8_str_layout()))
671 } else if resolved_struct == RESOLVED_STD_OPTION && stag.type_params().len() == 1
673 && is_primitive_type_tag(&stag.type_params()[0])
674 {
675 MTL::Vector(Box::new(
676 layout_of_primitive_typetag(&stag.type_params()[0]).unwrap(),
677 ))
678 } else {
679 return None;
680 }
681 }
682 })
683}
684
685fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectId, anyhow::Error> {
686 match arg {
688 JsonValue::String(s) => {
689 let s = s.trim().to_lowercase();
690 Ok(ObjectId::from_prefixed_short_hex(&s)?)
691 }
692 _ => bail!(
693 "Unable to parse arg {:?} as ObjectId at pos {}. Expected {:?}-byte hex string \
694 prefixed with 0x.",
695 arg,
696 idx,
697 ObjectId::LENGTH,
698 ),
699 }
700}
701
702fn resolve_object_vec_arg(idx: usize, arg: &IotaJsonValue) -> Result<Vec<ObjectId>, anyhow::Error> {
703 match arg.to_json_value() {
705 JsonValue::Array(a) => {
706 let mut object_ids = vec![];
707 for id in a {
708 object_ids.push(resolve_object_arg(idx, &id)?);
709 }
710 Ok(object_ids)
711 }
712 JsonValue::String(s) if s.starts_with('[') && s.ends_with(']') => {
713 let mut object_ids = vec![];
717 for tok in s[1..s.len() - 1].split(',') {
718 let id = JsonValue::String(tok.to_string());
719 object_ids.push(resolve_object_arg(idx, &id)?);
720 }
721 Ok(object_ids)
722 }
723 _ => bail!(
724 "Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
725 Expected a vector of {:?}-byte hex strings prefixed with 0x.\n\
726 Consider escaping your curly braces with a backslash (as in \\[0x42,0x7\\]) \
727 or enclosing the whole vector in single quotes (as in '[0x42,0x7]')",
728 arg.to_json_value(),
729 idx,
730 ObjectId::LENGTH,
731 ),
732 }
733}
734
735fn resolve_call_arg(
736 view: &CompiledModule,
737 type_args: &[TypeTag],
738 idx: usize,
739 arg: &IotaMoveCallInputValue,
740 param: &SignatureToken,
741) -> Result<ResolvedCallArg, anyhow::Error> {
742 let json = match arg {
743 IotaMoveCallInputValue::Bcs(bcs) => {
744 return Ok(ResolvedCallArg::Pure(bcs.clone()));
746 }
747 IotaMoveCallInputValue::Json(json) => json,
748 };
749
750 if let Some(layout) = primitive_type(view, type_args, param) {
751 return Ok(ResolvedCallArg::Pure(json.to_bcs_bytes(&layout).map_err(
752 |e| {
753 anyhow!(
754 "Could not serialize argument of type {param:?} at {idx} into {layout}. Got error: {e:?}"
755 )
756 },
757 )?));
758 }
759
760 match param {
764 SignatureToken::Reference(inner) | SignatureToken::MutableReference(inner) => {
765 resolve_call_arg(view, type_args, idx, arg, inner)
766 }
767 SignatureToken::Vector(inner) => match &**inner {
768 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
769 Ok(ResolvedCallArg::ObjVec(resolve_object_vec_arg(idx, json)?))
770 }
771 _ => {
772 bail!("Unexpected non-primitive vector arg {param:?} at {idx} with value {arg:?}");
773 }
774 },
775 SignatureToken::Datatype(_)
776 | SignatureToken::DatatypeInstantiation(_)
777 | SignatureToken::TypeParameter(_) => Ok(ResolvedCallArg::Object(resolve_object_arg(
778 idx,
779 &json.to_json_value(),
780 )?)),
781 _ => bail!("Unexpected non-primitive arg {param:?} at {idx} with value {arg:?}"),
782 }
783}
784
785pub fn is_receiving_argument(view: &CompiledModule, arg_type: &SignatureToken) -> bool {
786 use SignatureToken as ST;
787
788 let mut token = arg_type;
791 while let ST::Reference(inner) | ST::MutableReference(inner) = token {
792 token = inner;
793 }
794
795 matches!(
796 token,
797 ST::DatatypeInstantiation(inst) if resolve_struct(view, inst.0) == RESOLVED_RECEIVING_STRUCT && inst.1.len() == 1
798 )
799}
800
801pub fn resolve_call_args(
802 view: &CompiledModule,
803 type_args: &[TypeTag],
804 args: &[IotaMoveCallInputValue],
805 parameter_types: &[SignatureToken],
806) -> Result<Vec<ResolvedCallArg>, anyhow::Error> {
807 args.iter()
808 .zip(parameter_types)
809 .enumerate()
810 .map(|(idx, (arg, param))| resolve_call_arg(view, type_args, idx, arg, param))
811 .collect()
812}
813
814fn is_view_function_from_module_metadata(
821 module: &CompiledModule,
822 function_name: &str,
823) -> Result<bool, IotaError> {
824 let Some(metadata) = module
825 .metadata
826 .iter()
827 .find(|metadata| metadata.key == IOTA_METADATA_KEY)
828 else {
829 return Ok(false);
830 };
831 let metadata_wrapper: RuntimeModuleMetadataWrapper =
832 bcs::from_bytes(&metadata.value).map_err(|error| {
833 IotaError::RuntimeModuleMetadataDeserialization {
834 error: error.to_string(),
835 }
836 })?;
837 let metadata = metadata_wrapper.try_into_runtime_module_metadata(&ProtocolBuildConfig {
840 allow_view_function: true,
841 max_move_package_size: None,
842 })?;
843 Ok(match metadata {
844 RuntimeModuleMetadata::V1(_) => false,
845 RuntimeModuleMetadata::V2(metadata_v2) => metadata_v2
846 .fun_attributes
847 .get(function_name)
848 .is_some_and(|attributes| {
849 attributes
850 .iter()
851 .any(|attribute| matches!(attribute, IotaAttributeV2::View))
852 }),
853 })
854}
855
856pub fn resolve_move_function_args(
861 module: &CompiledModule,
862 function: &Identifier,
863 type_args: &[TypeTag],
864 args: Vec<IotaMoveCallInputValue>,
865 require_view_function: bool,
866) -> Result<Vec<(ResolvedCallArg, SignatureToken)>, anyhow::Error> {
867 let fdef = module
869 .find_function_def_by_name(function.as_str())
870 .map(|(_, fdef)| fdef)
871 .ok_or_else(|| anyhow!("Could not find function"))?;
872
873 if require_view_function {
874 let has_view_attribute = is_view_function_from_module_metadata(module, function.as_str())?;
875 if !has_view_attribute {
876 bail!("Function {function} is not declared as a #[view] function");
877 }
878 }
879 let function_signature = module.function_handle_at(fdef.function);
880 let parameters = &module.signature_at(function_signature.parameters).0;
881
882 let expected_len = match parameters.last() {
884 Some(param) if TxContext::kind(module, param) != TxContextKind::None => {
885 parameters.len() - 1
886 }
887 _ => parameters.len(),
888 };
889 if args.len() != expected_len {
890 bail!("Expected {} args, found {}", expected_len, args.len());
891 }
892
893 let resolved_args = resolve_call_args(module, type_args, &args, parameters)?;
895 let tupled_call_args = resolved_args
896 .into_iter()
897 .zip(parameters.iter())
898 .map(|(arg, expected_type)| (arg, expected_type.clone()))
899 .collect::<Vec<_>>();
900 Ok(tupled_call_args)
901}
902
903fn convert_string_to_u256(s: &str) -> Result<U256, anyhow::Error> {
904 if let Ok(v) = s.parse::<U256>() {
906 return Ok(v);
907 }
908
909 let s = s.trim().to_lowercase();
914 if !s.starts_with(HEX_PREFIX) {
915 bail!("Unable to convert {s} to unsigned int.",);
916 }
917 U256::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
918}
919
920#[macro_export]
921macro_rules! call_args {
922 ($($value:expr),*) => {
923 Ok::<_, anyhow::Error>(vec![$(iota_json::call_arg!($value)?,)*])
924 };
925 }
926
927#[macro_export]
928macro_rules! call_arg {
929 ($value:expr) => {{
930 use iota_json::IotaJsonValue;
931 trait IotaJsonArg {
932 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue>;
933 }
934 impl IotaJsonArg for &str {
936 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
937 IotaJsonValue::from_str(self)
938 }
939 }
940 impl IotaJsonArg for String {
941 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
942 IotaJsonValue::from_str(&self)
943 }
944 }
945 impl IotaJsonArg for iota_sdk_types::ObjectId {
946 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
947 IotaJsonValue::from_str(&self.to_string())
948 }
949 }
950 impl IotaJsonArg for iota_sdk_types::Address {
951 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
952 IotaJsonValue::from_str(&self.to_string())
953 }
954 }
955 impl IotaJsonArg for u64 {
956 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
957 IotaJsonValue::from_bcs_bytes(
958 Some(&iota_json::MoveTypeLayout::U64),
959 &bcs::to_bytes(self)?,
960 )
961 }
962 }
963 impl IotaJsonArg for Vec<u8> {
964 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
965 IotaJsonValue::from_bcs_bytes(None, &self)
966 }
967 }
968 impl IotaJsonArg for &[u8] {
969 fn to_iota_json(&self) -> anyhow::Result<IotaJsonValue> {
970 IotaJsonValue::from_bcs_bytes(None, self)
971 }
972 }
973 $value.to_iota_json()
974 }};
975}
976
977#[macro_export]
978macro_rules! type_args {
979 ($($value:expr), *) => {{
980 use iota_json_rpc_types::IotaTypeTag;
981 use iota_sdk_types::TypeTag;
982 trait IotaJsonTypeArg {
983 fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag>;
984 }
985 impl <T: core::fmt::Display> IotaJsonTypeArg for T {
986 fn to_iota_json(&self) -> anyhow::Result<IotaTypeTag> {
987 Ok(iota_types::parse_iota_type_tag(&self.to_string())?.into())
988 }
989 }
990 Ok::<_, anyhow::Error>(vec![$($value.to_iota_json()?,)*])
991 }};
992 }