1use hiero_sdk_proto::services;
4use hiero_sdk_proto::services::smart_contract_service_client::SmartContractServiceClient;
5use tonic::transport::Channel;
6
7use crate::ledger_id::RefLedgerId;
8use crate::protobuf::{
9 FromProtobuf,
10 ToProtobuf,
11};
12use crate::transaction::{
13 AnyTransactionData,
14 ChunkInfo,
15 ToSchedulableTransactionDataProtobuf,
16 ToTransactionDataProtobuf,
17 TransactionData,
18 TransactionExecute,
19};
20use crate::{
21 AccountId,
22 BoxGrpcFuture,
23 ContractId,
24 Error,
25 Transaction,
26 ValidateChecksums,
27};
28
29pub type ContractDeleteTransaction = Transaction<ContractDeleteTransactionData>;
33
34#[derive(Debug, Clone, Default)]
35pub struct ContractDeleteTransactionData {
36 contract_id: Option<ContractId>,
37
38 transfer_account_id: Option<AccountId>,
39
40 transfer_contract_id: Option<ContractId>,
41
42 permanent_removal: bool,
43}
44
45impl ContractDeleteTransaction {
46 #[must_use]
48 pub fn get_contract_id(&self) -> Option<ContractId> {
49 self.data().contract_id
50 }
51
52 pub fn contract_id(&mut self, id: ContractId) -> &mut Self {
54 self.data_mut().contract_id = Some(id);
55 self
56 }
57
58 #[must_use]
60 pub fn get_transfer_account_id(&self) -> Option<AccountId> {
61 self.data().transfer_account_id
62 }
63
64 pub fn transfer_account_id(&mut self, id: AccountId) -> &mut Self {
68 let data = self.data_mut();
69 data.transfer_account_id = Some(id);
70 data.transfer_contract_id = None; self
72 }
73
74 #[must_use]
76 pub fn get_transfer_contract_id(&self) -> Option<ContractId> {
77 self.data().transfer_contract_id
78 }
79
80 pub fn transfer_contract_id(&mut self, id: ContractId) -> &mut Self {
84 let data = self.data_mut();
85 data.transfer_contract_id = Some(id);
86 data.transfer_account_id = None; self
88 }
89
90 #[must_use]
92 pub fn get_permanent_removal(&self) -> bool {
93 self.data().permanent_removal
94 }
95
96 pub fn permanent_removal(&mut self, permanent: bool) -> &mut Self {
100 self.data_mut().permanent_removal = permanent;
101 self
102 }
103}
104
105impl TransactionData for ContractDeleteTransactionData {}
106
107impl TransactionExecute for ContractDeleteTransactionData {
108 fn execute(
109 &self,
110 channel: Channel,
111 request: services::Transaction,
112 ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
113 Box::pin(async { SmartContractServiceClient::new(channel).delete_contract(request).await })
114 }
115}
116
117impl ValidateChecksums for ContractDeleteTransactionData {
118 fn validate_checksums(&self, ledger_id: &RefLedgerId) -> Result<(), Error> {
119 self.contract_id.validate_checksums(ledger_id)?;
120 self.transfer_account_id.validate_checksums(ledger_id)?;
121 self.transfer_contract_id.validate_checksums(ledger_id)
122 }
123}
124
125impl ToTransactionDataProtobuf for ContractDeleteTransactionData {
126 fn to_transaction_data_protobuf(
127 &self,
128 chunk_info: &ChunkInfo,
129 ) -> services::transaction_body::Data {
130 let _ = chunk_info.assert_single_transaction();
131
132 services::transaction_body::Data::ContractDeleteInstance(self.to_protobuf())
133 }
134}
135
136impl ToSchedulableTransactionDataProtobuf for ContractDeleteTransactionData {
137 fn to_schedulable_transaction_data_protobuf(
138 &self,
139 ) -> services::schedulable_transaction_body::Data {
140 services::schedulable_transaction_body::Data::ContractDeleteInstance(self.to_protobuf())
141 }
142}
143
144impl FromProtobuf<services::ContractDeleteTransactionBody> for ContractDeleteTransactionData {
145 fn from_protobuf(pb: services::ContractDeleteTransactionBody) -> crate::Result<Self> {
146 use services::contract_delete_transaction_body::Obtainers;
147
148 let (transfer_account_id, transfer_contract_id) = match pb.obtainers {
149 Some(Obtainers::TransferAccountId(it)) => (Some(AccountId::from_protobuf(it)?), None),
150 Some(Obtainers::TransferContractId(it)) => (None, Some(ContractId::from_protobuf(it)?)),
151 None => (None, None),
152 };
153
154 Ok(Self {
155 contract_id: Option::from_protobuf(pb.contract_id)?,
156 transfer_account_id,
157 transfer_contract_id,
158 permanent_removal: pb.permanent_removal,
159 })
160 }
161}
162
163impl From<ContractDeleteTransactionData> for AnyTransactionData {
164 fn from(transaction: ContractDeleteTransactionData) -> Self {
165 Self::ContractDelete(transaction)
166 }
167}
168
169impl ToProtobuf for ContractDeleteTransactionData {
170 type Protobuf = services::ContractDeleteTransactionBody;
171
172 fn to_protobuf(&self) -> Self::Protobuf {
173 let delete_contract_id = self.contract_id.to_protobuf();
174
175 let obtainers = match (&self.transfer_account_id, &self.transfer_contract_id) {
176 (Some(account_id), None) => {
177 Some(services::contract_delete_transaction_body::Obtainers::TransferAccountId(
178 account_id.to_protobuf(),
179 ))
180 }
181
182 (None, Some(contract_id)) => {
183 Some(services::contract_delete_transaction_body::Obtainers::TransferContractId(
184 contract_id.to_protobuf(),
185 ))
186 }
187
188 (Some(account_id), Some(_)) => {
189 Some(services::contract_delete_transaction_body::Obtainers::TransferAccountId(
190 account_id.to_protobuf(),
191 ))
192 }
193
194 _ => None,
195 };
196
197 services::ContractDeleteTransactionBody {
198 contract_id: delete_contract_id,
199 permanent_removal: self.permanent_removal,
200 obtainers,
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use expect_test::expect;
208 use hiero_sdk_proto::services;
209
210 use crate::contract::ContractDeleteTransactionData;
211 use crate::protobuf::{
212 FromProtobuf,
213 ToProtobuf,
214 };
215 use crate::transaction::test_helpers::{
216 check_body,
217 transaction_body,
218 };
219 use crate::{
220 AccountId,
221 AnyTransaction,
222 ContractDeleteTransaction,
223 ContractId,
224 };
225
226 const CONTRACT_ID: ContractId = ContractId::new(0, 0, 5007);
227 const TRANSFER_ACCOUNT_ID: AccountId = AccountId::new(0, 0, 9);
228 const TRANSFER_CONTRACT_ID: ContractId = ContractId::new(0, 0, 5008);
229
230 fn make_transaction() -> ContractDeleteTransaction {
231 let mut tx = ContractDeleteTransaction::new_for_tests();
232
233 tx.contract_id(CONTRACT_ID)
234 .transfer_account_id(TRANSFER_ACCOUNT_ID)
235 .transfer_contract_id(TRANSFER_CONTRACT_ID)
236 .freeze()
237 .unwrap();
238
239 tx
240 }
241
242 #[test]
243 fn serialize() {
244 let tx = make_transaction();
245
246 let tx = transaction_body(tx);
247
248 let tx = check_body(tx);
249
250 expect![[r#"
251 ContractDeleteInstance(
252 ContractDeleteTransactionBody {
253 contract_id: Some(
254 ContractId {
255 shard_num: 0,
256 realm_num: 0,
257 contract: Some(
258 ContractNum(
259 5007,
260 ),
261 ),
262 },
263 ),
264 permanent_removal: false,
265 obtainers: Some(
266 TransferContractId(
267 ContractId {
268 shard_num: 0,
269 realm_num: 0,
270 contract: Some(
271 ContractNum(
272 5008,
273 ),
274 ),
275 },
276 ),
277 ),
278 },
279 )
280 "#]]
281 .assert_debug_eq(&tx)
282 }
283
284 #[test]
285 fn to_from_bytes() {
286 let tx = make_transaction();
287
288 let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
289
290 let tx = transaction_body(tx);
291
292 let tx2 = transaction_body(tx2);
293
294 assert_eq!(tx, tx2);
295 }
296
297 #[test]
298 fn from_proto_body() {
299 let tx = services::ContractDeleteTransactionBody {
300 contract_id: Some(CONTRACT_ID.to_protobuf()),
301 obtainers: Some(
302 services::contract_delete_transaction_body::Obtainers::TransferAccountId(
303 TRANSFER_ACCOUNT_ID.to_protobuf(),
304 ),
305 ),
306 permanent_removal: false,
307 };
308
309 let tx = ContractDeleteTransactionData::from_protobuf(tx).unwrap();
310
311 assert_eq!(tx.contract_id, Some(CONTRACT_ID));
312 assert_eq!(tx.transfer_account_id, Some(TRANSFER_ACCOUNT_ID));
313 assert_eq!(tx.transfer_contract_id, None);
314 }
315
316 #[test]
317 fn get_set_contract_id() {
318 let mut tx = ContractDeleteTransaction::new();
319 tx.contract_id(CONTRACT_ID);
320
321 assert_eq!(tx.get_contract_id(), Some(CONTRACT_ID));
322 }
323
324 #[test]
325 #[should_panic]
326 fn get_set_contract_id_frozen_panics() {
327 make_transaction().contract_id(CONTRACT_ID);
328 }
329
330 #[test]
331 fn get_set_transfer_account_id() {
332 let mut tx = ContractDeleteTransaction::new();
333 tx.transfer_account_id(TRANSFER_ACCOUNT_ID);
334
335 assert_eq!(tx.get_transfer_account_id(), Some(TRANSFER_ACCOUNT_ID));
336 }
337
338 #[test]
339 #[should_panic]
340 fn get_set_transfer_account_id_frozen_panics() {
341 make_transaction().transfer_account_id(TRANSFER_ACCOUNT_ID);
342 }
343
344 #[test]
345 fn get_set_transfer_contract_id() {
346 let mut tx = ContractDeleteTransaction::new();
347 tx.transfer_contract_id(TRANSFER_CONTRACT_ID);
348
349 assert_eq!(tx.get_transfer_contract_id(), Some(TRANSFER_CONTRACT_ID));
350 }
351
352 #[test]
353 #[should_panic]
354 fn get_set_transfer_contract_id_frozen_panics() {
355 make_transaction().transfer_contract_id(TRANSFER_CONTRACT_ID);
356 }
357}