1use hiero_sdk_proto::services;
4use hiero_sdk_proto::services::token_service_client::TokenServiceClient;
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 Error,
24 TokenId,
25 Transaction,
26 ValidateChecksums,
27};
28
29pub type TokenWipeTransaction = Transaction<TokenWipeTransactionData>;
58
59#[derive(Debug, Clone, Default)]
60pub struct TokenWipeTransactionData {
61 account_id: Option<AccountId>,
63
64 token_id: Option<TokenId>,
66
67 amount: Option<u64>,
70
71 serials: Vec<u64>,
73}
74
75impl TokenWipeTransaction {
76 #[must_use]
78 pub fn get_account_id(&self) -> Option<AccountId> {
79 self.data().account_id
80 }
81
82 pub fn account_id(&mut self, account_id: AccountId) -> &mut Self {
84 self.data_mut().account_id = Some(account_id);
85 self
86 }
87
88 #[must_use]
90 pub fn get_token_id(&self) -> Option<TokenId> {
91 self.data().token_id
92 }
93
94 pub fn token_id(&mut self, token_id: impl Into<TokenId>) -> &mut Self {
96 self.data_mut().token_id = Some(token_id.into());
97 self
98 }
99
100 #[must_use]
102 pub fn get_amount(&self) -> Option<u64> {
103 self.data().amount
104 }
105
106 pub fn amount(&mut self, amount: impl Into<u64>) -> &mut Self {
109 self.data_mut().amount = Some(amount.into());
110 self
111 }
112
113 #[must_use]
115 pub fn get_serials(&self) -> &[u64] {
116 &self.data().serials
117 }
118
119 pub fn serials(&mut self, serials: impl IntoIterator<Item = u64>) -> &mut Self {
121 self.data_mut().serials = serials.into_iter().collect();
122 self
123 }
124}
125
126impl TransactionData for TokenWipeTransactionData {}
127
128impl TransactionExecute for TokenWipeTransactionData {
129 fn execute(
130 &self,
131 channel: Channel,
132 request: services::Transaction,
133 ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
134 Box::pin(async { TokenServiceClient::new(channel).wipe_token_account(request).await })
135 }
136}
137
138impl ValidateChecksums for TokenWipeTransactionData {
139 fn validate_checksums(&self, ledger_id: &RefLedgerId) -> Result<(), Error> {
140 self.account_id.validate_checksums(ledger_id)?;
141 self.token_id.validate_checksums(ledger_id)
142 }
143}
144
145impl ToTransactionDataProtobuf for TokenWipeTransactionData {
146 fn to_transaction_data_protobuf(
147 &self,
148 chunk_info: &ChunkInfo,
149 ) -> services::transaction_body::Data {
150 let _ = chunk_info.assert_single_transaction();
151
152 services::transaction_body::Data::TokenWipe(self.to_protobuf())
153 }
154}
155
156impl ToSchedulableTransactionDataProtobuf for TokenWipeTransactionData {
157 fn to_schedulable_transaction_data_protobuf(
158 &self,
159 ) -> services::schedulable_transaction_body::Data {
160 services::schedulable_transaction_body::Data::TokenWipe(self.to_protobuf())
161 }
162}
163
164impl From<TokenWipeTransactionData> for AnyTransactionData {
165 fn from(transaction: TokenWipeTransactionData) -> Self {
166 Self::TokenWipe(transaction)
167 }
168}
169
170impl FromProtobuf<services::TokenWipeAccountTransactionBody> for TokenWipeTransactionData {
171 fn from_protobuf(pb: services::TokenWipeAccountTransactionBody) -> crate::Result<Self> {
172 Ok(Self {
173 account_id: Option::from_protobuf(pb.account)?,
174 token_id: Option::from_protobuf(pb.token)?,
175 amount: Some(pb.amount),
176 serials: pb.serial_numbers.into_iter().map(|it| it as u64).collect(),
177 })
178 }
179}
180impl ToProtobuf for TokenWipeTransactionData {
181 type Protobuf = services::TokenWipeAccountTransactionBody;
182
183 fn to_protobuf(&self) -> Self::Protobuf {
184 services::TokenWipeAccountTransactionBody {
185 token: self.token_id.to_protobuf(),
186 account: self.account_id.to_protobuf(),
187 amount: self.amount.unwrap_or_default(),
188 serial_numbers: self.serials.iter().map(|num| *num as i64).collect(),
189 }
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use expect_test::expect;
196 use hiero_sdk_proto::services;
197
198 use crate::protobuf::{
199 FromProtobuf,
200 ToProtobuf,
201 };
202 use crate::transaction::test_helpers::{
203 check_body,
204 transaction_body,
205 };
206 use crate::{
207 AccountId,
208 AnyTransaction,
209 TokenId,
210 TokenWipeTransaction,
211 };
212
213 const TEST_ACCOUNT_ID: AccountId = AccountId::new(0, 6, 9);
214 const TEST_TOKEN_ID: TokenId = TokenId::new(4, 2, 0);
215 const TEST_AMOUNT: u64 = 4;
216 const TEST_SERIALS: [u64; 3] = [8, 9, 10];
217 fn make_transaction() -> TokenWipeTransaction {
218 let mut tx = TokenWipeTransaction::new_for_tests();
219
220 tx.token_id(TEST_TOKEN_ID)
221 .account_id(TEST_ACCOUNT_ID)
222 .amount(TEST_AMOUNT)
223 .freeze()
224 .unwrap();
225
226 tx
227 }
228
229 fn make_transaction_nft() -> TokenWipeTransaction {
230 let mut tx = TokenWipeTransaction::new_for_tests();
231
232 tx.token_id(TEST_TOKEN_ID)
233 .account_id(TEST_ACCOUNT_ID)
234 .serials(TEST_SERIALS)
235 .freeze()
236 .unwrap();
237
238 tx
239 }
240
241 #[test]
242 fn serialize_fungible() {
243 let tx = make_transaction();
244
245 let tx = transaction_body(tx);
246
247 let tx = check_body(tx);
248
249 expect![[r#"
250 TokenWipe(
251 TokenWipeAccountTransactionBody {
252 token: Some(
253 TokenId {
254 shard_num: 4,
255 realm_num: 2,
256 token_num: 0,
257 },
258 ),
259 account: Some(
260 AccountId {
261 shard_num: 0,
262 realm_num: 6,
263 account: Some(
264 AccountNum(
265 9,
266 ),
267 ),
268 },
269 ),
270 amount: 4,
271 serial_numbers: [],
272 },
273 )
274 "#]]
275 .assert_debug_eq(&tx);
276 }
277
278 #[test]
279 fn to_from_bytes_fungible() {
280 let tx = make_transaction();
281
282 let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
283
284 let tx = transaction_body(tx);
285 let tx2 = transaction_body(tx2);
286
287 assert_eq!(tx, tx2);
288 }
289
290 #[test]
291 fn serialize_nft() {
292 let tx = make_transaction_nft();
293
294 let tx = transaction_body(tx);
295
296 let tx = check_body(tx);
297
298 expect![[r#"
299 TokenWipe(
300 TokenWipeAccountTransactionBody {
301 token: Some(
302 TokenId {
303 shard_num: 4,
304 realm_num: 2,
305 token_num: 0,
306 },
307 ),
308 account: Some(
309 AccountId {
310 shard_num: 0,
311 realm_num: 6,
312 account: Some(
313 AccountNum(
314 9,
315 ),
316 ),
317 },
318 ),
319 amount: 0,
320 serial_numbers: [
321 8,
322 9,
323 10,
324 ],
325 },
326 )
327 "#]]
328 .assert_debug_eq(&tx);
329 }
330
331 #[test]
332 fn to_from_bytes_nft() {
333 let tx = make_transaction_nft();
334
335 let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
336
337 let tx = transaction_body(tx);
338 let tx2 = transaction_body(tx2);
339
340 assert_eq!(tx, tx2);
341 }
342
343 #[test]
344 fn from_proto_body() {
345 let tx = services::TokenWipeAccountTransactionBody {
346 token: Some(TEST_TOKEN_ID.to_protobuf()),
347 account: Some(TEST_ACCOUNT_ID.to_protobuf()),
348 amount: TEST_AMOUNT,
349 serial_numbers: TEST_SERIALS.into_iter().map(|it| it as i64).collect(),
350 };
351
352 let tx = super::TokenWipeTransactionData::from_protobuf(tx).unwrap();
353
354 assert_eq!(tx.token_id, Some(TEST_TOKEN_ID));
355 assert_eq!(tx.account_id, Some(TEST_ACCOUNT_ID));
356 assert_eq!(tx.amount, Some(TEST_AMOUNT));
357 assert_eq!(tx.serials, TEST_SERIALS);
358 }
359
360 #[test]
361 fn get_set_token_id() {
362 let mut tx = TokenWipeTransaction::new();
363 tx.token_id(TEST_TOKEN_ID);
364
365 assert_eq!(tx.get_token_id(), Some(TEST_TOKEN_ID));
366 }
367
368 #[test]
369 #[should_panic]
370 fn get_set_token_id_frozen_panic() {
371 let mut tx = make_transaction();
372 tx.token_id(TEST_TOKEN_ID);
373 }
374
375 #[test]
376 fn get_set_account_id() {
377 let mut tx = TokenWipeTransaction::new();
378 tx.account_id(TEST_ACCOUNT_ID);
379
380 assert_eq!(tx.get_account_id(), Some(TEST_ACCOUNT_ID));
381 }
382
383 #[test]
384 #[should_panic]
385 fn get_set_account_id_frozen_panic() {
386 let mut tx = make_transaction();
387 tx.account_id(TEST_ACCOUNT_ID);
388 }
389
390 #[test]
391 fn get_set_amount() {
392 let mut tx = TokenWipeTransaction::new();
393 tx.amount(TEST_AMOUNT);
394
395 assert_eq!(tx.get_amount(), Some(TEST_AMOUNT));
396 }
397
398 #[test]
399 #[should_panic]
400 fn get_set_amount_frozen_panic() {
401 let mut tx = make_transaction();
402 tx.amount(TEST_AMOUNT);
403 }
404
405 #[test]
406 fn get_set_serial_numbers() {
407 let mut tx = TokenWipeTransaction::new();
408 tx.serials(TEST_SERIALS);
409
410 assert_eq!(tx.get_serials(), TEST_SERIALS);
411 }
412
413 #[test]
414 #[should_panic]
415 fn get_set_serial_numbers_frozen_panic() {
416 let mut tx = make_transaction_nft();
417 tx.serials(TEST_SERIALS);
418 }
419}