hiero_sdk/token/
token_dissociate_transaction.rs1use 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 TokenDissociateTransaction = Transaction<TokenDissociateTransactionData>;
47
48#[derive(Debug, Clone, Default)]
49pub struct TokenDissociateTransactionData {
50 account_id: Option<AccountId>,
52
53 token_ids: Vec<TokenId>,
55}
56
57impl TokenDissociateTransaction {
58 #[must_use]
60 pub fn get_account_id(&self) -> Option<AccountId> {
61 self.data().account_id
62 }
63
64 pub fn account_id(&mut self, account_id: AccountId) -> &mut Self {
66 self.data_mut().account_id = Some(account_id);
67 self
68 }
69
70 #[must_use]
72 pub fn get_token_ids(&self) -> &[TokenId] {
73 &self.data().token_ids
74 }
75
76 pub fn token_ids(&mut self, token_ids: impl IntoIterator<Item = TokenId>) -> &mut Self {
78 self.data_mut().token_ids = token_ids.into_iter().collect();
79 self
80 }
81}
82
83impl TransactionData for TokenDissociateTransactionData {}
84
85impl TransactionExecute for TokenDissociateTransactionData {
86 fn execute(
87 &self,
88 channel: Channel,
89 request: services::Transaction,
90 ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
91 Box::pin(async { TokenServiceClient::new(channel).dissociate_tokens(request).await })
92 }
93}
94
95impl ValidateChecksums for TokenDissociateTransactionData {
96 fn validate_checksums(&self, ledger_id: &RefLedgerId) -> Result<(), Error> {
97 self.account_id.validate_checksums(ledger_id)?;
98 for token_id in &self.token_ids {
99 token_id.validate_checksums(ledger_id)?;
100 }
101 Ok(())
102 }
103}
104
105impl ToTransactionDataProtobuf for TokenDissociateTransactionData {
106 fn to_transaction_data_protobuf(
107 &self,
108 chunk_info: &ChunkInfo,
109 ) -> services::transaction_body::Data {
110 let _ = chunk_info.assert_single_transaction();
111
112 services::transaction_body::Data::TokenDissociate(self.to_protobuf())
113 }
114}
115
116impl ToSchedulableTransactionDataProtobuf for TokenDissociateTransactionData {
117 fn to_schedulable_transaction_data_protobuf(
118 &self,
119 ) -> services::schedulable_transaction_body::Data {
120 services::schedulable_transaction_body::Data::TokenDissociate(self.to_protobuf())
121 }
122}
123
124impl From<TokenDissociateTransactionData> for AnyTransactionData {
125 fn from(transaction: TokenDissociateTransactionData) -> Self {
126 Self::TokenDissociate(transaction)
127 }
128}
129
130impl FromProtobuf<services::TokenDissociateTransactionBody> for TokenDissociateTransactionData {
131 fn from_protobuf(pb: services::TokenDissociateTransactionBody) -> crate::Result<Self> {
132 Ok(Self {
133 account_id: Option::from_protobuf(pb.account)?,
134 token_ids: Vec::from_protobuf(pb.tokens)?,
135 })
136 }
137}
138
139impl ToProtobuf for TokenDissociateTransactionData {
140 type Protobuf = services::TokenDissociateTransactionBody;
141
142 fn to_protobuf(&self) -> Self::Protobuf {
143 let account = self.account_id.to_protobuf();
144 let tokens = self.token_ids.to_protobuf();
145
146 services::TokenDissociateTransactionBody { account, tokens }
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use expect_test::expect;
153 use hiero_sdk_proto::services;
154
155 use crate::protobuf::{
156 FromProtobuf,
157 ToProtobuf,
158 };
159 use crate::token::TokenDissociateTransactionData;
160 use crate::transaction::test_helpers::{
161 check_body,
162 transaction_body,
163 };
164 use crate::{
165 AccountId,
166 AnyTransaction,
167 TokenDissociateTransaction,
168 TokenId,
169 };
170
171 const TEST_ACCOUNT_ID: AccountId =
172 AccountId { shard: 6, realm: 9, num: 0, alias: None, evm_address: None, checksum: None };
173
174 const TEST_TOKEN_IDS: [TokenId; 3] =
175 [TokenId::new(4, 2, 0), TokenId::new(4, 2, 1), TokenId::new(4, 2, 2)];
176
177 fn make_transaction() -> TokenDissociateTransaction {
178 let mut tx = TokenDissociateTransaction::new_for_tests();
179
180 tx.account_id(TEST_ACCOUNT_ID).token_ids(TEST_TOKEN_IDS).freeze().unwrap();
181
182 tx
183 }
184
185 #[test]
186 fn serialize() {
187 let tx = make_transaction();
188
189 let tx = transaction_body(tx);
190
191 let tx = check_body(tx);
192
193 expect![[r#"
194 TokenDissociate(
195 TokenDissociateTransactionBody {
196 account: Some(
197 AccountId {
198 shard_num: 6,
199 realm_num: 9,
200 account: Some(
201 AccountNum(
202 0,
203 ),
204 ),
205 },
206 ),
207 tokens: [
208 TokenId {
209 shard_num: 4,
210 realm_num: 2,
211 token_num: 0,
212 },
213 TokenId {
214 shard_num: 4,
215 realm_num: 2,
216 token_num: 1,
217 },
218 TokenId {
219 shard_num: 4,
220 realm_num: 2,
221 token_num: 2,
222 },
223 ],
224 },
225 )
226 "#]]
227 .assert_debug_eq(&tx)
228 }
229
230 #[test]
231 fn to_from_bytes() {
232 let tx = make_transaction();
233
234 let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
235
236 let tx = transaction_body(tx);
237
238 let tx2 = transaction_body(tx2);
239
240 assert_eq!(tx, tx2);
241 }
242
243 #[test]
244 fn from_proto_body() {
245 let tx = services::TokenDissociateTransactionBody {
246 account: Some(TEST_ACCOUNT_ID.to_protobuf()),
247 tokens: TEST_TOKEN_IDS.iter().map(TokenId::to_protobuf).collect(),
248 };
249
250 let data = TokenDissociateTransactionData::from_protobuf(tx).unwrap();
251
252 assert_eq!(data.account_id, Some(TEST_ACCOUNT_ID));
253 assert_eq!(data.token_ids, TEST_TOKEN_IDS);
254 }
255
256 #[test]
257 fn get_set_account_id() {
258 let mut tx = TokenDissociateTransaction::new();
259 tx.account_id(TEST_ACCOUNT_ID);
260
261 assert_eq!(tx.get_account_id(), Some(TEST_ACCOUNT_ID));
262 }
263
264 #[test]
265 #[should_panic]
266 fn get_set_account_id_frozen_panic() {
267 make_transaction().account_id(TEST_ACCOUNT_ID);
268 }
269
270 #[test]
271 fn get_set_token_ids() {
272 let mut tx = TokenDissociateTransaction::new();
273 tx.token_ids(TEST_TOKEN_IDS);
274
275 assert_eq!(tx.get_token_ids(), &TEST_TOKEN_IDS);
276 }
277
278 #[test]
279 #[should_panic]
280 fn get_set_token_ids_frozen_panic() {
281 make_transaction().token_ids(TEST_TOKEN_IDS);
282 }
283}