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 SignatureToken::Vector(inner) => {
579 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?))
580 }
581 SignatureToken::Datatype(struct_handle_idx) => {
582 let resolved_struct = resolve_struct(view, *struct_handle_idx);
583 if resolved_struct == RESOLVED_ASCII_STR {
584 MoveTypeLayout::Struct(Box::new(move_ascii_str_layout()))
585 } else if resolved_struct == RESOLVED_UTF8_STR {
586 MoveTypeLayout::Struct(Box::new(move_utf8_str_layout()))
588 } else if resolved_struct == RESOLVED_SUI_ID {
589 MoveTypeLayout::Struct(Box::new(id::ID::layout()))
590 } else {
591 return None;
592 }
593 }
594 SignatureToken::DatatypeInstantiation(struct_inst) => {
595 let (idx, targs) = &**struct_inst;
596 let resolved_struct = resolve_struct(view, *idx);
597 if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 {
599 MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?))
601 } else {
602 return None;
603 }
604 }
605 SignatureToken::TypeParameter(idx) => {
606 layout_of_primitive_typetag(type_args.get(*idx as usize)?)?
607 }
608 SignatureToken::Signer
609 | SignatureToken::Reference(_)
610 | SignatureToken::MutableReference(_) => return None,
611 })
612}
613
614fn layout_of_primitive_typetag(tag: &TypeTag) -> Option<MoveTypeLayout> {
615 use MoveTypeLayout as MTL;
616 if !is_primitive_type_tag(tag) {
617 return None;
618 }
619
620 Some(match tag {
621 TypeTag::Bool => MTL::Bool,
622 TypeTag::U8 => MTL::U8,
623 TypeTag::U16 => MTL::U16,
624 TypeTag::U32 => MTL::U32,
625 TypeTag::U64 => MTL::U64,
626 TypeTag::U128 => MTL::U128,
627 TypeTag::U256 => MTL::U256,
628 TypeTag::Address => MTL::Address,
629 TypeTag::Signer => return None,
630 TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)),
631 TypeTag::Struct(stag) => {
632 let StructTag {
633 address,
634 module,
635 name,
636 type_params: type_args,
637 } = &**stag;
638 let resolved_struct = (address, module.as_ident_str(), name.as_ident_str());
639 if resolved_struct == RESOLVED_SUI_ID {
641 MTL::Struct(Box::new(id::ID::layout()))
642 } else if resolved_struct == RESOLVED_ASCII_STR {
643 MTL::Struct(Box::new(move_ascii_str_layout()))
644 } else if resolved_struct == RESOLVED_UTF8_STR {
645 MTL::Struct(Box::new(move_utf8_str_layout()))
646 } else if resolved_struct == RESOLVED_STD_OPTION && type_args.len() == 1
648 && is_primitive_type_tag(&type_args[0])
649 {
650 MTL::Vector(Box::new(
651 layout_of_primitive_typetag(&type_args[0]).unwrap(),
652 ))
653 } else {
654 return None;
655 }
656 }
657 })
658}
659
660fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result<ObjectID, anyhow::Error> {
661 match arg {
663 JsonValue::String(s) => {
664 let s = s.trim().to_lowercase();
665 if !s.starts_with(HEX_PREFIX) {
666 bail!("ObjectID hex string must start with 0x.",);
667 }
668 Ok(ObjectID::from_hex_literal(&s)?)
669 }
670 _ => bail!(
671 "Unable to parse arg {:?} as ObjectID at pos {}. Expected {:?}-byte hex string \
672 prefixed with 0x.",
673 arg,
674 idx,
675 ObjectID::LENGTH,
676 ),
677 }
678}
679
680fn resolve_object_vec_arg(idx: usize, arg: &SuiJsonValue) -> Result<Vec<ObjectID>, anyhow::Error> {
681 match arg.to_json_value() {
683 JsonValue::Array(a) => {
684 let mut object_ids = vec![];
685 for id in a {
686 object_ids.push(resolve_object_arg(idx, &id)?);
687 }
688 Ok(object_ids)
689 }
690 JsonValue::String(s) if s.starts_with('[') && s.ends_with(']') => {
691 let mut object_ids = vec![];
695 for tok in s[1..s.len() - 1].split(',') {
696 let id = JsonValue::String(tok.to_string());
697 object_ids.push(resolve_object_arg(idx, &id)?);
698 }
699 Ok(object_ids)
700 }
701 _ => bail!(
702 "Unable to parse arg {:?} as vector of ObjectIDs at pos {}. \
703 Expected a vector of {:?}-byte hex strings prefixed with 0x.\n\
704 Consider escaping your curly braces with a backslash (as in \\[0x42,0x7\\]) \
705 or enclosing the whole vector in single quotes (as in '[0x42,0x7]')",
706 arg.to_json_value(),
707 idx,
708 ObjectID::LENGTH,
709 ),
710 }
711}
712
713fn resolve_call_arg(
714 view: &CompiledModule,
715 type_args: &[TypeTag],
716 idx: usize,
717 arg: &SuiJsonValue,
718 param: &SignatureToken,
719) -> Result<ResolvedCallArg, anyhow::Error> {
720 if let Some(layout) = primitive_type(view, type_args, param) {
721 return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err(
722 |e| {
723 anyhow!(
724 "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}",
725 param,
726 idx,
727 layout,
728 e
729 )
730 },
731 )?));
732 }
733
734 match param {
737 SignatureToken::Datatype(_)
738 | SignatureToken::DatatypeInstantiation(_)
739 | SignatureToken::TypeParameter(_)
740 | SignatureToken::Reference(_)
741 | SignatureToken::MutableReference(_) => Ok(ResolvedCallArg::Object(resolve_object_arg(
742 idx,
743 &arg.to_json_value(),
744 )?)),
745 SignatureToken::Vector(inner) => match &**inner {
746 SignatureToken::Datatype(_) | SignatureToken::DatatypeInstantiation(_) => {
747 Ok(ResolvedCallArg::ObjVec(resolve_object_vec_arg(idx, arg)?))
748 }
749 _ => {
750 bail!(
751 "Unexpected non-primitive vector arg {:?} at {} with value {:?}",
752 param,
753 idx,
754 arg
755 );
756 }
757 },
758 _ => bail!(
759 "Unexpected non-primitive arg {:?} at {} with value {:?}",
760 param,
761 idx,
762 arg
763 ),
764 }
765}
766
767pub fn is_receiving_argument(view: &CompiledModule, arg_type: &SignatureToken) -> bool {
768 use SignatureToken as ST;
769
770 let mut token = arg_type;
773 while let ST::Reference(inner) | ST::MutableReference(inner) = token {
774 token = inner;
775 }
776
777 matches!(
778 token,
779 ST::DatatypeInstantiation(inst) if resolve_struct(view, inst.0) == RESOLVED_RECEIVING_STRUCT && inst.1.len() == 1
780 )
781}
782
783fn resolve_call_args(
784 view: &CompiledModule,
785 type_args: &[TypeTag],
786 json_args: &[SuiJsonValue],
787 parameter_types: &[SignatureToken],
788) -> Result<Vec<ResolvedCallArg>, anyhow::Error> {
789 json_args
790 .iter()
791 .zip(parameter_types)
792 .enumerate()
793 .map(|(idx, (arg, param))| resolve_call_arg(view, type_args, idx, arg, param))
794 .collect()
795}
796
797pub fn resolve_move_function_args(
800 package: &MovePackage,
801 module_ident: Identifier,
802 function: Identifier,
803 type_args: &[TypeTag],
804 combined_args_json: Vec<SuiJsonValue>,
805) -> Result<Vec<(ResolvedCallArg, SignatureToken)>, anyhow::Error> {
806 let module = package.deserialize_module(&module_ident, &BinaryConfig::standard())?;
808 let function_str = function.as_ident_str();
809 let fdef = module
810 .function_defs
811 .iter()
812 .find(|fdef| {
813 module.identifier_at(module.function_handle_at(fdef.function).name) == function_str
814 })
815 .ok_or_else(|| {
816 anyhow!(
817 "Could not resolve function {} in module {}",
818 function,
819 module_ident
820 )
821 })?;
822 let function_signature = module.function_handle_at(fdef.function);
823 let parameters = &module.signature_at(function_signature.parameters).0;
824
825 let expected_len = match parameters.last() {
827 Some(param) if TxContext::kind(&module, param) != TxContextKind::None => {
828 parameters.len() - 1
829 }
830 _ => parameters.len(),
831 };
832 if combined_args_json.len() != expected_len {
833 bail!(
834 "Expected {} args, found {}",
835 expected_len,
836 combined_args_json.len()
837 );
838 }
839 let call_args = resolve_call_args(&module, type_args, &combined_args_json, parameters)?;
841 let tupled_call_args = call_args
842 .into_iter()
843 .zip(parameters.iter())
844 .map(|(arg, expected_type)| (arg, expected_type.clone()))
845 .collect::<Vec<_>>();
846 Ok(tupled_call_args)
847}
848
849fn convert_string_to_u256(s: &str) -> Result<U256, anyhow::Error> {
850 if let Ok(v) = s.parse::<U256>() {
852 return Ok(v);
853 }
854
855 let s = s.trim().to_lowercase();
860 if !s.starts_with(HEX_PREFIX) {
861 bail!("Unable to convert {s} to unsigned int.",);
862 }
863 U256::from_str_radix(s.trim_start_matches(HEX_PREFIX), 16).map_err(|e| e.into())
864}
865
866#[macro_export]
867macro_rules! call_args {
868 ($($value:expr),*) => {
869 Ok::<_, anyhow::Error>(vec![$(sui_json::call_arg!($value)?,)*])
870 };
871 }
872
873#[macro_export]
874macro_rules! call_arg {
875 ($value:expr) => {{
876 use sui_json::SuiJsonValue;
877 trait SuiJsonArg {
878 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue>;
879 }
880 impl SuiJsonArg for &str {
882 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
883 SuiJsonValue::from_str(self)
884 }
885 }
886 impl SuiJsonArg for String {
887 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
888 SuiJsonValue::from_str(&self)
889 }
890 }
891 impl SuiJsonArg for sui_types::base_types::ObjectID {
892 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
893 SuiJsonValue::from_str(&self.to_string())
894 }
895 }
896 impl SuiJsonArg for sui_types::base_types::SuiAddress {
897 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
898 SuiJsonValue::from_str(&self.to_string())
899 }
900 }
901 impl SuiJsonArg for u64 {
902 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
903 SuiJsonValue::from_bcs_bytes(
904 Some(&sui_json::MoveTypeLayout::U64),
905 &bcs::to_bytes(self)?,
906 )
907 }
908 }
909 impl SuiJsonArg for Vec<u8> {
910 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
911 SuiJsonValue::from_bcs_bytes(None, &self)
912 }
913 }
914 impl SuiJsonArg for &[u8] {
915 fn to_sui_json(&self) -> anyhow::Result<SuiJsonValue> {
916 SuiJsonValue::from_bcs_bytes(None, self)
917 }
918 }
919 $value.to_sui_json()
920 }};
921}
922
923#[macro_export]
924macro_rules! type_args {
925 ($($value:expr), *) => {{
926 use sui_json_rpc_types::SuiTypeTag;
927 use sui_types::TypeTag;
928 trait SuiJsonTypeArg {
929 fn to_sui_json(&self) -> anyhow::Result<SuiTypeTag>;
930 }
931 impl <T: core::fmt::Display> SuiJsonTypeArg for T {
932 fn to_sui_json(&self) -> anyhow::Result<SuiTypeTag> {
933 Ok(sui_types::parse_sui_type_tag(&self.to_string())?.into())
934 }
935 }
936 Ok::<_, anyhow::Error>(vec![$($value.to_sui_json()?,)*])
937 }};
938 }