1use sui_types::Address;
2use sui_types::Command;
3use sui_types::Digest;
4use sui_types::TransactionExpiration;
5use sui_types::Version;
6
7#[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#[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 pub object_id: Option<Address>,
88 #[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 #[serde(skip_serializing_if = "Option::is_none")]
103 pub digest: Option<Digest>,
104 #[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 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 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 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 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 pub fn by_id(object_id: Address) -> Self {
169 Self {
170 object_id: Some(object_id),
171 ..Default::default()
172 }
173 }
174
175 pub fn with_immutable_kind(self) -> Self {
177 Self {
178 kind: Some(InputKind::ImmutableOrOwned),
179 ..self
180 }
181 }
182
183 pub fn with_owned_kind(self) -> Self {
185 Self {
186 kind: Some(InputKind::ImmutableOrOwned),
187 ..self
188 }
189 }
190
191 pub fn with_receiving_kind(self) -> Self {
193 Self {
194 kind: Some(InputKind::Receiving),
195 ..self
196 }
197 }
198
199 pub fn with_shared_kind(self) -> Self {
201 Self {
202 kind: Some(InputKind::Shared),
203 ..self
204 }
205 }
206
207 pub fn with_version(self, version: u64) -> Self {
209 Self {
210 version: Some(version),
211 ..self
212 }
213 }
214
215 pub fn with_digest(self, digest: Digest) -> Self {
217 Self {
218 digest: Some(digest),
219 ..self
220 }
221 }
222
223 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 pub fn by_val(self) -> Self {
236 Self {
237 kind: Some(InputKind::Shared),
238 mutable: Some(true),
239 ..self
240 }
241 }
242
243 pub fn by_ref(self) -> Self {
246 Self {
247 kind: Some(InputKind::Shared),
248 mutable: Some(false),
249 ..self
250 }
251 }
252
253 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>>>;