sui_transaction_builder/
unresolved.rs

1use sui_types::Address;
2use sui_types::Command;
3use sui_types::ObjectDigest;
4use sui_types::ObjectId;
5use sui_types::TransactionExpiration;
6use sui_types::Version;
7
8// A potentially unresolved user transaction. Note that one can construct a fully resolved
9// transaction using this type by providing all the required data.
10#[derive(serde::Serialize, serde::Deserialize)]
11#[serde(rename = "UnresolvedTransaction")]
12pub struct Transaction {
13    #[serde(flatten)]
14    pub ptb: ProgrammableTransaction,
15    pub sender: Address,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub gas_payment: Option<GasPayment>,
18    pub expiration: TransactionExpiration,
19}
20
21#[derive(serde::Serialize, serde::Deserialize)]
22#[serde(rename = "UnresolvedProgrammableTransaction")]
23pub struct ProgrammableTransaction {
24    pub inputs: Vec<Input>,
25    pub commands: Vec<Command>,
26}
27
28#[derive(serde::Serialize, serde::Deserialize)]
29#[serde(rename = "UnresolvedGasPayment")]
30pub struct GasPayment {
31    #[serde(default, skip_serializing_if = "Vec::is_empty")]
32    pub objects: Vec<ObjectReference>,
33    pub owner: Address,
34    #[serde(
35        with = "OptionReadableDisplay",
36        default,
37        skip_serializing_if = "Option::is_none"
38    )]
39    pub price: Option<u64>,
40    #[serde(
41        with = "OptionReadableDisplay",
42        default,
43        skip_serializing_if = "Option::is_none"
44    )]
45    pub budget: Option<u64>,
46}
47
48#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
49#[serde(rename = "UnresolvedObjectReference")]
50pub struct ObjectReference {
51    pub object_id: ObjectId,
52    #[serde(
53        with = "OptionReadableDisplay",
54        default,
55        skip_serializing_if = "Option::is_none"
56    )]
57    pub version: Option<Version>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub digest: Option<ObjectDigest>,
60}
61
62#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
63#[serde(rename = "UnresolvedInputKind")]
64#[serde(rename_all = "snake_case")]
65pub enum InputKind {
66    Pure,
67    Shared,
68    Receiving,
69    ImmutableOrOwned,
70    Literal,
71}
72
73/// A potentially unresolved transaction input. Note that one can construct a fully resolved input
74/// using the provided constructors, but this struct is also useful when the input data is
75/// not complete.
76///
77/// If used in the context of transaction builder, make sure to call `tx.resolve` function on the
78/// transaction builder to resolve all unresolved inputs.
79#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
80#[serde(rename = "UnresolvedInput")]
81pub struct Input {
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub kind: Option<InputKind>,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub value: Option<Value>,
86    #[serde(skip_serializing_if = "Option::is_none")]
87    /// Unique identifier for this object.
88    pub object_id: Option<ObjectId>,
89    /// Either the `initial_shared_version` if object is a shared object, or the `version` if
90    /// this is an owned object.
91    /// The semantics of version can change depending on whether the object is shared or not.
92    /// For shared objects, this is the initial version the object was shared at. For all other
93    /// objects, this is the version of the object.
94    #[serde(
95        with = "OptionReadableDisplay",
96        default,
97        skip_serializing_if = "Option::is_none",
98        alias = "initial_shared_version"
99    )]
100    pub version: Option<Version>,
101    /// The digest of this object. This field is only relevant for owned/immutable/receiving
102    /// inputs.
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub digest: Option<ObjectDigest>,
105    /// Whether this object is mutable. This field is only relevant for shared objects.
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub mutable: Option<bool>,
108}
109
110#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
111#[serde(rename = "UnresolvedValue")]
112#[serde(try_from = "serde_json::Value", into = "serde_json::Value")]
113pub enum Value {
114    Null,
115    Bool(bool),
116    Number(u64),
117    String(String),
118    Array(Vec<Value>),
119}
120
121impl Input {
122    /// Return an owned kind of object with all required fields.
123    pub fn owned(object_id: ObjectId, version: u64, digest: ObjectDigest) -> Self {
124        Self {
125            kind: Some(InputKind::ImmutableOrOwned),
126            object_id: Some(object_id),
127            version: Some(version),
128            digest: Some(digest),
129            ..Default::default()
130        }
131    }
132
133    /// Return an immutable kind of object with all required fields.
134    pub fn immutable(object_id: ObjectId, version: u64, digest: ObjectDigest) -> Self {
135        Self {
136            kind: Some(InputKind::ImmutableOrOwned),
137            object_id: Some(object_id),
138            version: Some(version),
139            digest: Some(digest),
140            ..Default::default()
141        }
142    }
143
144    /// Return a receiving kind of object with all required fields.
145    pub fn receiving(object_id: ObjectId, version: u64, digest: ObjectDigest) -> Self {
146        Self {
147            kind: Some(InputKind::Receiving),
148            object_id: Some(object_id),
149            version: Some(version),
150            digest: Some(digest),
151            ..Default::default()
152        }
153    }
154
155    /// Return a shared object.
156    /// - `mutable` controls whether a command can accept the object by value or mutable reference.
157    /// - `initial_shared_version` is the first version the object was shared at.
158    pub fn shared(object_id: ObjectId, initial_shared_version: u64, mutable: bool) -> Self {
159        Self {
160            kind: Some(InputKind::Shared),
161            object_id: Some(object_id),
162            version: Some(initial_shared_version),
163            mutable: Some(mutable),
164            ..Default::default()
165        }
166    }
167
168    /// Return an object with only its unique identifier.
169    pub fn by_id(object_id: ObjectId) -> Self {
170        Self {
171            object_id: Some(object_id),
172            ..Default::default()
173        }
174    }
175
176    /// Set the object kind to immutable.
177    pub fn with_immutable_kind(self) -> Self {
178        Self {
179            kind: Some(InputKind::ImmutableOrOwned),
180            ..self
181        }
182    }
183
184    /// Set the object kind to owned.
185    pub fn with_owned_kind(self) -> Self {
186        Self {
187            kind: Some(InputKind::ImmutableOrOwned),
188            ..self
189        }
190    }
191
192    /// Set the object kind to receiving.
193    pub fn with_receiving_kind(self) -> Self {
194        Self {
195            kind: Some(InputKind::Receiving),
196            ..self
197        }
198    }
199
200    /// Set the object kind to shared.
201    pub fn with_shared_kind(self) -> Self {
202        Self {
203            kind: Some(InputKind::Shared),
204            ..self
205        }
206    }
207
208    /// Set the specified version.
209    pub fn with_version(self, version: u64) -> Self {
210        Self {
211            version: Some(version),
212            ..self
213        }
214    }
215
216    /// Set the specified digest.
217    pub fn with_digest(self, digest: ObjectDigest) -> Self {
218        Self {
219            digest: Some(digest),
220            ..self
221        }
222    }
223
224    // Shared fields
225
226    /// Set the initial shared version.
227    pub fn with_initial_shared_version(self, initial_version: u64) -> Self {
228        Self {
229            kind: Some(InputKind::Shared),
230            version: Some(initial_version),
231            ..self
232        }
233    }
234
235    /// Make the object shared and set `mutable` to true when the input is used by value.
236    pub fn by_val(self) -> Self {
237        Self {
238            kind: Some(InputKind::Shared),
239            mutable: Some(true),
240            ..self
241        }
242    }
243
244    /// Make the object shared and set `mutable` to false when the input is used by
245    /// reference.
246    pub fn by_ref(self) -> Self {
247        Self {
248            kind: Some(InputKind::Shared),
249            mutable: Some(false),
250            ..self
251        }
252    }
253
254    /// Make the object shared and set `mutable` to true when the input is used by mutable
255    /// reference.
256    pub fn by_mut(self) -> Self {
257        Self {
258            kind: Some(InputKind::Shared),
259            mutable: Some(true),
260            ..self
261        }
262    }
263}
264
265impl TryFrom<serde_json::Value> for Value {
266    type Error = &'static str;
267
268    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
269        let v = match value {
270            serde_json::Value::Null => Self::Null,
271            serde_json::Value::Bool(b) => Self::Bool(b),
272            serde_json::Value::Number(n) => {
273                Self::Number(n.as_u64().ok_or("expected unsigned integer")?)
274            }
275            serde_json::Value::String(s) => Self::String(s),
276            serde_json::Value::Array(a) => Self::Array(
277                a.into_iter()
278                    .map(Self::try_from)
279                    .collect::<Result<_, _>>()?,
280            ),
281            serde_json::Value::Object(_) => return Err("objects are not supported"),
282        };
283
284        Ok(v)
285    }
286}
287
288impl From<Value> for serde_json::Value {
289    fn from(value: Value) -> Self {
290        match value {
291            Value::Null => Self::Null,
292            Value::Bool(b) => Self::Bool(b),
293            Value::Number(n) => Self::Number(n.into()),
294            Value::String(s) => Self::String(s),
295            Value::Array(a) => Self::Array(a.into_iter().map(Into::into).collect()),
296        }
297    }
298}
299
300impl From<&sui_types::Object> for Input {
301    fn from(object: &sui_types::Object) -> Self {
302        use sui_types::Owner;
303
304        let input = Input::by_id(object.object_id())
305            .with_digest(object.digest())
306            .with_version(object.version());
307        match object.owner() {
308            Owner::Address(_) => input,
309            Owner::Object(_) => input,
310            Owner::Shared(at_version) => input.with_initial_shared_version(*at_version),
311            Owner::Immutable => input.with_immutable_kind(),
312            Owner::ConsensusAddress { start_version, .. } => {
313                input.with_initial_shared_version(*start_version)
314            }
315        }
316    }
317}
318
319impl From<ObjectId> for Input {
320    fn from(object_id: ObjectId) -> Self {
321        Input::by_id(object_id)
322    }
323}
324
325pub(crate) type OptionReadableDisplay =
326    ::serde_with::As<Option<::serde_with::IfIsHumanReadable<::serde_with::DisplayFromStr>>>;