sui_rpc/proto/sui/rpc/v2/
object.rs

1use super::*;
2use crate::field::FieldMaskTree;
3use crate::merge::Merge;
4use crate::proto::TryFromProtoError;
5use tap::Pipe;
6
7//
8// Object
9//
10
11pub const PACKAGE_TYPE: &str = "package";
12
13impl From<sui_sdk_types::Object> for Object {
14    fn from(value: sui_sdk_types::Object) -> Self {
15        Self::merge_from(value, &FieldMaskTree::new_wildcard())
16    }
17}
18
19impl Merge<&Object> for Object {
20    fn merge(&mut self, source: &Object, mask: &FieldMaskTree) {
21        let Object {
22            bcs,
23            object_id,
24            version,
25            digest,
26            owner,
27            object_type,
28            has_public_transfer,
29            contents,
30            package,
31            previous_transaction,
32            storage_rebate,
33            json,
34            balance,
35            display,
36        } = source;
37
38        if mask.contains(Self::BCS_FIELD.name) {
39            self.bcs = bcs.clone();
40        }
41
42        if mask.contains(Self::DIGEST_FIELD.name) {
43            self.digest = digest.clone();
44        }
45
46        if mask.contains(Self::OBJECT_ID_FIELD.name) {
47            self.object_id = object_id.clone();
48        }
49
50        if mask.contains(Self::VERSION_FIELD.name) {
51            self.version = *version;
52        }
53
54        if mask.contains(Self::OWNER_FIELD.name) {
55            self.owner = owner.clone();
56        }
57
58        if mask.contains(Self::PREVIOUS_TRANSACTION_FIELD.name) {
59            self.previous_transaction = previous_transaction.clone();
60        }
61
62        if mask.contains(Self::STORAGE_REBATE_FIELD.name) {
63            self.storage_rebate = *storage_rebate;
64        }
65
66        if mask.contains(Self::OBJECT_TYPE_FIELD.name) {
67            self.object_type = object_type.clone();
68        }
69
70        if mask.contains(Self::HAS_PUBLIC_TRANSFER_FIELD.name) {
71            self.has_public_transfer = *has_public_transfer;
72        }
73
74        if mask.contains(Self::CONTENTS_FIELD.name) {
75            self.contents = contents.clone();
76        }
77
78        if mask.contains(Self::PACKAGE_FIELD.name) {
79            self.package = package.clone();
80        }
81
82        if mask.contains(Self::JSON_FIELD.name) {
83            self.json = json.clone();
84        }
85
86        if mask.contains(Self::BALANCE_FIELD) {
87            self.balance = *balance;
88        }
89
90        if mask.contains(Self::DIGEST_FIELD) {
91            self.display = display.clone();
92        }
93    }
94}
95
96impl Merge<sui_sdk_types::Object> for Object {
97    fn merge(&mut self, source: sui_sdk_types::Object, mask: &FieldMaskTree) {
98        if mask.contains(Self::BCS_FIELD.name) {
99            let mut bcs = Bcs::serialize(&source).unwrap();
100            bcs.name = Some("Object".to_owned());
101            self.bcs = Some(bcs);
102        }
103
104        if mask.contains(Self::DIGEST_FIELD.name) {
105            self.digest = Some(source.digest().to_string());
106        }
107
108        if mask.contains(Self::OBJECT_ID_FIELD.name) {
109            self.object_id = Some(source.object_id().to_string());
110        }
111
112        if mask.contains(Self::VERSION_FIELD.name) {
113            self.version = Some(source.version());
114        }
115
116        if mask.contains(Self::OWNER_FIELD.name) {
117            self.owner = Some(source.owner().to_owned().into());
118        }
119
120        if mask.contains(Self::PREVIOUS_TRANSACTION_FIELD.name) {
121            self.previous_transaction = Some(source.previous_transaction().to_string());
122        }
123
124        if mask.contains(Self::STORAGE_REBATE_FIELD.name) {
125            self.storage_rebate = Some(source.storage_rebate());
126        }
127
128        match source.data() {
129            sui_sdk_types::ObjectData::Struct(move_struct) => {
130                self.merge(move_struct, mask);
131            }
132            sui_sdk_types::ObjectData::Package(move_package) => {
133                self.merge(move_package, mask);
134            }
135        }
136    }
137}
138
139impl Merge<&sui_sdk_types::MoveStruct> for Object {
140    fn merge(&mut self, source: &sui_sdk_types::MoveStruct, mask: &FieldMaskTree) {
141        if mask.contains(Self::OBJECT_TYPE_FIELD.name) {
142            self.object_type = Some(source.object_type().to_string());
143        }
144
145        if mask.contains(Self::HAS_PUBLIC_TRANSFER_FIELD.name) {
146            self.has_public_transfer = Some(source.has_public_transfer());
147        }
148
149        if mask.contains(Self::CONTENTS_FIELD.name) {
150            self.contents = Some(Bcs {
151                name: Some(source.object_type().to_string()),
152                value: Some(source.contents().to_vec().into()),
153            });
154        }
155    }
156}
157
158impl Merge<&sui_sdk_types::MovePackage> for Object {
159    fn merge(&mut self, source: &sui_sdk_types::MovePackage, mask: &FieldMaskTree) {
160        if mask.contains(Self::OBJECT_TYPE_FIELD.name) {
161            self.object_type = Some(PACKAGE_TYPE.to_owned());
162        }
163
164        if mask.contains(Self::PACKAGE_FIELD.name) {
165            self.package = Some(Package {
166                modules: source
167                    .modules
168                    .iter()
169                    .map(|(name, contents)| Module {
170                        name: Some(name.to_string()),
171                        contents: Some(contents.clone().into()),
172                        ..Default::default()
173                    })
174                    .collect(),
175                type_origins: source
176                    .type_origin_table
177                    .clone()
178                    .into_iter()
179                    .map(Into::into)
180                    .collect(),
181                linkage: source
182                    .linkage_table
183                    .iter()
184                    .map(
185                        |(
186                            original_id,
187                            sui_sdk_types::UpgradeInfo {
188                                upgraded_id,
189                                upgraded_version,
190                            },
191                        )| {
192                            Linkage {
193                                original_id: Some(original_id.to_string()),
194                                upgraded_id: Some(upgraded_id.to_string()),
195                                upgraded_version: Some(*upgraded_version),
196                            }
197                        },
198                    )
199                    .collect(),
200
201                ..Default::default()
202            })
203        }
204    }
205}
206
207#[allow(clippy::result_large_err)]
208fn try_extract_struct(value: &Object) -> Result<sui_sdk_types::MoveStruct, TryFromProtoError> {
209    let version = value
210        .version
211        .ok_or_else(|| TryFromProtoError::missing("version"))?;
212
213    let object_type = value
214        .object_type()
215        .parse()
216        .map_err(|e| TryFromProtoError::invalid(Object::OBJECT_TYPE_FIELD, e))?;
217
218    let has_public_transfer = value
219        .has_public_transfer
220        .ok_or_else(|| TryFromProtoError::missing("has_public_transfer"))?;
221    let contents = value
222        .contents
223        .as_ref()
224        .ok_or_else(|| TryFromProtoError::missing("contents"))?
225        .value()
226        .to_vec();
227
228    sui_sdk_types::MoveStruct::new(object_type, has_public_transfer, version, contents).ok_or_else(
229        || TryFromProtoError::invalid(Object::CONTENTS_FIELD, "contents missing object_id"),
230    )
231}
232
233#[allow(clippy::result_large_err)]
234fn try_extract_package(value: &Object) -> Result<sui_sdk_types::MovePackage, TryFromProtoError> {
235    if value.object_type() != PACKAGE_TYPE {
236        return Err(TryFromProtoError::invalid(
237            Object::OBJECT_TYPE_FIELD,
238            format!(
239                "expected type {}, found {}",
240                PACKAGE_TYPE,
241                value.object_type()
242            ),
243        ));
244    }
245
246    let version = value
247        .version
248        .ok_or_else(|| TryFromProtoError::missing("version"))?;
249    let id = value
250        .object_id
251        .as_ref()
252        .ok_or_else(|| TryFromProtoError::missing("object_id"))?
253        .parse()
254        .map_err(|e| TryFromProtoError::invalid(Object::OBJECT_ID_FIELD, e))?;
255
256    let package = value
257        .package
258        .as_ref()
259        .ok_or_else(|| TryFromProtoError::missing("package"))?;
260    let modules = package
261        .modules
262        .iter()
263        .map(|module| {
264            let name = module
265                .name
266                .as_ref()
267                .ok_or_else(|| TryFromProtoError::missing("name"))?
268                .parse()
269                .map_err(|e| TryFromProtoError::invalid(Module::NAME_FIELD, e))?;
270
271            let contents = module
272                .contents
273                .as_ref()
274                .ok_or_else(|| TryFromProtoError::missing("contents"))?
275                .to_vec();
276
277            Ok((name, contents))
278        })
279        .collect::<Result<_, TryFromProtoError>>()?;
280
281    let type_origin_table = package
282        .type_origins
283        .iter()
284        .map(TryInto::try_into)
285        .collect::<Result<_, _>>()?;
286
287    let linkage_table = package
288        .linkage
289        .iter()
290        .map(|upgrade_info| {
291            let original_id = upgrade_info
292                .original_id
293                .as_ref()
294                .ok_or_else(|| TryFromProtoError::missing("original_id"))?
295                .parse()
296                .map_err(|e| TryFromProtoError::invalid(Linkage::ORIGINAL_ID_FIELD, e))?;
297
298            let upgraded_id = upgrade_info
299                .upgraded_id
300                .as_ref()
301                .ok_or_else(|| TryFromProtoError::missing("upgraded_id"))?
302                .parse()
303                .map_err(|e| TryFromProtoError::invalid(Linkage::UPGRADED_ID_FIELD, e))?;
304            let upgraded_version = upgrade_info
305                .upgraded_version
306                .ok_or_else(|| TryFromProtoError::missing("upgraded_version"))?;
307
308            Ok((
309                original_id,
310                sui_sdk_types::UpgradeInfo {
311                    upgraded_id,
312                    upgraded_version,
313                },
314            ))
315        })
316        .collect::<Result<_, TryFromProtoError>>()?;
317
318    Ok(sui_sdk_types::MovePackage {
319        id,
320        version,
321        modules,
322        type_origin_table,
323        linkage_table,
324    })
325}
326
327impl TryFrom<&Object> for sui_sdk_types::Object {
328    type Error = TryFromProtoError;
329
330    fn try_from(value: &Object) -> Result<Self, Self::Error> {
331        let owner = value
332            .owner
333            .as_ref()
334            .ok_or_else(|| TryFromProtoError::missing("owner"))?
335            .try_into()?;
336
337        let previous_transaction = value
338            .previous_transaction
339            .as_ref()
340            .ok_or_else(|| TryFromProtoError::missing("previous_transaction"))?
341            .parse()
342            .map_err(|e| TryFromProtoError::invalid(Object::PREVIOUS_TRANSACTION_FIELD, e))?;
343        let storage_rebate = value
344            .storage_rebate
345            .ok_or_else(|| TryFromProtoError::missing("storage_rebate"))?;
346
347        let object_data = if value.object_type() == PACKAGE_TYPE {
348            // Package
349            sui_sdk_types::ObjectData::Package(try_extract_package(value)?)
350        } else {
351            // Struct
352            sui_sdk_types::ObjectData::Struct(try_extract_struct(value)?)
353        };
354
355        Ok(Self::new(
356            object_data,
357            owner,
358            previous_transaction,
359            storage_rebate,
360        ))
361    }
362}
363
364//
365// TypeOrigin
366//
367
368impl From<sui_sdk_types::TypeOrigin> for TypeOrigin {
369    fn from(value: sui_sdk_types::TypeOrigin) -> Self {
370        Self {
371            module_name: Some(value.module_name.to_string()),
372            datatype_name: Some(value.struct_name.to_string()),
373            package_id: Some(value.package.to_string()),
374        }
375    }
376}
377
378impl TryFrom<&TypeOrigin> for sui_sdk_types::TypeOrigin {
379    type Error = TryFromProtoError;
380
381    fn try_from(value: &TypeOrigin) -> Result<Self, Self::Error> {
382        let module_name = value
383            .module_name
384            .as_ref()
385            .ok_or_else(|| TryFromProtoError::missing("module_name"))?
386            .parse()
387            .map_err(|e| TryFromProtoError::invalid(TypeOrigin::MODULE_NAME_FIELD, e))?;
388
389        let struct_name = value
390            .datatype_name
391            .as_ref()
392            .ok_or_else(|| TryFromProtoError::missing("datatype_name"))?
393            .parse()
394            .map_err(|e| TryFromProtoError::invalid(TypeOrigin::DATATYPE_NAME_FIELD, e))?;
395
396        let package = value
397            .package_id
398            .as_ref()
399            .ok_or_else(|| TryFromProtoError::missing("package_id"))?
400            .parse()
401            .map_err(|e| TryFromProtoError::invalid(TypeOrigin::PACKAGE_ID_FIELD, e))?;
402
403        Ok(Self {
404            module_name,
405            struct_name,
406            package,
407        })
408    }
409}
410
411//
412// GenesisObject
413//
414
415impl From<sui_sdk_types::GenesisObject> for Object {
416    fn from(value: sui_sdk_types::GenesisObject) -> Self {
417        let mut message = Self {
418            object_id: Some(value.object_id().to_string()),
419            version: Some(value.version()),
420            owner: Some(value.owner().to_owned().into()),
421            ..Default::default()
422        };
423
424        match value.data() {
425            sui_sdk_types::ObjectData::Struct(move_struct) => {
426                message.merge(move_struct, &FieldMaskTree::new_wildcard());
427            }
428            sui_sdk_types::ObjectData::Package(move_package) => {
429                message.merge(move_package, &FieldMaskTree::new_wildcard());
430            }
431        }
432
433        message
434    }
435}
436
437impl TryFrom<&Object> for sui_sdk_types::GenesisObject {
438    type Error = TryFromProtoError;
439
440    fn try_from(value: &Object) -> Result<Self, Self::Error> {
441        let object_data = if value.object_type() == PACKAGE_TYPE {
442            // Package
443            sui_sdk_types::ObjectData::Package(try_extract_package(value)?)
444        } else {
445            // Struct
446            sui_sdk_types::ObjectData::Struct(try_extract_struct(value)?)
447        };
448
449        let owner = value
450            .owner
451            .as_ref()
452            .ok_or_else(|| TryFromProtoError::missing("owner"))?
453            .try_into()?;
454
455        Ok(Self::new(object_data, owner))
456    }
457}
458
459impl Object {
460    pub fn object_reference(&self) -> ObjectReference {
461        ObjectReference {
462            object_id: self.object_id.clone(),
463            version: self.version,
464            digest: self.digest.clone(),
465        }
466    }
467}
468
469//
470// ObjectReference
471//
472
473impl From<sui_sdk_types::ObjectReference> for ObjectReference {
474    fn from(value: sui_sdk_types::ObjectReference) -> Self {
475        let (object_id, version, digest) = value.into_parts();
476        Self {
477            object_id: Some(object_id.to_string()),
478            version: Some(version),
479            digest: Some(digest.to_string()),
480        }
481    }
482}
483
484impl TryFrom<&ObjectReference> for sui_sdk_types::ObjectReference {
485    type Error = TryFromProtoError;
486
487    fn try_from(value: &ObjectReference) -> Result<Self, Self::Error> {
488        let object_id = value
489            .object_id
490            .as_ref()
491            .ok_or_else(|| TryFromProtoError::missing("object_id"))?
492            .parse()
493            .map_err(|e| TryFromProtoError::invalid(ObjectReference::OBJECT_ID_FIELD, e))?;
494
495        let version = value
496            .version
497            .ok_or_else(|| TryFromProtoError::missing("version"))?;
498
499        let digest = value
500            .digest
501            .as_ref()
502            .ok_or_else(|| TryFromProtoError::missing("digest"))?
503            .parse()
504            .map_err(|e| TryFromProtoError::invalid(ObjectReference::DIGEST_FIELD, e))?;
505
506        Ok(Self::new(object_id, version, digest))
507    }
508}
509
510//
511// Owner
512//
513
514impl From<sui_sdk_types::Owner> for Owner {
515    fn from(value: sui_sdk_types::Owner) -> Self {
516        use owner::OwnerKind;
517        use sui_sdk_types::Owner::*;
518
519        let mut message = Self::default();
520
521        let kind = match value {
522            Address(address) => {
523                message.address = Some(address.to_string());
524                OwnerKind::Address
525            }
526            Object(object) => {
527                message.address = Some(object.to_string());
528                OwnerKind::Object
529            }
530            Shared(version) => {
531                message.version = Some(version);
532                OwnerKind::Shared
533            }
534            Immutable => OwnerKind::Immutable,
535            ConsensusAddress {
536                start_version,
537                owner,
538            } => {
539                message.version = Some(start_version);
540                message.address = Some(owner.to_string());
541                OwnerKind::ConsensusAddress
542            }
543            _ => OwnerKind::Unknown,
544        };
545
546        message.set_kind(kind);
547        message
548    }
549}
550
551impl TryFrom<&Owner> for sui_sdk_types::Owner {
552    type Error = TryFromProtoError;
553
554    fn try_from(value: &Owner) -> Result<Self, Self::Error> {
555        use owner::OwnerKind;
556
557        match value.kind() {
558            OwnerKind::Unknown => {
559                return Err(TryFromProtoError::invalid(
560                    Owner::KIND_FIELD,
561                    "unknown OwnerKind",
562                ));
563            }
564            OwnerKind::Address => Self::Address(
565                value
566                    .address()
567                    .parse()
568                    .map_err(|e| TryFromProtoError::invalid(Owner::ADDRESS_FIELD, e))?,
569            ),
570            OwnerKind::Object => Self::Object(
571                value
572                    .address()
573                    .parse()
574                    .map_err(|e| TryFromProtoError::invalid(Owner::ADDRESS_FIELD, e))?,
575            ),
576            OwnerKind::Shared => Self::Shared(value.version()),
577            OwnerKind::Immutable => Self::Immutable,
578            OwnerKind::ConsensusAddress => Self::ConsensusAddress {
579                start_version: value.version(),
580                owner: value
581                    .address()
582                    .parse()
583                    .map_err(|e| TryFromProtoError::invalid(Owner::ADDRESS_FIELD, e))?,
584            },
585        }
586        .pipe(Ok)
587    }
588}
589
590impl Merge<&ObjectSet> for ObjectSet {
591    fn merge(&mut self, source: &ObjectSet, mask: &FieldMaskTree) {
592        if let Some(submask) = mask.subtree(Self::OBJECTS_FIELD) {
593            self.objects = source
594                .objects()
595                .iter()
596                .map(|object| Object::merge_from(object, &submask))
597                .collect();
598        }
599    }
600}
601
602impl ObjectSet {
603    // Sorts the objects in this set by the key `(object_id, version)`
604    #[doc(hidden)]
605    pub fn sort_objects(&mut self) {
606        self.objects_mut().sort_by(|a, b| {
607            let a = (a.object_id(), a.version());
608            let b = (b.object_id(), b.version());
609            a.cmp(&b)
610        });
611    }
612
613    // Performs a binary search on the contained object set searching for the specified
614    // (object_id, version). This function assumes that both the `object_id` and `version` fields
615    // are set for all contained objects.
616    pub fn binary_search<'a>(
617        &'a self,
618        object_id: &sui_sdk_types::Address,
619        version: u64,
620    ) -> Option<&'a Object> {
621        let object_id = object_id.to_string();
622        let seek = (object_id.as_str(), version);
623        self.objects()
624            .binary_search_by(|object| {
625                let probe = (object.object_id(), object.version());
626                probe.cmp(&seek)
627            })
628            .ok()
629            .and_then(|found| self.objects().get(found))
630    }
631}