sui_transaction_builder/
unresolved.rs

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