Skip to main content

hiero_sdk/token/
token_freeze_transaction.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use 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
29/// Freezes transfers of the specified token for the account. Must be signed by the Token's freezeKey.
30///
31/// Once executed the Account is marked as Frozen and will not be able to receive or send tokens
32/// unless unfrozen. The operation is idempotent.
33///
34/// - If the provided account is not found, the transaction will resolve to `INVALID_ACCOUNT_ID`.
35/// - If the provided account has been deleted, the transaction will resolve to `ACCOUNT_DELETED`.
36/// - If the provided token is not found, the transaction will resolve to `INVALID_TOKEN_ID`.
37/// - If the provided token has been deleted, the transaction will resolve to `TOKEN_WAS_DELETED`.
38/// - If an Association between the provided token and account is not found, the transaction will
39/// resolve to `TOKEN_NOT_ASSOCIATED_TO_ACCOUNT`.
40/// - If no Freeze Key is defined, the transaction will resolve to `TOKEN_HAS_NO_FREEZE_KEY`.
41pub type TokenFreezeTransaction = Transaction<TokenFreezeTransactionData>;
42
43#[derive(Debug, Clone, Default)]
44pub struct TokenFreezeTransactionData {
45    /// The account to be frozen.
46    account_id: Option<AccountId>,
47
48    /// The token for which this account will be frozen.
49    token_id: Option<TokenId>,
50}
51
52impl TokenFreezeTransaction {
53    /// Returns the account to be frozen.
54    #[must_use]
55    pub fn get_account_id(&self) -> Option<AccountId> {
56        self.data().account_id
57    }
58
59    /// Sets the account to be frozen.
60    pub fn account_id(&mut self, account_id: AccountId) -> &mut Self {
61        self.data_mut().account_id = Some(account_id);
62        self
63    }
64
65    /// Returns the toke nfor which the account will be frozen.
66    #[must_use]
67    pub fn get_token_id(&self) -> Option<TokenId> {
68        self.data().token_id
69    }
70
71    /// Sets the token for which the account will be frozen.
72    pub fn token_id(&mut self, token_id: impl Into<TokenId>) -> &mut Self {
73        self.data_mut().token_id = Some(token_id.into());
74        self
75    }
76}
77
78impl TransactionData for TokenFreezeTransactionData {}
79
80impl TransactionExecute for TokenFreezeTransactionData {
81    fn execute(
82        &self,
83        channel: Channel,
84        request: services::Transaction,
85    ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
86        Box::pin(async { TokenServiceClient::new(channel).freeze_token_account(request).await })
87    }
88}
89
90impl ValidateChecksums for TokenFreezeTransactionData {
91    fn validate_checksums(&self, ledger_id: &RefLedgerId) -> Result<(), Error> {
92        self.account_id.validate_checksums(ledger_id)?;
93        self.token_id.validate_checksums(ledger_id)
94    }
95}
96
97impl ToTransactionDataProtobuf for TokenFreezeTransactionData {
98    fn to_transaction_data_protobuf(
99        &self,
100        chunk_info: &ChunkInfo,
101    ) -> services::transaction_body::Data {
102        let _ = chunk_info.assert_single_transaction();
103
104        services::transaction_body::Data::TokenFreeze(self.to_protobuf())
105    }
106}
107
108impl ToSchedulableTransactionDataProtobuf for TokenFreezeTransactionData {
109    fn to_schedulable_transaction_data_protobuf(
110        &self,
111    ) -> services::schedulable_transaction_body::Data {
112        services::schedulable_transaction_body::Data::TokenFreeze(self.to_protobuf())
113    }
114}
115
116impl From<TokenFreezeTransactionData> for AnyTransactionData {
117    fn from(transaction: TokenFreezeTransactionData) -> Self {
118        Self::TokenFreeze(transaction)
119    }
120}
121
122impl FromProtobuf<services::TokenFreezeAccountTransactionBody> for TokenFreezeTransactionData {
123    fn from_protobuf(pb: services::TokenFreezeAccountTransactionBody) -> crate::Result<Self> {
124        Ok(Self {
125            account_id: Option::from_protobuf(pb.account)?,
126            token_id: Option::from_protobuf(pb.token)?,
127        })
128    }
129}
130
131impl ToProtobuf for TokenFreezeTransactionData {
132    type Protobuf = services::TokenFreezeAccountTransactionBody;
133
134    fn to_protobuf(&self) -> Self::Protobuf {
135        let account = self.account_id.to_protobuf();
136        let token = self.token_id.to_protobuf();
137
138        services::TokenFreezeAccountTransactionBody { token, account }
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use expect_test::expect;
145    use hiero_sdk_proto::services;
146
147    use crate::protobuf::{
148        FromProtobuf,
149        ToProtobuf,
150    };
151    use crate::token::TokenFreezeTransactionData;
152    use crate::transaction::test_helpers::{
153        check_body,
154        transaction_body,
155    };
156    use crate::{
157        AccountId,
158        AnyTransaction,
159        TokenFreezeTransaction,
160        TokenId,
161    };
162
163    const ACCOUNT_ID: AccountId = AccountId::new(0, 0, 222);
164
165    const TOKEN_ID: TokenId = TokenId::new(5, 3, 3);
166
167    fn make_transaction() -> TokenFreezeTransaction {
168        let mut tx = TokenFreezeTransaction::new_for_tests();
169
170        tx.account_id(ACCOUNT_ID).token_id(TOKEN_ID).freeze().unwrap();
171
172        tx
173    }
174
175    #[test]
176    fn serialize() {
177        let tx = make_transaction();
178
179        let tx = transaction_body(tx);
180
181        let tx = check_body(tx);
182
183        expect![[r#"
184            TokenFreeze(
185                TokenFreezeAccountTransactionBody {
186                    token: Some(
187                        TokenId {
188                            shard_num: 5,
189                            realm_num: 3,
190                            token_num: 3,
191                        },
192                    ),
193                    account: Some(
194                        AccountId {
195                            shard_num: 0,
196                            realm_num: 0,
197                            account: Some(
198                                AccountNum(
199                                    222,
200                                ),
201                            ),
202                        },
203                    ),
204                },
205            )
206        "#]]
207        .assert_debug_eq(&tx)
208    }
209
210    #[test]
211    fn to_from_bytes() {
212        let tx = make_transaction();
213
214        let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
215
216        let tx = transaction_body(tx);
217
218        let tx2 = transaction_body(tx2);
219
220        assert_eq!(tx, tx2);
221    }
222
223    #[test]
224    fn from_proto_body() {
225        let tx = services::TokenFreezeAccountTransactionBody {
226            account: Some(ACCOUNT_ID.to_protobuf()),
227            token: Some(TOKEN_ID.to_protobuf()),
228        };
229
230        let data = TokenFreezeTransactionData::from_protobuf(tx).unwrap();
231
232        assert_eq!(data.account_id, Some(ACCOUNT_ID));
233        assert_eq!(data.token_id, Some(TOKEN_ID));
234    }
235
236    #[test]
237    fn get_set_token_id() {
238        let mut tx = TokenFreezeTransaction::new();
239        tx.token_id(TOKEN_ID);
240
241        assert_eq!(tx.get_token_id(), Some(TOKEN_ID));
242    }
243
244    #[test]
245    #[should_panic]
246    fn get_set_token_id_frozen_panic() {
247        make_transaction().token_id(TOKEN_ID);
248    }
249
250    #[test]
251    fn get_set_account_id() {
252        let mut tx = TokenFreezeTransaction::new();
253        tx.account_id(ACCOUNT_ID);
254
255        assert_eq!(tx.get_account_id(), Some(ACCOUNT_ID));
256    }
257
258    #[test]
259    #[should_panic]
260    fn get_set_account_id_frozen_panic() {
261        make_transaction().account_id(ACCOUNT_ID);
262    }
263}