1use hiero_sdk_proto::services;
4use hiero_sdk_proto::services::file_service_client::FileServiceClient;
5use hiero_sdk_proto::services::smart_contract_service_client::SmartContractServiceClient;
6use time::OffsetDateTime;
7use tonic::transport::Channel;
8
9use crate::protobuf::{
10 FromProtobuf,
11 ToProtobuf,
12};
13use crate::transaction::{
14 AnyTransactionData,
15 ChunkInfo,
16 ToSchedulableTransactionDataProtobuf,
17 ToTransactionDataProtobuf,
18 TransactionData,
19 TransactionExecute,
20};
21use crate::{
22 BoxGrpcFuture,
23 ContractId,
24 Error,
25 FileId,
26 Transaction,
27 ValidateChecksums,
28};
29
30pub type SystemDeleteTransaction = Transaction<SystemDeleteTransactionData>;
32
33#[derive(Debug, Clone, Default)]
45pub struct SystemDeleteTransactionData {
46 expiration_time: Option<OffsetDateTime>,
47 file_id: Option<FileId>,
48 contract_id: Option<ContractId>,
49}
50
51impl SystemDeleteTransaction {
52 #[must_use]
54 pub fn get_contract_id(&self) -> Option<ContractId> {
55 self.data().contract_id
56 }
57
58 pub fn contract_id(&mut self, id: impl Into<ContractId>) -> &mut Self {
60 let data = self.data_mut();
61 data.file_id = None;
62 data.contract_id = Some(id.into());
63 self
64 }
65
66 #[must_use]
68 pub fn get_file_id(&self) -> Option<FileId> {
69 self.data().file_id
70 }
71
72 pub fn file_id(&mut self, id: impl Into<FileId>) -> &mut Self {
74 let data = self.data_mut();
75 data.contract_id = None;
76 data.file_id = Some(id.into());
77 self
78 }
79
80 #[must_use]
83 pub fn get_expiration_time(&self) -> Option<OffsetDateTime> {
84 self.data().expiration_time
85 }
86
87 pub fn expiration_time(&mut self, expiration_time: OffsetDateTime) -> &mut Self {
90 self.data_mut().expiration_time = Some(expiration_time);
91 self
92 }
93}
94
95impl TransactionData for SystemDeleteTransactionData {}
96
97impl TransactionExecute for SystemDeleteTransactionData {
98 #[allow(deprecated)]
99 fn execute(
100 &self,
101 channel: Channel,
102 request: services::Transaction,
103 ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
104 Box::pin(async move {
105 if self.file_id.is_some() {
106 FileServiceClient::new(channel).system_delete(request).await
107 } else {
108 SmartContractServiceClient::new(channel).system_delete(request).await
109 }
110 })
111 }
112}
113
114impl ValidateChecksums for SystemDeleteTransactionData {
115 fn validate_checksums(&self, ledger_id: &crate::ledger_id::RefLedgerId) -> Result<(), Error> {
116 self.file_id.validate_checksums(ledger_id)?;
117 self.contract_id.validate_checksums(ledger_id)
118 }
119}
120
121impl ToTransactionDataProtobuf for SystemDeleteTransactionData {
122 fn to_transaction_data_protobuf(
123 &self,
124 chunk_info: &ChunkInfo,
125 ) -> services::transaction_body::Data {
126 let _ = chunk_info.assert_single_transaction();
127
128 services::transaction_body::Data::SystemDelete(self.to_protobuf())
129 }
130}
131
132impl ToSchedulableTransactionDataProtobuf for SystemDeleteTransactionData {
133 fn to_schedulable_transaction_data_protobuf(
134 &self,
135 ) -> services::schedulable_transaction_body::Data {
136 services::schedulable_transaction_body::Data::SystemDelete(self.to_protobuf())
137 }
138}
139
140impl From<SystemDeleteTransactionData> for AnyTransactionData {
141 fn from(transaction: SystemDeleteTransactionData) -> Self {
142 Self::SystemDelete(transaction)
143 }
144}
145
146impl FromProtobuf<services::SystemDeleteTransactionBody> for SystemDeleteTransactionData {
147 fn from_protobuf(pb: services::SystemDeleteTransactionBody) -> crate::Result<Self> {
148 use services::system_delete_transaction_body::Id;
149 let (file_id, contract_id) = match pb.id {
150 Some(Id::FileId(it)) => (Some(FileId::from_protobuf(it)?), None),
151 Some(Id::ContractId(it)) => (None, Some(ContractId::from_protobuf(it)?)),
152 None => (None, None),
153 };
154
155 Ok(Self { file_id, contract_id, expiration_time: pb.expiration_time.map(Into::into) })
156 }
157}
158
159impl ToProtobuf for SystemDeleteTransactionData {
160 type Protobuf = services::SystemDeleteTransactionBody;
161
162 fn to_protobuf(&self) -> Self::Protobuf {
163 let expiration_time = self.expiration_time.map(Into::into);
164 let contract_id = self.contract_id.to_protobuf();
165 let file_id = self.file_id.to_protobuf();
166
167 let id = match (contract_id, file_id) {
168 (Some(contract_id), _) => {
169 Some(services::system_delete_transaction_body::Id::ContractId(contract_id))
170 }
171
172 (_, Some(file_id)) => {
173 Some(services::system_delete_transaction_body::Id::FileId(file_id))
174 }
175
176 _ => None,
177 };
178
179 services::SystemDeleteTransactionBody { expiration_time, id }
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use expect_test::expect;
186 use hiero_sdk_proto::services;
187
188 use crate::protobuf::{
189 FromProtobuf,
190 ToProtobuf,
191 };
192 use crate::system::SystemDeleteTransactionData;
193 use crate::transaction::test_helpers::{
194 check_body,
195 transaction_body,
196 VALID_START,
197 };
198 use crate::{
199 AnyTransaction,
200 ContractId,
201 FileId,
202 SystemDeleteTransaction,
203 };
204
205 const FILE_ID: FileId = FileId::new(0, 0, 444);
206 const CONTRACT_ID: ContractId = ContractId::new(0, 0, 444);
207
208 fn make_transaction_file() -> SystemDeleteTransaction {
209 let mut tx = SystemDeleteTransaction::new_for_tests();
210
211 tx.file_id(FILE_ID).expiration_time(VALID_START).freeze().unwrap();
212 tx
213 }
214
215 fn make_transaction_contract() -> SystemDeleteTransaction {
216 let mut tx = SystemDeleteTransaction::new_for_tests();
217
218 tx.contract_id(CONTRACT_ID).expiration_time(VALID_START).freeze().unwrap();
219 tx
220 }
221
222 #[test]
223 fn serialize_file() {
224 let tx = make_transaction_file();
225
226 let tx = transaction_body(tx);
227
228 let tx = check_body(tx);
229
230 expect![[r#"
231 SystemDelete(
232 SystemDeleteTransactionBody {
233 expiration_time: Some(
234 TimestampSeconds {
235 seconds: 1554158542,
236 },
237 ),
238 id: Some(
239 FileId(
240 FileId {
241 shard_num: 0,
242 realm_num: 0,
243 file_num: 444,
244 },
245 ),
246 ),
247 },
248 )
249 "#]]
250 .assert_debug_eq(&tx)
251 }
252
253 #[test]
254 fn serialize_contract() {
255 let tx = make_transaction_contract();
256
257 let tx = transaction_body(tx);
258
259 let tx = check_body(tx);
260
261 expect![[r#"
262 SystemDelete(
263 SystemDeleteTransactionBody {
264 expiration_time: Some(
265 TimestampSeconds {
266 seconds: 1554158542,
267 },
268 ),
269 id: Some(
270 ContractId(
271 ContractId {
272 shard_num: 0,
273 realm_num: 0,
274 contract: Some(
275 ContractNum(
276 444,
277 ),
278 ),
279 },
280 ),
281 ),
282 },
283 )
284 "#]]
285 .assert_debug_eq(&tx)
286 }
287
288 #[test]
289 fn to_from_bytes_file() {
290 let tx = make_transaction_file();
291
292 let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
293
294 let tx = transaction_body(tx);
295
296 let tx2 = transaction_body(tx2);
297
298 assert_eq!(tx, tx2);
299 }
300
301 #[test]
302 fn to_from_bytes_contract() {
303 let tx = make_transaction_contract();
304
305 let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
306
307 let tx = transaction_body(tx);
308
309 let tx2 = transaction_body(tx2);
310
311 assert_eq!(tx, tx2);
312 }
313
314 #[test]
315 fn from_proto_body() {
316 let tx = services::SystemDeleteTransactionBody {
317 expiration_time: Some(services::TimestampSeconds {
318 seconds: VALID_START.unix_timestamp(),
319 }),
320 id: Some(services::system_delete_transaction_body::Id::FileId(FILE_ID.to_protobuf())),
321 };
322
323 let tx = SystemDeleteTransactionData::from_protobuf(tx).unwrap();
324
325 assert_eq!(tx.file_id, Some(FILE_ID));
326 assert_eq!(tx.contract_id, None);
327 assert_eq!(tx.expiration_time, Some(VALID_START));
328 }
329
330 #[test]
331 fn get_set_file_id() {
332 let mut tx = SystemDeleteTransaction::new();
333 tx.file_id(FILE_ID);
334
335 assert_eq!(tx.get_file_id(), Some(FILE_ID));
336 }
337
338 #[test]
339 #[should_panic]
340 fn get_set_file_id_frozen_panics() {
341 make_transaction_file().file_id(FILE_ID);
342 }
343
344 #[test]
345 fn get_set_contract_id() {
346 let mut tx = SystemDeleteTransaction::new();
347 tx.contract_id(CONTRACT_ID);
348
349 assert_eq!(tx.get_contract_id(), Some(CONTRACT_ID));
350 }
351
352 #[test]
353 #[should_panic]
354 fn get_set_contract_id_frozen_panics() {
355 make_transaction_file().contract_id(CONTRACT_ID);
356 }
357
358 #[test]
359 fn get_set_expiration_time() {
360 let mut tx = SystemDeleteTransaction::new();
361 tx.expiration_time(VALID_START);
362
363 assert_eq!(tx.get_expiration_time(), Some(VALID_START));
364 }
365
366 #[test]
367 #[should_panic]
368 fn get_set_expiration_time_frozen_panics() {
369 make_transaction_file().expiration_time(VALID_START);
370 }
371}