1use move_binary_format::file_format::AbilitySet;
5use move_core_types::identifier::IdentStr;
6use move_vm_types::loaded_data::runtime_types::Type;
7use serde::Deserialize;
8use sui_types::{
9 base_types::{ObjectID, SequenceNumber, SuiAddress},
10 coin::Coin,
11 error::ExecutionError,
12 execution_status::{CommandArgumentError, ExecutionErrorKind},
13 object::Owner,
14 transfer::Receiving,
15};
16
17#[derive(Clone, Debug)]
18pub enum InputObjectMetadata {
19 Receiving {
20 id: ObjectID,
21 version: SequenceNumber,
22 },
23 InputObject {
24 id: ObjectID,
25 is_mutable_input: bool,
26 owner: Owner,
27 version: SequenceNumber,
28 },
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum UsageKind {
33 BorrowImm,
34 BorrowMut,
35 ByValue,
36}
37
38#[derive(Clone, Copy)]
39pub enum CommandKind<'a> {
40 MoveCall {
41 package: ObjectID,
42 module: &'a IdentStr,
43 function: &'a IdentStr,
44 },
45 MakeMoveVec,
46 TransferObjects,
47 SplitCoins,
48 MergeCoins,
49 Publish,
50 Upgrade,
51}
52
53#[derive(Clone, Debug)]
54pub struct InputValue {
55 pub object_metadata: Option<InputObjectMetadata>,
57 pub inner: ResultValue,
58}
59
60#[derive(Clone, Debug)]
61pub struct ResultValue {
62 pub last_usage_kind: Option<UsageKind>,
66 pub value: Option<Value>,
67}
68
69#[derive(Debug, Clone)]
70pub enum Value {
71 Object(ObjectValue),
72 Raw(RawValueType, Vec<u8>),
73 Receiving(ObjectID, SequenceNumber, Option<Type>),
74}
75
76#[derive(Debug, Clone)]
77pub struct ObjectValue {
78 pub type_: Type,
79 pub has_public_transfer: bool,
80 pub used_in_non_entry_move_call: bool,
84 pub contents: ObjectContents,
85}
86
87#[derive(Debug, Clone)]
88pub enum ObjectContents {
89 Coin(Coin),
90 Raw(Vec<u8>),
91}
92
93#[derive(Debug, Clone)]
94pub enum RawValueType {
95 Any,
96 Loaded {
97 ty: Type,
98 abilities: AbilitySet,
99 used_in_non_entry_move_call: bool,
100 },
101}
102
103impl InputObjectMetadata {
104 pub fn id(&self) -> ObjectID {
105 match self {
106 InputObjectMetadata::Receiving { id, .. } => *id,
107 InputObjectMetadata::InputObject { id, .. } => *id,
108 }
109 }
110
111 pub fn version(&self) -> SequenceNumber {
112 match self {
113 InputObjectMetadata::Receiving { version, .. } => *version,
114 InputObjectMetadata::InputObject { version, .. } => *version,
115 }
116 }
117}
118
119impl InputValue {
120 pub fn new_object(object_metadata: InputObjectMetadata, value: ObjectValue) -> Self {
121 InputValue {
122 object_metadata: Some(object_metadata),
123 inner: ResultValue::new(Value::Object(value)),
124 }
125 }
126
127 pub fn new_raw(ty: RawValueType, value: Vec<u8>) -> Self {
128 InputValue {
129 object_metadata: None,
130 inner: ResultValue::new(Value::Raw(ty, value)),
131 }
132 }
133
134 pub fn new_receiving_object(id: ObjectID, version: SequenceNumber) -> Self {
135 InputValue {
136 object_metadata: Some(InputObjectMetadata::Receiving { id, version }),
137 inner: ResultValue::new(Value::Receiving(id, version, None)),
138 }
139 }
140}
141
142impl ResultValue {
143 pub fn new(value: Value) -> Self {
144 Self {
145 last_usage_kind: None,
146 value: Some(value),
147 }
148 }
149}
150
151impl Value {
152 pub fn is_copyable(&self) -> bool {
153 match self {
154 Value::Object(_) => false,
155 Value::Raw(RawValueType::Any, _) => true,
156 Value::Raw(RawValueType::Loaded { abilities, .. }, _) => abilities.has_copy(),
157 Value::Receiving(_, _, _) => false,
158 }
159 }
160
161 pub fn write_bcs_bytes(&self, buf: &mut Vec<u8>) {
162 match self {
163 Value::Object(obj_value) => obj_value.write_bcs_bytes(buf),
164 Value::Raw(_, bytes) => buf.extend(bytes),
165 Value::Receiving(id, version, _) => {
166 buf.extend(Receiving::new(*id, *version).to_bcs_bytes())
167 }
168 }
169 }
170
171 pub fn was_used_in_non_entry_move_call(&self) -> bool {
172 match self {
173 Value::Object(obj) => obj.used_in_non_entry_move_call,
174 Value::Raw(RawValueType::Any, _) => false,
177 Value::Raw(
178 RawValueType::Loaded {
179 used_in_non_entry_move_call,
180 ..
181 },
182 _,
183 ) => *used_in_non_entry_move_call,
184 Value::Receiving(_, _, _) => false,
187 }
188 }
189}
190
191impl ObjectValue {
192 pub unsafe fn coin(type_: Type, coin: Coin) -> Self {
195 Self {
196 type_,
197 has_public_transfer: true,
198 used_in_non_entry_move_call: false,
199 contents: ObjectContents::Coin(coin),
200 }
201 }
202
203 pub fn ensure_public_transfer_eligible(&self) -> Result<(), ExecutionError> {
204 if !self.has_public_transfer {
205 return Err(ExecutionErrorKind::InvalidTransferObject.into());
206 }
207 Ok(())
208 }
209
210 pub fn write_bcs_bytes(&self, buf: &mut Vec<u8>) {
211 match &self.contents {
212 ObjectContents::Raw(bytes) => buf.extend(bytes),
213 ObjectContents::Coin(coin) => buf.extend(coin.to_bcs_bytes()),
214 }
215 }
216}
217
218pub trait TryFromValue: Sized {
219 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError>;
220}
221
222impl TryFromValue for Value {
223 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
224 Ok(value)
225 }
226}
227
228impl TryFromValue for ObjectValue {
229 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
230 match value {
231 Value::Object(o) => Ok(o),
232 Value::Raw(RawValueType::Any, _) => Err(CommandArgumentError::TypeMismatch),
233 Value::Raw(RawValueType::Loaded { .. }, _) => Err(CommandArgumentError::TypeMismatch),
234 Value::Receiving(_, _, _) => Err(CommandArgumentError::TypeMismatch),
235 }
236 }
237}
238
239impl TryFromValue for SuiAddress {
240 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
241 try_from_value_prim(&value, Type::Address)
242 }
243}
244
245impl TryFromValue for u64 {
246 fn try_from_value(value: Value) -> Result<Self, CommandArgumentError> {
247 try_from_value_prim(&value, Type::U64)
248 }
249}
250
251fn try_from_value_prim<'a, T: Deserialize<'a>>(
252 value: &'a Value,
253 expected_ty: Type,
254) -> Result<T, CommandArgumentError> {
255 match value {
256 Value::Object(_) => Err(CommandArgumentError::TypeMismatch),
257 Value::Receiving(_, _, _) => Err(CommandArgumentError::TypeMismatch),
258 Value::Raw(RawValueType::Any, bytes) => {
259 bcs::from_bytes(bytes).map_err(|_| CommandArgumentError::InvalidBCSBytes)
260 }
261 Value::Raw(RawValueType::Loaded { ty, .. }, bytes) => {
262 if ty != &expected_ty {
263 return Err(CommandArgumentError::TypeMismatch);
264 }
265 bcs::from_bytes(bytes).map_err(|_| CommandArgumentError::InvalidBCSBytes)
266 }
267 }
268}