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