Skip to main content

hiero_sdk/token/
token_revoke_kyc_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    TokenId,
24    Transaction,
25    ValidateChecksums,
26};
27
28/// Revokes KYC from the account for the given token.
29///
30/// Must be signed by the Token's kycKey.
31///
32/// Once executed the Account is marked as KYC Revoked.
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 KYC Key is defined, the transaction will resolve to `TOKEN_HAS_NO_KYC_KEY`.
41pub type TokenRevokeKycTransaction = Transaction<TokenRevokeKycTransactionData>;
42
43#[derive(Debug, Clone, Default)]
44pub struct TokenRevokeKycTransactionData {
45    /// The account to have their KYC revoked.
46    account_id: Option<AccountId>,
47
48    /// The token for which this account will have their KYC revoked.
49    token_id: Option<TokenId>,
50}
51
52impl TokenRevokeKycTransaction {
53    /// Returns the account to have their KYC revoked.
54    #[must_use]
55    pub fn get_account_id(&self) -> Option<AccountId> {
56        self.data().account_id
57    }
58    /// Sets the account to have their KYC revoked.
59    pub fn account_id(&mut self, account_id: AccountId) -> &mut Self {
60        self.data_mut().account_id = Some(account_id);
61        self
62    }
63
64    /// Returns the token for which the account will have their KYC revoked.
65    #[must_use]
66    pub fn get_token_id(&self) -> Option<TokenId> {
67        self.data().token_id
68    }
69
70    /// Sets the token for which this account will have their KYC revoked.
71    pub fn token_id(&mut self, token_id: impl Into<TokenId>) -> &mut Self {
72        self.data_mut().token_id = Some(token_id.into());
73        self
74    }
75}
76
77impl TransactionData for TokenRevokeKycTransactionData {}
78
79impl TransactionExecute for TokenRevokeKycTransactionData {
80    fn execute(
81        &self,
82        channel: Channel,
83        request: services::Transaction,
84    ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
85        Box::pin(async {
86            TokenServiceClient::new(channel).revoke_kyc_from_token_account(request).await
87        })
88    }
89}
90
91impl ValidateChecksums for TokenRevokeKycTransactionData {
92    fn validate_checksums(&self, ledger_id: &RefLedgerId) -> crate::Result<()> {
93        self.token_id.validate_checksums(ledger_id)?;
94        self.account_id.validate_checksums(ledger_id)
95    }
96}
97
98impl ToTransactionDataProtobuf for TokenRevokeKycTransactionData {
99    fn to_transaction_data_protobuf(
100        &self,
101        chunk_info: &ChunkInfo,
102    ) -> services::transaction_body::Data {
103        let _ = chunk_info.assert_single_transaction();
104
105        services::transaction_body::Data::TokenRevokeKyc(self.to_protobuf())
106    }
107}
108
109impl ToSchedulableTransactionDataProtobuf for TokenRevokeKycTransactionData {
110    fn to_schedulable_transaction_data_protobuf(
111        &self,
112    ) -> services::schedulable_transaction_body::Data {
113        services::schedulable_transaction_body::Data::TokenRevokeKyc(self.to_protobuf())
114    }
115}
116
117impl From<TokenRevokeKycTransactionData> for AnyTransactionData {
118    fn from(transaction: TokenRevokeKycTransactionData) -> Self {
119        Self::TokenRevokeKyc(transaction)
120    }
121}
122
123impl FromProtobuf<services::TokenRevokeKycTransactionBody> for TokenRevokeKycTransactionData {
124    fn from_protobuf(pb: services::TokenRevokeKycTransactionBody) -> crate::Result<Self> {
125        Ok(Self {
126            account_id: Option::from_protobuf(pb.account)?,
127            token_id: Option::from_protobuf(pb.token)?,
128        })
129    }
130}
131
132impl ToProtobuf for TokenRevokeKycTransactionData {
133    type Protobuf = services::TokenRevokeKycTransactionBody;
134
135    fn to_protobuf(&self) -> Self::Protobuf {
136        services::TokenRevokeKycTransactionBody {
137            token: self.token_id.to_protobuf(),
138            account: self.account_id.to_protobuf(),
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use expect_test::expect;
146    use hiero_sdk_proto::services;
147
148    use super::TokenRevokeKycTransactionData;
149    use crate::protobuf::{
150        FromProtobuf,
151        ToProtobuf,
152    };
153    use crate::transaction::test_helpers::{
154        check_body,
155        transaction_body,
156    };
157    use crate::{
158        AccountId,
159        AnyTransaction,
160        TokenId,
161        TokenRevokeKycTransaction,
162    };
163
164    const TEST_TOKEN_ID: TokenId = TokenId::new(4, 2, 0);
165    const TEST_ACCOUNT_ID: AccountId =
166        AccountId { shard: 6, realm: 9, num: 0, alias: None, evm_address: None, checksum: None };
167
168    fn make_transaction() -> TokenRevokeKycTransaction {
169        let mut tx = TokenRevokeKycTransaction::new_for_tests();
170
171        tx.token_id(TEST_TOKEN_ID).account_id(TEST_ACCOUNT_ID).freeze().unwrap();
172
173        tx
174    }
175
176    #[test]
177    fn seriralize() {
178        let tx = make_transaction();
179
180        let tx = transaction_body(tx);
181
182        let tx = check_body(tx);
183
184        expect![[r#"
185            TokenRevokeKyc(
186                TokenRevokeKycTransactionBody {
187                    token: Some(
188                        TokenId {
189                            shard_num: 4,
190                            realm_num: 2,
191                            token_num: 0,
192                        },
193                    ),
194                    account: Some(
195                        AccountId {
196                            shard_num: 6,
197                            realm_num: 9,
198                            account: Some(
199                                AccountNum(
200                                    0,
201                                ),
202                            ),
203                        },
204                    ),
205                },
206            )
207        "#]]
208        .assert_debug_eq(&tx);
209    }
210
211    #[test]
212    fn to_from_bytes() {
213        let tx = make_transaction();
214
215        let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
216
217        let tx = transaction_body(tx);
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::TokenRevokeKycTransactionBody {
226            token: Some(TEST_TOKEN_ID.to_protobuf()),
227            account: Some(TEST_ACCOUNT_ID.to_protobuf()),
228        };
229
230        let tx = TokenRevokeKycTransactionData::from_protobuf(tx).unwrap();
231
232        assert_eq!(tx.account_id, Some(TEST_ACCOUNT_ID));
233        assert_eq!(tx.token_id, Some(TEST_TOKEN_ID));
234    }
235
236    #[test]
237    fn get_set_account_id() {
238        let mut tx = TokenRevokeKycTransaction::new();
239        tx.account_id(TEST_ACCOUNT_ID);
240
241        assert_eq!(tx.get_account_id(), Some(TEST_ACCOUNT_ID));
242    }
243
244    #[test]
245    #[should_panic]
246    fn get_set_account_id_frozen_panic() {
247        let mut tx = make_transaction();
248        tx.account_id(TEST_ACCOUNT_ID);
249    }
250
251    #[test]
252    fn get_set_token_id() {
253        let mut tx = TokenRevokeKycTransaction::new();
254        tx.token_id(TEST_TOKEN_ID);
255
256        assert_eq!(tx.get_token_id(), Some(TEST_TOKEN_ID));
257    }
258
259    #[test]
260    #[should_panic]
261    fn get_set_token_id_frozen_panic() {
262        let mut tx = make_transaction();
263        tx.token_id(TEST_TOKEN_ID);
264    }
265}