af_sui_types/sui/
object.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, Bytes};
use sui_sdk_types::{MovePackage, Version};

use super::move_object_type::MoveObjectType;
use crate::{Address, ObjectId, TransactionDigest};

// =============================================================================
//  Object
// =============================================================================

/// Alias of `sui_types::object::ObjectInner`, skipping the `Arc` in `sui_types::object::Object`.
#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
pub struct Object {
    /// The meat of the object
    data: Data,
    /// The owner that unlocks this object
    pub owner: Owner,
    /// The digest of the transaction that created or last mutated this object
    pub previous_transaction: TransactionDigest,
    /// The amount of SUI we would rebate if this object gets deleted.
    /// This number is re-calculated each time the object is mutated based on
    /// the present storage gas price.
    pub storage_rebate: u64,
}

impl Object {
    /// Its unique on-chain identifier.
    pub fn id(&self) -> ObjectId {
        match &self.data {
            Data::Move(o) => o.id(),
            Data::Package(p) => p.id,
        }
    }

    /// Its current version.
    pub const fn version(&self) -> Version {
        match &self.data {
            Data::Move(o) => o.version,
            Data::Package(p) => p.version,
        }
    }

    /// Reference to the underlying Move object, if it is one.
    pub const fn as_move(&self) -> Option<&MoveObject> {
        let Data::Move(ref obj) = self.data else {
            return None;
        };
        Some(obj)
    }

    /// Reference to the underlying Move package, if it is one.
    pub const fn as_package(&self) -> Option<&MovePackage> {
        let Data::Package(ref obj) = self.data else {
            return None;
        };
        Some(obj)
    }

    /// Convert to the underlying Move object, if it is one.
    pub fn into_move(self) -> Option<MoveObject> {
        let Data::Move(obj) = self.data else {
            return None;
        };
        Some(obj)
    }

    /// Convert to the underlying Move package, if it is one.
    pub fn into_package(self) -> Option<MovePackage> {
        let Data::Package(obj) = self.data else {
            return None;
        };
        Some(obj)
    }
}

// =============================================================================
//  Data
// =============================================================================

#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
pub(crate) enum Data {
    /// An object whose governing logic lives in a published Move module
    Move(MoveObject),
    /// Map from each module name to raw serialized Move module bytes
    Package(MovePackage),
    // ... Sui "native" types go here
}

// =============================================================================
//  Owner
// =============================================================================

/// The entity that owns an object.
#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash, Ord, PartialOrd)]
pub enum Owner {
    /// Object is exclusively owned by a single address, and is mutable.
    AddressOwner(Address),
    /// Object is exclusively owned by a single object, and is mutable.
    /// The object ID is converted to Address as SuiAddress is universal.
    ObjectOwner(ObjectId),
    /// Object is shared, can be used by any address, and is mutable.
    Shared {
        /// The version at which the object became shared
        initial_shared_version: Version,
    },
    /// Object is immutable, and hence ownership doesn't matter.
    Immutable,
    /// Object is sequenced via consensus. Ownership is managed by the configured authenticator.
    ///
    /// Note: wondering what happened to `V1`? `Shared` above was the V1 of consensus objects.
    ConsensusV2 {
        /// The version at which the object most recently became a consensus object.
        /// This serves the same function as `initial_shared_version`, except it may change
        /// if the object's Owner type changes.
        start_version: Version,
        /// The authentication mode of the object
        authenticator: Box<Authenticator>,
    },
}

impl Owner {
    /// Only return address of [`AddressOwner`], otherwise return error.
    ///
    /// [`ObjectOwner`]'s address is converted from object id, thus we will skip it.
    ///
    /// [`AddressOwner`]: Owner::AddressOwner
    /// [`ObjectOwner`]: Owner::ObjectOwner
    pub const fn get_address_owner_address(&self) -> Option<Address> {
        match self {
            Self::AddressOwner(address) => Some(*address),
            Self::Shared { .. }
            | Self::Immutable
            | Self::ObjectOwner(_)
            | Self::ConsensusV2 { .. } => None,
        }
    }

    /// This function will return address of both [`AddressOwner`] and [`ObjectOwner`],
    ///
    /// Address of [`ObjectOwner`] is converted from object id, even though the type is [`Address`].
    ///
    /// [`AddressOwner`]: Owner::AddressOwner
    /// [`ObjectOwner`]: Owner::ObjectOwner
    pub const fn get_owner_address(&self) -> Option<Address> {
        match self {
            Self::AddressOwner(address) => Some(*address),
            Self::ObjectOwner(id) => Some(*id.as_address()),
            Self::Shared { .. } | Self::Immutable | Self::ConsensusV2 { .. } => None,
        }
    }

    pub const fn is_immutable(&self) -> bool {
        matches!(self, Self::Immutable)
    }

    pub const fn is_address_owned(&self) -> bool {
        matches!(self, Self::AddressOwner(_))
    }

    pub const fn is_child_object(&self) -> bool {
        matches!(self, Self::ObjectOwner(_))
    }

    pub const fn is_shared(&self) -> bool {
        matches!(self, Self::Shared { .. })
    }
}

impl From<sui_sdk_types::Owner> for Owner {
    fn from(value: sui_sdk_types::Owner) -> Self {
        use sui_sdk_types::Owner::*;
        match value {
            Address(a) => Self::AddressOwner(a),
            Object(o) => Self::ObjectOwner(o),
            Shared(v) => Self::Shared {
                initial_shared_version: v,
            },
            Immutable => Self::Immutable,
        }
    }
}

impl PartialEq<ObjectId> for Owner {
    fn eq(&self, other: &ObjectId) -> bool {
        match self {
            Self::ObjectOwner(id) => id.inner() == other.inner(),
            Self::AddressOwner(_)
            | Self::Shared { .. }
            | Self::Immutable
            | Self::ConsensusV2 { .. } => false,
        }
    }
}

impl std::fmt::Display for Owner {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::AddressOwner(address) => {
                write!(f, "Account Address ( {} )", address)
            }
            Self::ObjectOwner(address) => {
                write!(f, "Object ID: ( {} )", address)
            }
            Self::Immutable => {
                write!(f, "Immutable")
            }
            Self::Shared { .. } => {
                write!(f, "Shared")
            }
            Self::ConsensusV2 {
                start_version,
                authenticator,
            } => {
                write!(f, "ConsensusV2( {}, {} )", start_version, authenticator)
            }
        }
    }
}

// =============================================================================
//  Authenticator
// =============================================================================

/// An object authenticator for Sui's concensus v2.
///
/// Currently `non_exhaustive` as there is a clear expectation that this will be expanded in the
/// future.
#[derive(Eq, PartialEq, Debug, Clone, Copy, Deserialize, Serialize, Hash, Ord, PartialOrd)]
#[non_exhaustive]
pub enum Authenticator {
    /// The contained Address exclusively has all permissions: read, write, delete, transfer
    SingleOwner(Address),
}

impl std::fmt::Display for Authenticator {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::SingleOwner(address) => {
                write!(f, "SingleOwner({})", address)
            }
        }
    }
}

// =============================================================================
//  MoveObject
// =============================================================================

/// Index marking the end of the object's ID + the beginning of its version
const ID_END_INDEX: usize = ObjectId::LENGTH;

#[serde_as]
#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)]
pub struct MoveObject {
    /// The type of this object. Immutable
    pub type_: MoveObjectType,
    /// DEPRECATED this field is no longer used to determine whether a tx can transfer this
    /// object. Instead, it is always calculated from the objects type when loaded in execution
    pub(crate) has_public_transfer: bool,
    /// Number that increases each time a tx takes this object as a mutable input
    /// This is a lamport timestamp, not a sequentially increasing version
    pub version: Version,
    /// BCS bytes of a Move struct value
    #[serde_as(as = "Bytes")]
    pub contents: Vec<u8>,
}

impl MoveObject {
    /// Get the object's ID from its BCS serialization.
    ///
    /// # Panics
    ///
    /// This will panic if the BCS contents do not enconde a [`MoveObject`].
    pub fn id(&self) -> ObjectId {
        ObjectId::new(
            self.contents[0..ID_END_INDEX]
                .try_into()
                .expect("Corrupted Object BCS"),
        )
    }
}