1use std::collections::{BTreeMap, VecDeque};
5use std::fmt::{self, Debug, Formatter};
6use std::str::FromStr;
7
8use anyhow::{anyhow, bail};
9use fastcrypto::encoding::{Encoding, Hex};
10use move_binary_format::CompiledModule;
11use move_binary_format::{binary_config::BinaryConfig, file_format::SignatureToken};
12use move_bytecode_utils::resolve_struct;
13pub use move_core_types::annotated_value::MoveTypeLayout;
14use move_core_types::annotated_value::{MoveFieldLayout, MoveVariant};
15use move_core_types::u256::U256;
16use move_core_types::{
17 annotated_value::{MoveStruct, MoveValue},
18 identifier::Identifier,
19 language_storage::{StructTag, TypeTag},
20 runtime_value as R,
21};
22use schemars::JsonSchema;
23use serde::{Deserialize, Serialize};
24use serde_json::{Number, Value as JsonValue, json};
25
26use sui_types::MOVE_STDLIB_ADDRESS;
27use sui_types::base_types::{
28 ObjectID, RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, STD_ASCII_MODULE_NAME,
29 STD_ASCII_STRUCT_NAME, STD_OPTION_MODULE_NAME, STD_OPTION_STRUCT_NAME, STD_UTF8_MODULE_NAME,
30 STD_UTF8_STRUCT_NAME, SuiAddress, TxContext, TxContextKind, is_primitive_type_tag,
31 move_ascii_str_layout, move_utf8_str_layout,
32};
33use sui_types::id::{self, ID, RESOLVED_SUI_ID};
34use sui_types::move_package::MovePackage;
35use sui_types::object::bounded_visitor::BoundedVisitor;
36use sui_types::transfer::RESOLVED_RECEIVING_STRUCT;
37
38const HEX_PREFIX: &str = "0x";
39
40#[cfg(test)]
41mod tests;
42
43#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
45pub enum SuiJsonValueErrorKind {
46 ValueTypeNotAllowed,
48
49 ArrayNotHomogeneous,
51}
52
53#[derive(Debug)]
54pub struct SuiJsonValueError {
55 kind: SuiJsonValueErrorKind,
56 val: JsonValue,
57}
58
59impl SuiJsonValueError {
60 pub fn new(val: &JsonValue, kind: SuiJsonValueErrorKind) -> Self {
61 Self {
62 kind,
63 val: val.clone(),
64 }
65 }
66}
67
68impl std::error::Error for SuiJsonValueError {}
69
70impl fmt::Display for SuiJsonValueError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 let err_str = match self.kind {
73 SuiJsonValueErrorKind::ValueTypeNotAllowed => {
74 format!("JSON value type {} not allowed.", self.val)
75 }
76 SuiJsonValueErrorKind::ArrayNotHomogeneous => {
77 format!("Array not homogeneous. Mismatched value: {}.", self.val)
78 }
79 };
80 write!(f, "{err_str}")
81 }
82}
83
84#[derive(Eq, PartialEq, Debug)]
86pub enum ResolvedCallArg {
87 Object(ObjectID),
88 Pure(Vec<u8>),
89 ObjVec(Vec<ObjectID>),
90}
91
92#[derive(Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema)]
93pub struct SuiJsonValue(JsonValue);
94impl SuiJsonValue {
95 pub fn new(json_value: JsonValue) -> Result<SuiJsonValue, anyhow::Error> {
96 Self::check_value(&json_value)?;
97 Ok(Self(json_value))
98 }
99
100 fn check_value(json_value: &JsonValue) -> Result<(), anyhow::Error> {
101 match json_value {
102 JsonValue::Bool(_) | JsonValue::String(_) => (),
104 JsonValue::Number(n) => {
105 if !n.is_u64() {
107 return Err(anyhow!(
108 "{n} not allowed. Number must be unsigned integer of at most u32"
109 ));
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) -> SuiJsonValue {
128 Self(JsonValue::String(id.to_hex_uncompressed()))
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 json = if let Some(layout) = layout {
142 fn try_parse_string(layout: &MoveTypeLayout, bytes: &[u8]) -> Option<String> {
144 if let MoveTypeLayout::Vector(t) = layout
145 && let MoveTypeLayout::U8 = **t
146 {
147 return bcs::from_bytes::<String>(bytes).ok();
148 }
149 None
150 }
151 if let Some(s) = try_parse_string(layout, bytes) {
152 json!(s)
153 } else {
154 BoundedVisitor::deserialize_value(bytes, layout).map_or_else(
155 |_| {
156 JsonValue::Array(
158 bytes
159 .iter()
160 .map(|b| JsonValue::Number(Number::from(*b)))
161 .collect(),
162 )
163 },
164 |move_value| {
165 move_value_to_json(&move_value).unwrap_or_else(|| {
166 JsonValue::Array(
168 bytes
169 .iter()
170 .map(|b| JsonValue::Number(Number::from(*b)))
171 .collect(),
172 )
173 })
174 },
175 )
176 }
177 } else {
178 json!(bytes)
179 };
180 SuiJsonValue::new(json)
181 }
182
183 pub fn to_json_value(&self) -> JsonValue {
184 self.0.clone()
185 }
186
187 pub fn to_sui_address(&self) -> anyhow::Result<SuiAddress> {
188 json_value_to_sui_address(&self.0)
189 }
190
191 fn handle_inner_struct_layout(
192 inner_vec: &[MoveFieldLayout],
193 val: &JsonValue,
194 ty: &MoveTypeLayout,
195 s: &String,
196 ) -> Result<R::MoveValue, anyhow::Error> {
197 debug_assert!(matches!(val, JsonValue::String(_)));
201
202 if inner_vec.len() != 1 {
203 bail!(
204 "Cannot convert string arg {s} to {ty} which is expected \
205 to be a struct with one field"
206 );
207 }
208
209 match &inner_vec[0].layout {
210 MoveTypeLayout::Vector(inner) => match **inner {
211 MoveTypeLayout::U8 => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
212 Self::to_move_value(val, &inner_vec[0].layout.clone())?,
213 ]))),
214 MoveTypeLayout::Address => Ok(R::MoveValue::Struct(R::MoveStruct(vec![
215 Self::to_move_value(val, &MoveTypeLayout::Address)?,
216 ]))),
217 _ => bail!(
218 "Cannot convert string arg {s} to {ty} \
219 which is expected to be a struct \
220 with one field of address or u8 vector type"
221 ),
222 },
223 MoveTypeLayout::Struct(struct_layout) if struct_layout.type_ == ID::type_() => {
224 Ok(R::MoveValue::Struct(R::MoveStruct(vec![
225 Self::to_move_value(val, &inner_vec[0].layout.clone())?,
226 ])))
227 }
228 _ => bail!(
229 "Cannot convert string arg {s} to {ty} which is expected \
230 to be a struct with one field of a vector type"
231 ),
232 }
233 }
234
235 pub fn to_move_value(
236 val: &JsonValue,
237 ty: &MoveTypeLayout,
238 ) -> Result<R::MoveValue, anyhow::Error> {
239 Ok(match (val, ty) {
240 (JsonValue::Bool(b), MoveTypeLayout::Bool) => R::MoveValue::Bool(*b),
242
243 (JsonValue::Number(n), MoveTypeLayout::U8) => match n.as_u64() {
245 Some(x) => R::MoveValue::U8(u8::try_from(x)?),
246 None => return Err(anyhow!("{} is not a valid number. Only u8 allowed.", n)),
247 },
248 (JsonValue::Number(n), MoveTypeLayout::U16) => match n.as_u64() {
249 Some(x) => R::MoveValue::U16(u16::try_from(x)?),
250 None => return Err(anyhow!("{} is not a valid number. Only u16 allowed.", n)),
251 },
252 (JsonValue::Number(n), MoveTypeLayout::U32) => match n.as_u64() {
253 Some(x) => R::MoveValue::U32(u32::try_from(x)?),
254 None => return Err(anyhow!("{} is not a valid number. Only u32 allowed.", n)),
255 },
256
257 (JsonValue::String(s), MoveTypeLayout::U8) => {
259 R::MoveValue::U8(u8::try_from(convert_string_to_u256(s.as_str())?)?)
260 }
261 (JsonValue::String(s), MoveTypeLayout::U16) => {
262 R::MoveValue::U16(u16::try_from(convert_string_to_u256(s.as_str())?)?)
263 }
264 (JsonValue::String(s), MoveTypeLayout::U32) => {
265 R::MoveValue::U32(u32::try_from(convert_string_to_u256(s.as_str())?)?)
266 }
267 (JsonValue::String(s), MoveTypeLayout::U64) => {
268 R::MoveValue::U64(u64::try_from(convert_string_to_u256(s.as_str())?)?)
269 }
270 (JsonValue::String(s), MoveTypeLayout::U128) => {
271 R::MoveValue::U128(u128::try_from(convert_string_to_u256(s.as_str())?)?)
272 }
273 (JsonValue::String(s), MoveTypeLayout::U256) => {
274 R::MoveValue::U256(convert_string_to_u256(s.as_str())?)
275 }
276 (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
278 if is_move_string_type(&struct_layout.type_) =>
279 {
280 R::MoveValue::Vector(s.as_bytes().iter().copied().map(R::MoveValue::U8).collect())
281 }
282 (JsonValue::String(s), MoveTypeLayout::Struct(struct_layout))
284 if struct_layout.type_ == ID::type_() =>
285 {
286 if struct_layout.fields.len() != 1 {
287 bail!(
288 "Cannot convert string arg {s} to {} which is expected to be a struct with one field",
289 struct_layout.type_
290 );
291 };
292 let addr = SuiAddress::from_str(s)?;
293 R::MoveValue::Address(addr.into())
294 }
295 (JsonValue::Object(o), MoveTypeLayout::Struct(struct_layout)) => {
296 let mut field_values = vec![];
297 for layout in struct_layout.fields.iter() {
298 let field = o
299 .get(layout.name.as_str())
300 .ok_or_else(|| anyhow!("Missing field {} for struct {ty}", layout.name))?;
301 field_values.push(Self::to_move_value(field, &layout.layout)?);
302 }
303 R::MoveValue::Struct(R::MoveStruct(field_values))
304 }
305 (value, MoveTypeLayout::Struct(struct_layout)) if struct_layout.fields.len() == 1 => {
307 Self::to_move_value(value, &struct_layout.fields[0].layout)?
308 }
309 (JsonValue::String(s), MoveTypeLayout::Vector(t)) => {
310 match &**t {
311 MoveTypeLayout::U8 => {
312 let vec = if s.starts_with(HEX_PREFIX) {
322 Hex::decode(s).map_err(|e| anyhow!(e))?
324 } else {
325 s.as_bytes().to_vec()
327 };
328 R::MoveValue::Vector(vec.iter().copied().map(R::MoveValue::U8).collect())
329 }
330 MoveTypeLayout::Struct(struct_layout) => {
331 Self::handle_inner_struct_layout(&struct_layout.fields, val, ty, s)?
332 }
333 _ => bail!("Cannot convert string arg {s} to {ty}"),
334 }
335 }
336
337 (JsonValue::Array(a), MoveTypeLayout::Vector(inner)) => {
339 R::MoveValue::Vector(
341 a.iter()
342 .map(|i| Self::to_move_value(i, inner))
343 .collect::<Result<Vec<_>, _>>()?,
344 )
345 }
346
347 (v, MoveTypeLayout::Address) => {
348 let addr = json_value_to_sui_address(v)?;
349 R::MoveValue::Address(addr.into())
350 }
351
352 _ => bail!("Unexpected arg {val:?} for expected type {ty:?}"),
353 })
354 }
355}
356
357impl Debug for SuiJsonValue {
358 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
359 write!(f, "{}", self.0)
360 }
361}
362
363fn json_value_to_sui_address(value: &JsonValue) -> anyhow::Result<SuiAddress> {
364 match value {
365 JsonValue::String(s) => {
366 let s = s.trim().to_lowercase();
367 if !s.starts_with(HEX_PREFIX) {
368 bail!("Address hex string must start with 0x.",);
369 }
370 Ok(SuiAddress::from_str(&s)?)
371 }
372 JsonValue::Array(bytes) => {
373 fn value_to_byte_array(v: &Vec<JsonValue>) -> Option<Vec<u8>> {
374 let mut bytes = vec![];
375 for b in v {
376 let b = b.as_u64()?;
377 if b <= u8::MAX as u64 {
378 bytes.push(b as u8);
379 } else {
380 return None;
381 }
382 }
383 Some(bytes)
384 }
385 let bytes = value_to_byte_array(bytes)
386 .ok_or_else(|| anyhow!("Invalid input: Cannot parse input into SuiAddress."))?;
387 Ok(SuiAddress::try_from(bytes)?)
388 }
389 v => bail!("Unexpected arg {v} for expected type address"),
390 }
391}
392
393fn move_value_to_json(move_value: &MoveValue) -> Option<JsonValue> {
394 Some(match move_value {
395 MoveValue::Vector(values) => JsonValue::Array(
396 values
397 .iter()
398 .map(move_value_to_json)
399 .collect::<Option<_>>()?,
400 ),
401 MoveValue::Bool(v) => json!(v),
402 MoveValue::Signer(v) | MoveValue::Address(v) => json!(SuiAddress::from(*v).to_string()),
403 MoveValue::U8(v) => json!(v),
404 MoveValue::U64(v) => json!(v.to_string()),
405 MoveValue::U128(v) => json!(v.to_string()),
406 MoveValue::U16(v) => json!(v),
407 MoveValue::U32(v) => json!(v),
408 MoveValue::U256(v) => json!(v.to_string()),
409 MoveValue::Struct(move_struct) => match move_struct {
410 MoveStruct { fields, type_ } if is_move_string_type(type_) => {
411 let (_, v) = fields.first()?;
413 let string: String = bcs::from_bytes(&v.simple_serialize()?).ok()?;
414 json!(string)
415 }
416 MoveStruct { fields, type_ } if is_move_option_type(type_) => {
417 let (_, v) = fields.first()?;
419 if let MoveValue::Vector(v) = v {
420 JsonValue::Array(v.iter().filter_map(move_value_to_json).collect::<Vec<_>>())
421 } else {
422 return None;
423 }
424 }
425 MoveStruct { fields, type_ } if type_ == &ID::type_() => {
426 let (_, v) = fields.first()?;
428 if let MoveValue::Address(address) = v {
429 json!(SuiAddress::from(*address))
430 } else {
431 return None;
432 }
433 }
434 MoveStruct { fields, .. } => {
436 let fields = fields
437 .iter()
438 .map(|(key, value)| (key, move_value_to_json(value)))
439 .collect::<BTreeMap<_, _>>();
440 json!(fields)
441 }
442 },
443 MoveValue::Variant(MoveVariant {
445 type_: _,
446 tag: _,
447 variant_name,
448 fields,
449 }) => {
450 let fields = fields
451 .iter()
452 .map(|(key, value)| (key, move_value_to_json(value)))
453 .collect::<BTreeMap<_, _>>();
454 json!({
455 "variant": variant_name.to_string(),
456 "fields": fields,
457 })
458 }
459 })
460}
461
462fn is_move_string_type(tag: &StructTag) -> bool {
463 (tag.address == MOVE_STDLIB_ADDRESS
464 && tag.module.as_ident_str() == STD_UTF8_MODULE_NAME
465 && tag.name.as_ident_str() == STD_UTF8_STRUCT_NAME)
466 || (tag.address == MOVE_STDLIB_ADDRESS
467 && tag.module.as_ident_str() == STD_ASCII_MODULE_NAME
468 && tag.name.as_ident_str() == STD_ASCII_STRUCT_NAME)
469}
470fn is_move_option_type(tag: &StructTag) -> bool {
471 tag.address == MOVE_STDLIB_ADDRESS
472 && tag.module.as_ident_str() == STD_OPTION_MODULE_NAME
473 && tag.name.as_ident_str() == STD_OPTION_STRUCT_NAME
474}
475
476impl FromStr for SuiJsonValue {
477 type Err = anyhow::Error;
478 fn from_str(s: &str) -> Result<Self, anyhow::Error> {
479 fn try_escape_array(s: &str) -> JsonValue {
480 let s = s.trim();
481 if s.starts_with('[')
482 && s.ends_with(']')
483 && let Some(s) = s.strip_prefix('[').and_then(|s| s.strip_suffix(']'))
484 {
485 return JsonValue::Array(s.split(',').map(try_escape_array).collect());
486 }
487 json!(s)
488 }
489 SuiJsonValue::new(serde_json::from_str(s).unwrap_or_else(|_| try_escape_array(s)))
491 }
492}
493
494#[derive(Eq, PartialEq, Debug, Clone, Hash)]
495enum ValidJsonType {
496 Bool,
497 Number,
498 String,
499 Array,
500 Any,
502}
503
504pub fn check_valid_homogeneous(val: &JsonValue) -> Result<(), SuiJsonValueError> {
507 let mut deq: VecDeque<&JsonValue> = VecDeque::new();
508 deq.push_back(val);
509 check_valid_homogeneous_rec(&mut deq)
510}
511
512fn check_valid_homogeneous_rec(curr_q: &mut VecDeque<&JsonValue>) -> Result<(), SuiJsonValueError> {
515 if curr_q.is_empty() {
516 return Ok(());
518 }
519 let mut next_q = VecDeque::new();
521 let mut level_type = ValidJsonType::Any;
523
524 while let Some(v) = curr_q.pop_front() {
526 let curr = match v {
527 JsonValue::Bool(_) => ValidJsonType::Bool,
528 JsonValue::Number(x) if x.is_u64() => ValidJsonType::Number,
529 JsonValue::String(_) => ValidJsonType::String,
530 JsonValue::Array(w) => {
531 w.iter().for_each(|t| next_q.push_back(t));
533 ValidJsonType::Array
534 }
535 _ => {
537 return Err(SuiJsonValueError::new(
538 v,
539 SuiJsonValueErrorKind::ValueTypeNotAllowed,
540 ));
541 }
542 };
543
544 if level_type == ValidJsonType::Any {
545 level_type = curr;
547 } else if level_type != curr {
548 return Err(SuiJsonValueError::new(
550 v,
551 SuiJsonValueErrorKind::ArrayNotHomogeneous,
552 ));
553 }
554 }
555 check_valid_homogeneous_rec(&mut next_q)
557}
558
559pub fn primitive_type(
565 view: &CompiledModule,
566 type_args: &[TypeTag],
567 param: &SignatureToken,
568) -> Option<MoveTypeLayout> {
569 Some(match param {
570 SignatureToken::Bool => MoveTypeLayout::Bool,
571 SignatureToken::U8 => MoveTypeLayout::U8,
572 SignatureToken::U16 => MoveTypeLayout::U16,
573 SignatureToken::U32 => MoveTypeLayout::U32,
574 SignatureToken::U64 => MoveTypeLayout::U64,
575 SignatureToken::U128 => MoveTypeLayout::U128,
576 SignatureToken::U256 => MoveTypeLayout::U256,
577 SignatureToken::Address => MoveTypeLayout::Address,
578
579 SignatureToken::Vector(inner) => {
580 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?))
581 }
582
583 SignatureToken::Datatype(struct_handle_idx) => {
584 let resolved_struct = resolve_struct(view, *struct_handle_idx);
585 if resolved_struct == RESOLVED_ASCII_STR {
586 MoveTypeLayout::Struct(Box::new(move_ascii_str_layout()))
587 } else if resolved_struct == RESOLVED_UTF8_STR {
588 MoveTypeLayout::Struct(Box::new(move_utf8_str_layout()))
590 } else if resolved_struct == RESOLVED_SUI_ID {
591 MoveTypeLayout::Struct(Box::new(id::ID::layout()))
592 } else {
593 return None;
594 }
595 }
596
597 SignatureToken::DatatypeInstantiation(struct_inst) => {
598 let (idx, targs) = &**struct_inst;
599 let resolved_struct = resolve_struct(view, *idx);
600 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
602 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?))
604 } else {
605 return None;
606 }
607 }
608
609 SignatureToken::TypeParameter(idx) => {
610 layout_of_primitive_typetag(type_args.get(*idx as usize)?)?
611 }
612
613 SignatureToken::Reference(sig) | SignatureToken::MutableReference(sig) => {
614 primitive_type(view, type_args, sig)?
615 }
616
617 SignatureToken::Signer => return None,
618 })
619}
620
621fn layout_of_primitive_typetag(tag: &TypeTag) -> Option<MoveTypeLayout> {
622 use MoveTypeLayout as MTL;
623 if !is_primitive_type_tag(tag) {
624 return None;
625 }
626
627 Some(match tag {
628 TypeTag::Bool => MTL::Bool,
629 TypeTag::U8 => MTL::U8,
630 TypeTag::U16 => MTL::U16,
631 TypeTag::U32 => MTL::U32,
632 TypeTag::U64 => MTL::U64,
633 TypeTag::U128 => MTL::U128,
634 TypeTag::U256 => MTL::U256,
635 TypeTag::Address => MTL::Address,
636 TypeTag::Signer => return None,
637 TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)),
638 TypeTag::Struct(stag) => {
639 let StructTag {
640 address,
641 module,
642 name,
643 type_params: type_args,
644 } = &**stag;
645 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
646 if resolved_struct == RESOLVED_SUI_ID {
648 MTL::Struct(Box::new(id::ID::layout()))
649 } else if resolved_struct == RESOLVED_ASCII_STR {
650 MTL::Struct(Box::new(move_ascii_str_layout()))
651 } else if resolved_struct == RESOLVED_UTF8_STR {
652 MTL::Struct(Box::new(move_utf8_str_layout()))
653 } else if resolved_struct == RESOLVED_STD_OPTION && type_args.len() == 1
655 && is_primitive_type_tag(&type_args[0])
656 {
657 MTL::Vector(Box::new(
658 layout_of_primitive_typetag(&type_args[0]).unwrap(),
659 ))
660 } else {
661 return None;
662 }
663 }
664 })
665}
666
667fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectID, anyhow::Error> {
668 match arg {
670 JsonValue::String(s) => {
671 let s = s.trim().to_lowercase();
672 if !s.starts_with(HEX_PREFIX) {
673 bail!("ObjectID hex string must start with 0x.",);
674 }
675 Ok(ObjectID::from_hex_literal(&s)?)
676 }
677 _ => bail!(
678 "Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?}-byte hex string \
679 prefixed with 0x.",
680 arg,
681 idx,
682 ObjectID::LENGTH,
683 ),
684 }
685}
686
687fn resolve_object_vec_arg(idx: usize, arg: &SuiJsonValue) -> Result<Vec<ObjectID>, anyhow::Error> {
688 match arg.to_json_value() {
690 JsonValue::Array(a) => {
691 let mut object_ids = vec![];
692 for id in a {
693 object_ids.push(resolve_object_arg(idx, &id)?);
694 }
695 Ok(object_ids)
696 }
697 JsonValue::String(s) if s.starts_with('[') && s.ends_with(']') => {
698 let mut object_ids = vec![];
702 for tok in s[1..s.len() - 1].split(',') {
703 let id = JsonValue::String(tok.to_string());
704 object_ids.push(resolve_object_arg(idx, &id)?);
705 }
706 Ok(object_ids)
707 }
708 _ => bail!(
709 "Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
710 Expected a vector of {:?}-byte hex strings prefixed with 0x.\n\
711 Consider escaping your curly braces with a backslash (as in \\[0x42,0x7\\]) \
712 or enclosing the whole vector in single quotes (as in '[0x42,0x7]')",
713 arg.to_json_value(),
714 idx,
715 ObjectID::LENGTH,
716 ),
717 }
718}
719
720fn resolve_call_arg(
721 view: &CompiledModule,
722 type_args: &[TypeTag],
723 idx: usize,
724 arg: &SuiJsonValue,
725 param: &SignatureToken,
726) -> Result<ResolvedCallArg, anyhow::Error> {
727 if let Some(layout) = primitive_type(view, type_args, param) {
728 return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err(
729 |e| {
730 anyhow!(
731 "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}",
732 param,
733 idx,
734 layout,
735 e
736 )
737 },
738 )?));
739 }
740
741 match param {
744 SignatureToken::Datatype(_)
745 | SignatureToken::DatatypeInstantiation(_)
746 | SignatureToken::TypeParameter(_)
747 | SignatureToken::Reference(_)
748 | SignatureToken::MutableReference(_) => Ok(ResolvedCallArg::Object(resolve_object_arg(
749 idx,
750 &arg.to_json_value(),
751 )?)),
752 SignatureToken::Vector(inner) => match &**inner {
753 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
754 Ok(ResolvedCallArg::ObjVec(resolve_object_vec_arg(idx, arg)?))
755 }
756 _ => {
757 bail!(
758 "Unexpected non-primitive vector arg {:?} at {} with value {:?}",
759 param,
760 idx,
761 arg
762 );
763 }
764 },
765 _ => bail!(
766 "Unexpected non-primitive arg {:?} at {} with value {:?}",
767 param,
768 idx,
769 arg
770 ),
771 }
772}
773
774pub fn is_receiving_argument(view: &CompiledModule, arg_type: &SignatureToken) -> bool {
775 use SignatureToken as ST;
776
777 let mut token = arg_type;
780 while let ST::Reference(inner) | ST::MutableReference(inner) = token {
781 token = inner;
782 }
783
784 matches!(
785 token,
786 ST::DatatypeInstantiation(inst) if resolve_struct(view, inst.0) == RESOLVED_RECEIVING_STRUCT && inst.1.len() == 1
787 )
788}
789
790fn resolve_call_args(
791 view: &CompiledModule,
792 type_args: &[TypeTag],
793 json_args: &[SuiJsonValue],
794 parameter_types: &[SignatureToken],
795) -> Result<Vec<ResolvedCallArg>, anyhow::Error> {
796 json_args
797 .iter()
798 .zip(parameter_types)
799 .enumerate()
800 .map(|(idx, (arg, param))| resolve_call_arg(view, type_args, idx, arg, param))
801 .collect()
802}
803
804pub fn resolve_move_function_args(
807 package: &MovePackage,
808 module_ident: Identifier,
809 function: Identifier,
810 type_args: &[TypeTag],
811 combined_args_json: Vec<SuiJsonValue>,
812) -> Result<Vec<(ResolvedCallArg, SignatureToken)>, anyhow::Error> {
813 let module = package.deserialize_module(&module_ident, &BinaryConfig::standard())?;
815 let function_str = function.as_ident_str();
816 let fdef = module
817 .function_defs
818 .iter()
819 .find(|fdef| {
820 module.identifier_at(module.function_handle_at(fdef.function).name) == function_str
821 })
822 .ok_or_else(|| {
823 anyhow!(
824 "Could not resolve function {} in module {}",
825 function,
826 module_ident
827 )
828 })?;
829 let function_signature = module.function_handle_at(fdef.function);
830 let parameters = &module.signature_at(function_signature.parameters).0;
831
832 let expected_len = match parameters.last() {
834 Some(param) if TxContext::kind(&module, param) != TxContextKind::None => {
835 parameters.len() - 1
836 }
837 _ => parameters.len(),
838 };
839 if combined_args_json.len() != expected_len {
840 bail!(
841 "Expected {} args, found {}",
842 expected_len,
843 combined_args_json.len()
844 );
845 }
846 let call_args = resolve_call_args(&module, type_args, &combined_args_json, parameters)?;
848 let tupled_call_args = call_args
849 .into_iter()
850 .zip(parameters.iter())
851 .map(|(arg, expected_type)| (arg, expected_type.clone()))
852 .collect::<Vec<_>>();
853 Ok(tupled_call_args)
854}
855
856fn convert_string_to_u256(s: &str) -> Result<U256, anyhow::Error> {
857 if let Ok(v) = s.parse::<U256>() {
859 return Ok(v);
860 }
861
862 let s = s.trim().to_lowercase();
867 if !s.starts_with(HEX_PREFIX) {
868 bail!("Unable to convert {s} to unsigned int.",);
869 }
870 U256::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
871}
872
873#[macro_export]
874macro_rules! call_args {
875 ($($value:expr),*) => {
876 Ok::<_, anyhow::Error>(vec![$(sui_json::call_arg!($value)?,)*])
877 };
878 }
879
880#[macro_export]
881macro_rules! call_arg {
882 ($value:expr) => {{
883 use sui_json::SuiJsonValue;
884 trait SuiJsonArg {
885 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue>;
886 }
887 impl SuiJsonArg for &str {
889 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
890 SuiJsonValue::from_str(self)
891 }
892 }
893 impl SuiJsonArg for String {
894 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
895 SuiJsonValue::from_str(&self)
896 }
897 }
898 impl SuiJsonArg for sui_types::base_types::ObjectID {
899 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
900 SuiJsonValue::from_str(&self.to_string())
901 }
902 }
903 impl SuiJsonArg for sui_types::base_types::SuiAddress {
904 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
905 SuiJsonValue::from_str(&self.to_string())
906 }
907 }
908 impl SuiJsonArg for u64 {
909 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
910 SuiJsonValue::from_bcs_bytes(
911 Some(&sui_json::MoveTypeLayout::U64),
912 &bcs::to_bytes(self)?,
913 )
914 }
915 }
916 impl SuiJsonArg for Vec<u8> {
917 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
918 SuiJsonValue::from_bcs_bytes(None, &self)
919 }
920 }
921 impl SuiJsonArg for &[u8] {
922 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
923 SuiJsonValue::from_bcs_bytes(None, self)
924 }
925 }
926 $value.to_sui_json()
927 }};
928}
929
930#[macro_export]
931macro_rules! type_args {
932 ($($value:expr), *) => {{
933 use sui_json_rpc_types::SuiTypeTag;
934 use sui_types::TypeTag;
935 trait SuiJsonTypeArg {
936 fn to_sui_json(&self) -> anyhow::Result<SuiTypeTag>;
937 }
938 impl <T: core::fmt::Display> SuiJsonTypeArg for T {
939 fn to_sui_json(&self) -> anyhow::Result<SuiTypeTag> {
940 Ok(sui_types::parse_sui_type_tag(&self.to_string())?.into())
941 }
942 }
943 Ok::<_, anyhow::Error>(vec![$($value.to_sui_json()?,)*])
944 }};
945 }