Skip to main content

hiero_sdk/token/
token_grant_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    Error,
24    TokenId,
25    Transaction,
26    ValidateChecksums,
27};
28
29/// Grants KYC to the account for the given token. Must be signed by the Token's kycKey.
30///
31/// Once executed the Account is marked as KYC Granted.
32///
33/// - If the provided account is not found, the transaction will resolve to `INVALID_ACCOUNT_ID`.
34/// - If the provided account has been deleted, the transaction will resolve to `ACCOUNT_DELETED`.
35/// - If the provided token is not found, the transaction will resolve to `INVALID_TOKEN_ID`.
36/// - If the provided token has been deleted, the transaction will resolve to `TOKEN_WAS_DELETED`.
37/// - If an Association between the provided token and account is not found, the transaction will
38/// resolve to `TOKEN_NOT_ASSOCIATED_TO_ACCOUNT`.
39/// - If no KYC Key is defined, the transaction will resolve to `TOKEN_HAS_NO_KYC_KEY`.
40pub type TokenGrantKycTransaction = Transaction<TokenGrantKycTransactionData>;
41
42#[derive(Debug, Clone, Default)]
43pub struct TokenGrantKycTransactionData {
44    /// The account to be granted KYC.
45    account_id: Option<AccountId>,
46
47    /// The token for which this account will be granted KYC.
48    token_id: Option<TokenId>,
49}
50
51impl TokenGrantKycTransaction {
52    /// Returns the account to be granted KYC.
53    #[must_use]
54    pub fn get_account_id(&self) -> Option<AccountId> {
55        self.data().account_id
56    }
57
58    /// Sets the account to be granted KYC.
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 be granted KYC.
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 the account will be granted KYC.
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 TokenGrantKycTransactionData {}
78
79impl TransactionExecute for TokenGrantKycTransactionData {
80    fn execute(
81        &self,
82        channel: Channel,
83        request: services::Transaction,
84    ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
85        Box::pin(async {
86            TokenServiceClient::new(channel).grant_kyc_to_token_account(request).await
87        })
88    }
89}
90
91impl ValidateChecksums for TokenGrantKycTransactionData {
92    fn validate_checksums(&self, ledger_id: &RefLedgerId) -> Result<(), Error> {
93        self.account_id.validate_checksums(ledger_id)?;
94        self.token_id.validate_checksums(ledger_id)
95    }
96}
97
98impl ToTransactionDataProtobuf for TokenGrantKycTransactionData {
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::TokenGrantKyc(self.to_protobuf())
106    }
107}
108
109impl ToSchedulableTransactionDataProtobuf for TokenGrantKycTransactionData {
110    fn to_schedulable_transaction_data_protobuf(
111        &self,
112    ) -> services::schedulable_transaction_body::Data {
113        services::schedulable_transaction_body::Data::TokenGrantKyc(self.to_protobuf())
114    }
115}
116
117impl From<TokenGrantKycTransactionData> for AnyTransactionData {
118    fn from(transaction: TokenGrantKycTransactionData) -> Self {
119        Self::TokenGrantKyc(transaction)
120    }
121}
122
123impl FromProtobuf<services::TokenGrantKycTransactionBody> for TokenGrantKycTransactionData {
124    fn from_protobuf(pb: services::TokenGrantKycTransactionBody) -> 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 TokenGrantKycTransactionData {
133    type Protobuf = services::TokenGrantKycTransactionBody;
134
135    fn to_protobuf(&self) -> Self::Protobuf {
136        services::TokenGrantKycTransactionBody {
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 crate::protobuf::{
149        FromProtobuf,
150        ToProtobuf,
151    };
152    use crate::token::TokenGrantKycTransactionData;
153    use crate::transaction::test_helpers::{
154        check_body,
155        transaction_body,
156    };
157    use crate::{
158        AccountId,
159        AnyTransaction,
160        TokenGrantKycTransaction,
161        TokenId,
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() -> TokenGrantKycTransaction {
169        let mut tx = TokenGrantKycTransaction::new_for_tests();
170
171        tx.account_id(TEST_ACCOUNT_ID).token_id(TEST_TOKEN_ID).freeze().unwrap();
172
173        tx
174    }
175
176    #[test]
177    fn serialize() {
178        let tx = make_transaction();
179
180        let tx = transaction_body(tx);
181
182        let tx = check_body(tx);
183
184        expect![[r#"
185            TokenGrantKyc(
186                TokenGrantKycTransactionBody {
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
219        let tx2 = transaction_body(tx2);
220
221        assert_eq!(tx, tx2);
222    }
223
224    #[test]
225    fn from_proto_body() {
226        let tx = services::TokenGrantKycTransactionBody {
227            account: Some(TEST_ACCOUNT_ID.to_protobuf()),
228            token: Some(TEST_TOKEN_ID.to_protobuf()),
229        };
230
231        let data = TokenGrantKycTransactionData::from_protobuf(tx).unwrap();
232
233        assert_eq!(data.account_id, Some(TEST_ACCOUNT_ID));
234        assert_eq!(data.token_id, Some(TEST_TOKEN_ID));
235    }
236
237    #[test]
238    fn get_set_account_id() {
239        let mut tx = TokenGrantKycTransaction::new();
240
241        tx.account_id(TEST_ACCOUNT_ID);
242
243        assert_eq!(tx.get_account_id(), Some(TEST_ACCOUNT_ID));
244    }
245
246    #[test]
247    #[should_panic]
248    fn get_set_account_id_frozen_panic() {
249        let mut tx = make_transaction();
250
251        tx.account_id(TEST_ACCOUNT_ID);
252    }
253
254    #[test]
255    fn get_set_token_id() {
256        let mut tx = TokenGrantKycTransaction::new();
257
258        tx.token_id(TEST_TOKEN_ID);
259
260        assert_eq!(tx.get_token_id(), Some(TEST_TOKEN_ID));
261    }
262
263    #[test]
264    #[should_panic]
265    fn get_set_token_id_frozen_panic() {
266        let mut tx = make_transaction();
267
268        tx.token_id(TEST_TOKEN_ID);
269    }
270}