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

1use super::*;
2use crate::field::FieldMaskTree;
3use crate::merge::Merge;
4use crate::proto::TryFromProtoError;
5use tap::Pipe;
6
7//
8// TransactionEffects
9//
10
11impl From<sui_sdk_types::TransactionEffects> for TransactionEffects {
12    fn from(value: sui_sdk_types::TransactionEffects) -> Self {
13        Self::merge_from(&value, &FieldMaskTree::new_wildcard())
14    }
15}
16
17impl Merge<&sui_sdk_types::TransactionEffects> for TransactionEffects {
18    fn merge(&mut self, source: &sui_sdk_types::TransactionEffects, mask: &FieldMaskTree) {
19        if mask.contains(Self::BCS_FIELD.name) {
20            let mut bcs = Bcs::serialize(&source).unwrap();
21            bcs.name = Some("TransactionEffects".to_owned());
22            self.bcs = Some(bcs);
23        }
24
25        if mask.contains(Self::DIGEST_FIELD.name) {
26            self.digest = Some(source.digest().to_string());
27        }
28
29        match source {
30            sui_sdk_types::TransactionEffects::V1(v1) => self.merge(v1.as_ref(), mask),
31            sui_sdk_types::TransactionEffects::V2(v2) => self.merge(v2.as_ref(), mask),
32        }
33    }
34}
35
36impl Merge<&TransactionEffects> for TransactionEffects {
37    fn merge(
38        &mut self,
39        TransactionEffects {
40            bcs,
41            digest,
42            version,
43            status,
44            epoch,
45            gas_used,
46            transaction_digest,
47            gas_object,
48            events_digest,
49            dependencies,
50            lamport_version,
51            changed_objects,
52            unchanged_consensus_objects,
53            auxiliary_data_digest,
54            unchanged_loaded_runtime_objects,
55        }: &TransactionEffects,
56        mask: &FieldMaskTree,
57    ) {
58        if mask.contains(Self::BCS_FIELD.name) {
59            self.bcs = bcs.clone();
60        }
61
62        if mask.contains(Self::DIGEST_FIELD.name) {
63            self.digest = digest.clone();
64        }
65        if mask.contains(Self::VERSION_FIELD.name) {
66            self.version = *version;
67        }
68
69        if mask.contains(Self::STATUS_FIELD.name) {
70            self.status = status.clone();
71        }
72
73        if mask.contains(Self::EPOCH_FIELD.name) {
74            self.epoch = *epoch;
75        }
76
77        if mask.contains(Self::GAS_USED_FIELD.name) {
78            self.gas_used = *gas_used;
79        }
80
81        if mask.contains(Self::TRANSACTION_DIGEST_FIELD.name) {
82            self.transaction_digest = transaction_digest.clone();
83        }
84
85        if mask.contains(Self::GAS_OBJECT_FIELD.name) {
86            self.gas_object = gas_object.clone();
87        }
88
89        if mask.contains(Self::EVENTS_DIGEST_FIELD.name) {
90            self.events_digest = events_digest.clone();
91        }
92
93        if mask.contains(Self::DEPENDENCIES_FIELD.name) {
94            self.dependencies = dependencies.clone();
95        }
96
97        if mask.contains(Self::LAMPORT_VERSION_FIELD.name) {
98            self.lamport_version = *lamport_version;
99        }
100
101        if mask.contains(Self::CHANGED_OBJECTS_FIELD.name) {
102            self.changed_objects = changed_objects.clone();
103        }
104
105        if mask.contains(Self::UNCHANGED_CONSENSUS_OBJECTS_FIELD.name) {
106            self.unchanged_consensus_objects = unchanged_consensus_objects.clone();
107        }
108
109        if mask.contains(Self::AUXILIARY_DATA_DIGEST_FIELD.name) {
110            self.auxiliary_data_digest = auxiliary_data_digest.clone();
111        }
112
113        if mask.contains(Self::UNCHANGED_LOADED_RUNTIME_OBJECTS_FIELD.name) {
114            self.unchanged_loaded_runtime_objects = unchanged_loaded_runtime_objects.clone();
115        }
116    }
117}
118
119impl TryFrom<&TransactionEffects> for sui_sdk_types::TransactionEffects {
120    type Error = TryFromProtoError;
121
122    fn try_from(value: &TransactionEffects) -> Result<Self, Self::Error> {
123        value
124            .bcs
125            .as_ref()
126            .ok_or_else(|| TryFromProtoError::missing("bcs"))?
127            .deserialize()
128            .map_err(|e| TryFromProtoError::invalid(TransactionEffects::BCS_FIELD, e))
129    }
130}
131
132//
133// TransactionEffectsV1
134//
135
136impl Merge<&sui_sdk_types::TransactionEffectsV1> for TransactionEffects {
137    fn merge(
138        &mut self,
139        sui_sdk_types::TransactionEffectsV1 {
140            status,
141            epoch,
142            gas_used,
143            modified_at_versions,
144            consensus_objects,
145            transaction_digest,
146            created,
147            mutated,
148            unwrapped,
149            deleted,
150            unwrapped_then_deleted,
151            wrapped,
152            gas_object,
153            events_digest,
154            dependencies,
155        }: &sui_sdk_types::TransactionEffectsV1,
156        mask: &FieldMaskTree,
157    ) {
158        if mask.contains(Self::VERSION_FIELD.name) {
159            self.version = Some(1);
160        }
161
162        if mask.contains(Self::STATUS_FIELD.name) {
163            self.status = Some(status.clone().into());
164        }
165
166        if mask.contains(Self::EPOCH_FIELD.name) {
167            self.epoch = Some(*epoch);
168        }
169
170        if mask.contains(Self::GAS_USED_FIELD.name) {
171            self.gas_used = Some(gas_used.clone().into());
172        }
173
174        if mask.contains(Self::TRANSACTION_DIGEST_FIELD.name) {
175            self.transaction_digest = Some(transaction_digest.to_string());
176        }
177
178        if mask.contains(Self::EVENTS_DIGEST_FIELD.name) {
179            self.events_digest = events_digest.map(|d| d.to_string());
180        }
181
182        if mask.contains(Self::DEPENDENCIES_FIELD.name) {
183            self.dependencies = dependencies.iter().map(ToString::to_string).collect();
184        }
185
186        if mask.contains(Self::CHANGED_OBJECTS_FIELD.name)
187            || mask.contains(Self::UNCHANGED_CONSENSUS_OBJECTS_FIELD.name)
188            || mask.contains(Self::GAS_OBJECT_FIELD.name)
189        {
190            let mut changed_objects = Vec::new();
191            let mut unchanged_consensus_objects = Vec::new();
192
193            for object in created {
194                let change = ChangedObject {
195                    object_id: Some(object.reference.object_id().to_string()),
196                    input_state: Some(changed_object::InputObjectState::DoesNotExist.into()),
197                    input_version: None,
198                    input_digest: None,
199                    input_owner: None,
200                    output_state: Some(changed_object::OutputObjectState::ObjectWrite.into()),
201                    output_version: Some(object.reference.version()),
202                    output_digest: Some(object.reference.digest().to_string()),
203                    output_owner: Some(object.owner.into()),
204                    id_operation: Some(changed_object::IdOperation::Created.into()),
205                    object_type: None,
206                    accumulator_write: None,
207                };
208
209                changed_objects.push(change);
210            }
211
212            for object in mutated {
213                let change = ChangedObject {
214                    object_id: Some(object.reference.object_id().to_string()),
215                    input_state: Some(changed_object::InputObjectState::Exists.into()),
216                    input_version: None,
217                    input_digest: None,
218                    input_owner: None,
219                    output_state: Some(changed_object::OutputObjectState::ObjectWrite.into()),
220                    output_version: Some(object.reference.version()),
221                    output_digest: Some(object.reference.digest().to_string()),
222                    output_owner: Some(object.owner.into()),
223                    id_operation: Some(changed_object::IdOperation::None.into()),
224                    object_type: None,
225                    accumulator_write: None,
226                };
227
228                changed_objects.push(change);
229            }
230
231            for object in unwrapped {
232                let change = ChangedObject {
233                    object_id: Some(object.reference.object_id().to_string()),
234                    input_state: Some(changed_object::InputObjectState::DoesNotExist.into()),
235                    input_version: None,
236                    input_digest: None,
237                    input_owner: None,
238                    output_state: Some(changed_object::OutputObjectState::ObjectWrite.into()),
239                    output_version: Some(object.reference.version()),
240                    output_digest: Some(object.reference.digest().to_string()),
241                    output_owner: Some(object.owner.into()),
242                    id_operation: Some(changed_object::IdOperation::None.into()),
243                    object_type: None,
244                    accumulator_write: None,
245                };
246
247                changed_objects.push(change);
248            }
249
250            for object in deleted {
251                let change = ChangedObject {
252                    object_id: Some(object.object_id().to_string()),
253                    input_state: Some(changed_object::InputObjectState::Exists.into()),
254                    input_version: None,
255                    input_digest: None,
256                    input_owner: None,
257                    output_state: Some(changed_object::OutputObjectState::DoesNotExist.into()),
258                    output_version: Some(object.version()),
259                    output_digest: Some(object.digest().to_string()),
260                    output_owner: None,
261                    id_operation: Some(changed_object::IdOperation::Deleted.into()),
262                    object_type: None,
263                    accumulator_write: None,
264                };
265
266                changed_objects.push(change);
267            }
268
269            for object in unwrapped_then_deleted {
270                let change = ChangedObject {
271                    object_id: Some(object.object_id().to_string()),
272                    input_state: Some(changed_object::InputObjectState::DoesNotExist.into()),
273                    input_version: None,
274                    input_digest: None,
275                    input_owner: None,
276                    output_state: Some(changed_object::OutputObjectState::DoesNotExist.into()),
277                    output_version: Some(object.version()),
278                    output_digest: Some(object.digest().to_string()),
279                    output_owner: None,
280                    id_operation: Some(changed_object::IdOperation::Deleted.into()),
281                    object_type: None,
282                    accumulator_write: None,
283                };
284
285                changed_objects.push(change);
286            }
287
288            for object in wrapped {
289                let change = ChangedObject {
290                    object_id: Some(object.object_id().to_string()),
291                    input_state: Some(changed_object::InputObjectState::Exists.into()),
292                    input_version: None,
293                    input_digest: None,
294                    input_owner: None,
295                    output_state: Some(changed_object::OutputObjectState::DoesNotExist.into()),
296                    output_version: Some(object.version()),
297                    output_digest: Some(object.digest().to_string()),
298                    output_owner: None,
299                    id_operation: Some(changed_object::IdOperation::Deleted.into()),
300                    object_type: None,
301                    accumulator_write: None,
302                };
303
304                changed_objects.push(change);
305            }
306
307            for modified_at_version in modified_at_versions {
308                let object_id = modified_at_version.object_id.to_string();
309                let version = modified_at_version.version;
310                if let Some(changed_object) = changed_objects
311                    .iter_mut()
312                    .find(|object| object.object_id() == object_id)
313                {
314                    changed_object.input_version = Some(version);
315                }
316            }
317
318            for object in consensus_objects {
319                let object_id = object.object_id().to_string();
320                let version = object.version();
321                let digest = object.digest().to_string();
322
323                if let Some(changed_object) = changed_objects
324                    .iter_mut()
325                    .find(|object| object.object_id() == object_id)
326                {
327                    changed_object.input_version = Some(version);
328                    changed_object.input_digest = Some(digest);
329                } else {
330                    let unchanged_consensus_object = UnchangedConsensusObject {
331                        kind: Some(
332                            unchanged_consensus_object::UnchangedConsensusObjectKind::ReadOnlyRoot
333                                .into(),
334                        ),
335                        object_id: Some(object_id),
336                        version: Some(version),
337                        digest: Some(digest),
338                        object_type: None,
339                    };
340
341                    unchanged_consensus_objects.push(unchanged_consensus_object);
342                }
343            }
344
345            if mask.contains(Self::GAS_OBJECT_FIELD.name) {
346                let gas_object_id = gas_object.reference.object_id().to_string();
347                self.gas_object = changed_objects
348                    .iter()
349                    .find(|object| object.object_id() == gas_object_id)
350                    .cloned();
351            }
352
353            if mask.contains(Self::CHANGED_OBJECTS_FIELD.name) {
354                self.changed_objects = changed_objects;
355            }
356
357            if mask.contains(Self::UNCHANGED_CONSENSUS_OBJECTS_FIELD.name) {
358                self.unchanged_consensus_objects = unchanged_consensus_objects;
359            }
360        }
361    }
362}
363
364//
365// TransactionEffectsV2
366//
367
368impl Merge<&sui_sdk_types::TransactionEffectsV2> for TransactionEffects {
369    fn merge(
370        &mut self,
371        sui_sdk_types::TransactionEffectsV2 {
372            status,
373            epoch,
374            gas_used,
375            transaction_digest,
376            gas_object_index,
377            events_digest,
378            dependencies,
379            lamport_version,
380            changed_objects,
381            unchanged_consensus_objects,
382            auxiliary_data_digest,
383        }: &sui_sdk_types::TransactionEffectsV2,
384        mask: &FieldMaskTree,
385    ) {
386        if mask.contains(Self::VERSION_FIELD.name) {
387            self.version = Some(2);
388        }
389
390        if mask.contains(Self::STATUS_FIELD.name) {
391            self.status = Some(status.clone().into());
392        }
393
394        if mask.contains(Self::EPOCH_FIELD.name) {
395            self.epoch = Some(*epoch);
396        }
397
398        if mask.contains(Self::GAS_USED_FIELD.name) {
399            self.gas_used = Some(gas_used.clone().into());
400        }
401
402        if mask.contains(Self::TRANSACTION_DIGEST_FIELD.name) {
403            self.transaction_digest = Some(transaction_digest.to_string());
404        }
405
406        if mask.contains(Self::GAS_OBJECT_FIELD.name) {
407            self.gas_object = gas_object_index
408                .map(|index| changed_objects.get(index as usize).cloned().map(Into::into))
409                .flatten();
410        }
411
412        if mask.contains(Self::EVENTS_DIGEST_FIELD.name) {
413            self.events_digest = events_digest.map(|d| d.to_string());
414        }
415
416        if mask.contains(Self::DEPENDENCIES_FIELD.name) {
417            self.dependencies = dependencies.iter().map(ToString::to_string).collect();
418        }
419
420        if mask.contains(Self::LAMPORT_VERSION_FIELD.name) {
421            self.lamport_version = Some(*lamport_version);
422        }
423
424        if mask.contains(Self::CHANGED_OBJECTS_FIELD.name) {
425            self.changed_objects = changed_objects
426                .clone()
427                .into_iter()
428                .map(Into::into)
429                .collect();
430        }
431
432        for object in self.changed_objects.iter_mut().chain(&mut self.gas_object) {
433            if object.output_digest.is_some() && object.output_version.is_none() {
434                object.output_version = Some(*lamport_version);
435            }
436        }
437
438        if mask.contains(Self::UNCHANGED_CONSENSUS_OBJECTS_FIELD.name) {
439            self.unchanged_consensus_objects = unchanged_consensus_objects
440                .clone()
441                .into_iter()
442                .map(Into::into)
443                .collect();
444        }
445
446        if mask.contains(Self::AUXILIARY_DATA_DIGEST_FIELD.name) {
447            self.auxiliary_data_digest = auxiliary_data_digest.map(|d| d.to_string());
448        }
449    }
450}
451
452//
453// ChangedObject
454//
455
456impl From<sui_sdk_types::ChangedObject> for ChangedObject {
457    fn from(value: sui_sdk_types::ChangedObject) -> Self {
458        use changed_object::InputObjectState;
459        use changed_object::OutputObjectState;
460
461        let mut message = Self {
462            object_id: Some(value.object_id.to_string()),
463            ..Default::default()
464        };
465
466        // Input State
467        let input_state = match value.input_state {
468            sui_sdk_types::ObjectIn::NotExist => InputObjectState::DoesNotExist,
469            sui_sdk_types::ObjectIn::Exist {
470                version,
471                digest,
472                owner,
473            } => {
474                message.input_version = Some(version);
475                message.input_digest = Some(digest.to_string());
476                message.input_owner = Some(owner.into());
477                InputObjectState::Exists
478            }
479            _ => InputObjectState::Unknown,
480        };
481        message.set_input_state(input_state);
482
483        // Output State
484        let output_state = match value.output_state {
485            sui_sdk_types::ObjectOut::NotExist => OutputObjectState::DoesNotExist,
486            sui_sdk_types::ObjectOut::ObjectWrite { digest, owner } => {
487                message.output_digest = Some(digest.to_string());
488                message.output_owner = Some(owner.into());
489                OutputObjectState::ObjectWrite
490            }
491            sui_sdk_types::ObjectOut::PackageWrite { version, digest } => {
492                message.output_version = Some(version);
493                message.output_digest = Some(digest.to_string());
494                OutputObjectState::PackageWrite
495            }
496            sui_sdk_types::ObjectOut::AccumulatorWrite(accumulator_write) => {
497                message.set_accumulator_write(accumulator_write);
498                OutputObjectState::AccumulatorWrite
499            }
500            _ => OutputObjectState::Unknown,
501        };
502        message.set_output_state(output_state);
503
504        message.set_id_operation(value.id_operation.into());
505        message
506    }
507}
508
509impl TryFrom<&ChangedObject> for sui_sdk_types::ChangedObject {
510    type Error = TryFromProtoError;
511
512    fn try_from(value: &ChangedObject) -> Result<Self, Self::Error> {
513        use changed_object::InputObjectState;
514        use changed_object::OutputObjectState;
515
516        let object_id = value
517            .object_id
518            .as_ref()
519            .ok_or_else(|| TryFromProtoError::missing("object_id"))?
520            .parse()
521            .map_err(|e| TryFromProtoError::invalid(ChangedObject::OBJECT_ID_FIELD, e))?;
522
523        let input_state = match value.input_state() {
524            InputObjectState::Unknown => {
525                return Err(TryFromProtoError::invalid(
526                    ChangedObject::INPUT_STATE_FIELD,
527                    "unknown InputObjectState",
528                ));
529            }
530            InputObjectState::DoesNotExist => sui_sdk_types::ObjectIn::NotExist,
531            InputObjectState::Exists => sui_sdk_types::ObjectIn::Exist {
532                version: value
533                    .input_version
534                    .ok_or_else(|| TryFromProtoError::missing("version"))?,
535                digest: value
536                    .input_digest
537                    .as_ref()
538                    .ok_or_else(|| TryFromProtoError::missing("digest"))?
539                    .parse()
540                    .map_err(|e| {
541                        TryFromProtoError::invalid(ChangedObject::INPUT_DIGEST_FIELD, e)
542                    })?,
543                owner: value
544                    .input_owner
545                    .as_ref()
546                    .ok_or_else(|| TryFromProtoError::missing("owner"))?
547                    .try_into()?,
548            },
549        };
550
551        let output_state = match value.output_state() {
552            OutputObjectState::Unknown => {
553                return Err(TryFromProtoError::invalid(
554                    ChangedObject::OUTPUT_STATE_FIELD,
555                    "unknown OutputObjectState",
556                ));
557            }
558            OutputObjectState::DoesNotExist => sui_sdk_types::ObjectOut::NotExist,
559            OutputObjectState::ObjectWrite => sui_sdk_types::ObjectOut::ObjectWrite {
560                digest: value
561                    .output_digest
562                    .as_ref()
563                    .ok_or_else(|| TryFromProtoError::missing("digest"))?
564                    .parse()
565                    .map_err(|e| {
566                        TryFromProtoError::invalid(ChangedObject::OUTPUT_DIGEST_FIELD, e)
567                    })?,
568
569                owner: value
570                    .output_owner
571                    .as_ref()
572                    .ok_or_else(|| TryFromProtoError::missing("owner"))?
573                    .try_into()?,
574            },
575            OutputObjectState::PackageWrite => sui_sdk_types::ObjectOut::PackageWrite {
576                version: value
577                    .output_version
578                    .ok_or_else(|| TryFromProtoError::missing("version"))?,
579                digest: value
580                    .output_digest
581                    .as_ref()
582                    .ok_or_else(|| TryFromProtoError::missing("digest"))?
583                    .parse()
584                    .map_err(|e| {
585                        TryFromProtoError::invalid(ChangedObject::OUTPUT_DIGEST_FIELD, e)
586                    })?,
587            },
588            OutputObjectState::AccumulatorWrite => sui_sdk_types::ObjectOut::AccumulatorWrite(
589                value
590                    .accumulator_write_opt()
591                    .ok_or_else(|| TryFromProtoError::missing("accumulator_write"))?
592                    .try_into()?,
593            ),
594        };
595
596        let id_operation = value.id_operation().try_into()?;
597
598        Ok(Self {
599            object_id,
600            input_state,
601            output_state,
602            id_operation,
603        })
604    }
605}
606
607//
608// IdOperation
609//
610
611impl From<sui_sdk_types::IdOperation> for changed_object::IdOperation {
612    fn from(value: sui_sdk_types::IdOperation) -> Self {
613        use sui_sdk_types::IdOperation::*;
614
615        match value {
616            None => Self::None,
617            Created => Self::Created,
618            Deleted => Self::Deleted,
619            _ => Self::Unknown,
620        }
621    }
622}
623
624impl TryFrom<changed_object::IdOperation> for sui_sdk_types::IdOperation {
625    type Error = TryFromProtoError;
626
627    fn try_from(value: changed_object::IdOperation) -> Result<Self, Self::Error> {
628        use changed_object::IdOperation;
629
630        match value {
631            IdOperation::Unknown => {
632                return Err(TryFromProtoError::invalid(
633                    "id_operation",
634                    "unknown IdOperation",
635                ));
636            }
637            IdOperation::None => Self::None,
638            IdOperation::Created => Self::Created,
639            IdOperation::Deleted => Self::Deleted,
640        }
641        .pipe(Ok)
642    }
643}
644
645//
646// IdOperation
647//
648
649impl From<sui_sdk_types::AccumulatorWrite> for AccumulatorWrite {
650    fn from(value: sui_sdk_types::AccumulatorWrite) -> Self {
651        use accumulator_write::AccumulatorOperation;
652
653        let mut message = Self::default();
654
655        message.set_address(value.address());
656        message.set_accumulator_type(value.accumulator_type());
657
658        message.set_operation(match value.operation() {
659            sui_sdk_types::AccumulatorOperation::Merge => AccumulatorOperation::Merge,
660            sui_sdk_types::AccumulatorOperation::Split => AccumulatorOperation::Split,
661            _ => AccumulatorOperation::Unknown,
662        });
663
664        message.set_value(value.value());
665
666        message
667    }
668}
669
670impl TryFrom<&AccumulatorWrite> for sui_sdk_types::AccumulatorWrite {
671    type Error = TryFromProtoError;
672
673    fn try_from(value: &AccumulatorWrite) -> Result<Self, Self::Error> {
674        let address = value
675            .address_opt()
676            .ok_or_else(|| TryFromProtoError::missing("address"))?
677            .parse()
678            .map_err(|e| TryFromProtoError::invalid("address", e))?;
679        let accumulator_type = value
680            .accumulator_type_opt()
681            .ok_or_else(|| TryFromProtoError::missing("accumulator_type"))?
682            .parse()
683            .map_err(|e| TryFromProtoError::invalid("accumulator_type", e))?;
684
685        let operation = match value.operation() {
686            accumulator_write::AccumulatorOperation::Unknown => {
687                return Err(TryFromProtoError::invalid("operation", "unknown operation"));
688            }
689            accumulator_write::AccumulatorOperation::Merge => {
690                sui_sdk_types::AccumulatorOperation::Merge
691            }
692            accumulator_write::AccumulatorOperation::Split => {
693                sui_sdk_types::AccumulatorOperation::Split
694            }
695        };
696
697        let value = value
698            .value_opt()
699            .ok_or_else(|| TryFromProtoError::missing("value"))?;
700
701        Ok(Self::new(address, accumulator_type, operation, value))
702    }
703}
704
705//
706// UnchangedConsensusObject
707//
708
709impl From<sui_sdk_types::UnchangedConsensusObject> for UnchangedConsensusObject {
710    fn from(value: sui_sdk_types::UnchangedConsensusObject) -> Self {
711        use sui_sdk_types::UnchangedConsensusKind::*;
712        use unchanged_consensus_object::UnchangedConsensusObjectKind;
713
714        let mut message = Self {
715            object_id: Some(value.object_id.to_string()),
716            ..Default::default()
717        };
718
719        let kind = match value.kind {
720            ReadOnlyRoot { version, digest } => {
721                message.version = Some(version);
722                message.digest = Some(digest.to_string());
723                UnchangedConsensusObjectKind::ReadOnlyRoot
724            }
725            MutateDeleted { version } => {
726                message.version = Some(version);
727                UnchangedConsensusObjectKind::MutateConsensusStreamEnded
728            }
729            ReadDeleted { version } => {
730                message.version = Some(version);
731                UnchangedConsensusObjectKind::ReadConsensusStreamEnded
732            }
733            Canceled { version } => {
734                message.version = Some(version);
735                UnchangedConsensusObjectKind::Canceled
736            }
737            PerEpochConfig => UnchangedConsensusObjectKind::PerEpochConfig,
738            _ => UnchangedConsensusObjectKind::Unknown,
739        };
740
741        message.set_kind(kind);
742        message
743    }
744}
745
746impl TryFrom<&UnchangedConsensusObject> for sui_sdk_types::UnchangedConsensusObject {
747    type Error = TryFromProtoError;
748
749    fn try_from(value: &UnchangedConsensusObject) -> Result<Self, Self::Error> {
750        use sui_sdk_types::UnchangedConsensusKind;
751        use unchanged_consensus_object::UnchangedConsensusObjectKind;
752
753        let object_id = value
754            .object_id
755            .as_ref()
756            .ok_or_else(|| TryFromProtoError::missing("object_id"))?
757            .parse()
758            .map_err(|e| {
759                TryFromProtoError::invalid(UnchangedConsensusObject::OBJECT_ID_FIELD, e)
760            })?;
761
762        let kind = match value.kind() {
763            UnchangedConsensusObjectKind::Unknown => {
764                return Err(TryFromProtoError::invalid(
765                    UnchangedConsensusObject::KIND_FIELD,
766                    "unknown InputKind",
767                ));
768            }
769
770            UnchangedConsensusObjectKind::ReadOnlyRoot => UnchangedConsensusKind::ReadOnlyRoot {
771                version: value
772                    .version
773                    .ok_or_else(|| TryFromProtoError::missing("version"))?,
774
775                digest: value
776                    .digest
777                    .as_ref()
778                    .ok_or_else(|| TryFromProtoError::missing("digest"))?
779                    .parse()
780                    .map_err(|e| {
781                        TryFromProtoError::invalid(UnchangedConsensusObject::DIGEST_FIELD, e)
782                    })?,
783            },
784            UnchangedConsensusObjectKind::MutateConsensusStreamEnded => {
785                UnchangedConsensusKind::MutateDeleted {
786                    version: value
787                        .version
788                        .ok_or_else(|| TryFromProtoError::missing("version"))?,
789                }
790            }
791            UnchangedConsensusObjectKind::ReadConsensusStreamEnded => {
792                UnchangedConsensusKind::ReadDeleted {
793                    version: value
794                        .version
795                        .ok_or_else(|| TryFromProtoError::missing("version"))?,
796                }
797            }
798            UnchangedConsensusObjectKind::Canceled => UnchangedConsensusKind::Canceled {
799                version: value
800                    .version
801                    .ok_or_else(|| TryFromProtoError::missing("version"))?,
802            },
803            UnchangedConsensusObjectKind::PerEpochConfig => UnchangedConsensusKind::PerEpochConfig,
804        };
805
806        Ok(Self { object_id, kind })
807    }
808}