Skip to main content

hiero_sdk/token/
token_update_nfts_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    BoxGrpcFuture,
22    Error,
23    TokenId,
24    Transaction,
25    ValidateChecksums,
26};
27
28/// At consensus, updates an already created Non Fungible Token to the given values.
29///
30/// If no value is given for a field, that field is left unchanged.
31/// Only certain fields such as metadata can be updated.
32/// Updating the metadata of an NFT does not affect its ownership or transferability.
33/// This operation is intended for updating attributes of individual NFTs in a collection./
34/// --- Signing Requirements ---
35/// 1. To update metadata of an NFT, the metadata_key of the token should sign the transaction.
36pub type TokenUpdateNftsTransaction = Transaction<TokenUpdateNftsTransactionData>;
37
38#[derive(Debug, Clone, Default)]
39pub struct TokenUpdateNftsTransactionData {
40    /// The token to be updated.
41    token_id: Option<TokenId>,
42
43    /// The list of serial numbers to be updated.
44    serials: Vec<i64>,
45
46    /// Metadata of the created token definition.
47    metadata: Vec<u8>,
48}
49
50impl TokenUpdateNftsTransaction {
51    /// Returns the token for which to update NFTs.
52    #[must_use]
53    pub fn get_token_id(&self) -> Option<TokenId> {
54        self.data().token_id
55    }
56
57    /// Sets the token for which to update NFTs.
58    pub fn token_id(&mut self, token_id: impl Into<TokenId>) -> &mut Self {
59        self.data_mut().token_id = Some(token_id.into());
60        self
61    }
62
63    /// Returns the list of serial numbers to be updated.
64    #[must_use]
65    pub fn get_serials(&self) -> Vec<i64> {
66        self.data().serials.clone()
67    }
68
69    /// Sets the list of serial numbers to be updated.
70    pub fn serials(&mut self, serials: Vec<i64>) -> &mut Self {
71        self.data_mut().serials = serials;
72        self
73    }
74
75    /// Returns the new metadata of the NFT(s).
76    #[must_use]
77    pub fn get_metadata(&self) -> Vec<u8> {
78        self.data().metadata.clone()
79    }
80
81    /// Sets the new metadata of the NFT(s).
82    pub fn metadata(&mut self, metadata: Vec<u8>) -> &mut Self {
83        self.data_mut().metadata = metadata;
84        self
85    }
86}
87
88impl TransactionData for TokenUpdateNftsTransactionData {}
89
90impl TransactionExecute for TokenUpdateNftsTransactionData {
91    fn execute(
92        &self,
93        channel: Channel,
94        request: services::Transaction,
95    ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
96        Box::pin(async { TokenServiceClient::new(channel).update_token(request).await })
97    }
98}
99
100impl ValidateChecksums for TokenUpdateNftsTransactionData {
101    fn validate_checksums(&self, ledger_id: &RefLedgerId) -> Result<(), Error> {
102        self.token_id.validate_checksums(ledger_id)
103    }
104}
105
106impl ToTransactionDataProtobuf for TokenUpdateNftsTransactionData {
107    fn to_transaction_data_protobuf(
108        &self,
109        chunk_info: &ChunkInfo,
110    ) -> services::transaction_body::Data {
111        let _ = chunk_info.assert_single_transaction();
112
113        services::transaction_body::Data::TokenUpdateNfts(self.to_protobuf())
114    }
115}
116
117impl ToSchedulableTransactionDataProtobuf for TokenUpdateNftsTransactionData {
118    fn to_schedulable_transaction_data_protobuf(
119        &self,
120    ) -> services::schedulable_transaction_body::Data {
121        services::schedulable_transaction_body::Data::TokenUpdateNfts(self.to_protobuf())
122    }
123}
124
125impl From<TokenUpdateNftsTransactionData> for AnyTransactionData {
126    fn from(transaction: TokenUpdateNftsTransactionData) -> Self {
127        Self::TokenUpdateNfts(transaction)
128    }
129}
130
131impl FromProtobuf<services::TokenUpdateNftsTransactionBody> for TokenUpdateNftsTransactionData {
132    fn from_protobuf(pb: services::TokenUpdateNftsTransactionBody) -> crate::Result<Self> {
133        Ok(Self {
134            token_id: Option::from_protobuf(pb.token)?,
135            serials: pb.serial_numbers,
136            metadata: pb.metadata.unwrap_or_default(),
137        })
138    }
139}
140
141impl ToProtobuf for TokenUpdateNftsTransactionData {
142    type Protobuf = services::TokenUpdateNftsTransactionBody;
143
144    fn to_protobuf(&self) -> Self::Protobuf {
145        services::TokenUpdateNftsTransactionBody {
146            token: self.token_id.to_protobuf(),
147            serial_numbers: self.serials.clone(),
148            metadata: Some(self.metadata.clone()),
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use expect_test::expect_file;
156    use hiero_sdk_proto::services;
157
158    use super::TokenUpdateNftsTransactionData;
159    use crate::protobuf::{
160        FromProtobuf,
161        ToProtobuf,
162    };
163    use crate::transaction::test_helpers::{
164        check_body,
165        transaction_body,
166    };
167    use crate::{
168        AnyTransaction,
169        TokenId,
170        TokenUpdateNftsTransaction,
171    };
172
173    const TEST_TOKEN_ID: TokenId = TokenId::new(0, 0, 12);
174    const TEST_METADATA: &str = "Token Metadata";
175
176    fn test_serials() -> Vec<i64> {
177        vec![1, 2, 3]
178    }
179
180    fn make_transaction() -> TokenUpdateNftsTransaction {
181        let mut tx = TokenUpdateNftsTransaction::new_for_tests();
182
183        tx.token_id(TEST_TOKEN_ID)
184            .serials(test_serials())
185            .metadata(TEST_METADATA.as_bytes().to_vec())
186            .freeze()
187            .unwrap();
188
189        tx
190    }
191
192    #[test]
193    fn serialize() {
194        let tx = make_transaction();
195
196        let tx = transaction_body(tx);
197
198        let tx = check_body(tx);
199
200        expect_file!["./snapshots/token_update_nfts_transaction/serialize.txt"]
201            .assert_debug_eq(&tx);
202    }
203
204    #[test]
205    fn to_from_bytes() {
206        let tx = make_transaction();
207
208        let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
209
210        let tx = transaction_body(tx);
211        let tx2 = transaction_body(tx2);
212
213        assert_eq!(tx, tx2);
214    }
215
216    #[test]
217    fn from_proto_body() {
218        let tx = services::TokenUpdateNftsTransactionBody {
219            token: Some(TEST_TOKEN_ID.to_protobuf()),
220            serial_numbers: test_serials(),
221            metadata: Some(TEST_METADATA.to_owned().into()),
222        };
223
224        let tx = TokenUpdateNftsTransactionData::from_protobuf(tx).unwrap();
225
226        assert_eq!(tx.token_id, Some(TEST_TOKEN_ID));
227        assert_eq!(tx.metadata, TEST_METADATA.as_bytes().to_vec());
228        assert_eq!(tx.serials, test_serials())
229    }
230
231    #[test]
232    fn get_set_token_id() {
233        let mut tx = TokenUpdateNftsTransaction::new();
234        tx.token_id(TEST_TOKEN_ID);
235        assert_eq!(tx.get_token_id(), Some(TEST_TOKEN_ID));
236    }
237
238    #[test]
239    #[should_panic]
240    fn get_set_token_id_frozen_panic() {
241        let mut tx = make_transaction();
242        tx.token_id(TEST_TOKEN_ID);
243    }
244
245    #[test]
246    fn get_set_metadata() {
247        let mut tx = TokenUpdateNftsTransaction::new();
248        tx.metadata(TEST_METADATA.as_bytes().to_vec());
249        assert_eq!(tx.get_metadata(), TEST_METADATA.as_bytes().to_vec());
250    }
251
252    #[test]
253    #[should_panic]
254    fn get_set_metadata_frozen_panic() {
255        let mut tx = make_transaction();
256        tx.metadata(TEST_METADATA.as_bytes().to_vec());
257    }
258
259    #[test]
260    fn get_set_serials() {
261        let mut tx = TokenUpdateNftsTransaction::new();
262        tx.serials(test_serials());
263        assert_eq!(tx.get_serials(), test_serials());
264    }
265
266    #[test]
267    #[should_panic]
268    fn get_set_serials_frozen_panic() {
269        let mut tx = make_transaction();
270        tx.serials(test_serials());
271    }
272}