1use hedera_proto::services;
4use hedera_proto::services::token_service_client::TokenServiceClient;
5use tonic::transport::Channel;
6
7use crate::protobuf::{
8 FromProtobuf,
9 ToProtobuf,
10};
11use crate::token::custom_fees::AnyCustomFee;
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
28pub type TokenFeeScheduleUpdateTransaction = Transaction<TokenFeeScheduleUpdateTransactionData>;
37
38#[derive(Debug, Clone, Default)]
39pub struct TokenFeeScheduleUpdateTransactionData {
40 token_id: Option<TokenId>,
42
43 custom_fees: Vec<AnyCustomFee>,
45}
46
47impl TokenFeeScheduleUpdateTransaction {
48 #[must_use]
50 pub fn get_token_id(&self) -> Option<TokenId> {
51 self.data().token_id
52 }
53
54 pub fn token_id(&mut self, token_id: impl Into<TokenId>) -> &mut Self {
57 self.data_mut().token_id = Some(token_id.into());
58 self
59 }
60
61 #[must_use]
63 pub fn get_custom_fees(&self) -> &[AnyCustomFee] {
64 &self.data().custom_fees
65 }
66
67 pub fn custom_fees(
69 &mut self,
70 custom_fees: impl IntoIterator<Item = AnyCustomFee>,
71 ) -> &mut Self {
72 self.data_mut().custom_fees = custom_fees.into_iter().collect();
73 self
74 }
75}
76
77impl TransactionData for TokenFeeScheduleUpdateTransactionData {}
78
79impl TransactionExecute for TokenFeeScheduleUpdateTransactionData {
80 fn execute(
81 &self,
82 channel: Channel,
83 request: services::Transaction,
84 ) -> BoxGrpcFuture<'_, services::TransactionResponse> {
85 Box::pin(async {
86 TokenServiceClient::new(channel).update_token_fee_schedule(request).await
87 })
88 }
89}
90
91impl ValidateChecksums for TokenFeeScheduleUpdateTransactionData {
92 fn validate_checksums(&self, ledger_id: &crate::ledger_id::RefLedgerId) -> Result<(), Error> {
93 self.token_id.validate_checksums(ledger_id)
95 }
96}
97
98impl ToTransactionDataProtobuf for TokenFeeScheduleUpdateTransactionData {
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::TokenFeeScheduleUpdate(self.to_protobuf())
106 }
107}
108
109impl ToSchedulableTransactionDataProtobuf for TokenFeeScheduleUpdateTransactionData {
110 fn to_schedulable_transaction_data_protobuf(
111 &self,
112 ) -> services::schedulable_transaction_body::Data {
113 services::schedulable_transaction_body::Data::TokenFeeScheduleUpdate(self.to_protobuf())
114 }
115}
116
117impl From<TokenFeeScheduleUpdateTransactionData> for AnyTransactionData {
118 fn from(transaction: TokenFeeScheduleUpdateTransactionData) -> Self {
119 Self::TokenFeeScheduleUpdate(transaction)
120 }
121}
122
123impl FromProtobuf<services::TokenFeeScheduleUpdateTransactionBody>
124 for TokenFeeScheduleUpdateTransactionData
125{
126 fn from_protobuf(pb: services::TokenFeeScheduleUpdateTransactionBody) -> crate::Result<Self> {
127 Ok(Self {
128 token_id: Option::from_protobuf(pb.token_id)?,
129 custom_fees: Vec::from_protobuf(pb.custom_fees)?,
130 })
131 }
132}
133
134impl ToProtobuf for TokenFeeScheduleUpdateTransactionData {
135 type Protobuf = services::TokenFeeScheduleUpdateTransactionBody;
136
137 fn to_protobuf(&self) -> Self::Protobuf {
138 services::TokenFeeScheduleUpdateTransactionBody {
139 token_id: self.token_id.to_protobuf(),
140 custom_fees: self.custom_fees.to_protobuf(),
141 }
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use expect_test::expect;
148 use hedera_proto::services;
149
150 use crate::protobuf::{
151 FromProtobuf,
152 ToProtobuf,
153 };
154 use crate::token::TokenFeeScheduleUpdateTransactionData;
155 use crate::transaction::test_helpers::{
156 check_body,
157 transaction_body,
158 };
159 use crate::{
160 AnyCustomFee,
161 AnyTransaction,
162 FixedFee,
163 FractionalFee,
164 TokenFeeScheduleUpdateTransaction,
165 TokenId,
166 };
167
168 const TOKEN_ID: TokenId = TokenId::new(0, 0, 8798);
169
170 fn custom_fees() -> [AnyCustomFee; 2] {
171 [
172 FixedFee {
173 fee: crate::FixedFeeData {
174 amount: 10,
175 denominating_token_id: Some(TokenId::new(0, 0, 483902)),
176 },
177 fee_collector_account_id: Some("4322".parse().unwrap()),
178 all_collectors_are_exempt: false,
179 }
180 .into(),
181 FractionalFee {
182 fee: crate::FractionalFeeData {
183 denominator: 7,
184 numerator: 3,
185 minimum_amount: 3,
186 maximum_amount: 100,
187 assessment_method: crate::FeeAssessmentMethod::Exclusive,
188 },
189 fee_collector_account_id: Some("389042".parse().unwrap()),
190 all_collectors_are_exempt: false,
191 }
192 .into(),
193 ]
194 }
195
196 fn make_transaction() -> TokenFeeScheduleUpdateTransaction {
197 let mut tx = TokenFeeScheduleUpdateTransaction::new_for_tests();
198
199 tx.token_id(TOKEN_ID).custom_fees(custom_fees()).freeze().unwrap();
200
201 tx
202 }
203
204 #[test]
205 fn serialize() {
206 let tx = make_transaction();
207
208 let tx = transaction_body(tx);
209
210 let tx = check_body(tx);
211
212 expect![[r#"
213 TokenFeeScheduleUpdate(
214 TokenFeeScheduleUpdateTransactionBody {
215 token_id: Some(
216 TokenId {
217 shard_num: 0,
218 realm_num: 0,
219 token_num: 8798,
220 },
221 ),
222 custom_fees: [
223 CustomFee {
224 fee_collector_account_id: Some(
225 AccountId {
226 shard_num: 0,
227 realm_num: 0,
228 account: Some(
229 AccountNum(
230 4322,
231 ),
232 ),
233 },
234 ),
235 all_collectors_are_exempt: false,
236 fee: Some(
237 FixedFee(
238 FixedFee {
239 amount: 10,
240 denominating_token_id: Some(
241 TokenId {
242 shard_num: 0,
243 realm_num: 0,
244 token_num: 483902,
245 },
246 ),
247 },
248 ),
249 ),
250 },
251 CustomFee {
252 fee_collector_account_id: Some(
253 AccountId {
254 shard_num: 0,
255 realm_num: 0,
256 account: Some(
257 AccountNum(
258 389042,
259 ),
260 ),
261 },
262 ),
263 all_collectors_are_exempt: false,
264 fee: Some(
265 FractionalFee(
266 FractionalFee {
267 fractional_amount: Some(
268 Fraction {
269 numerator: 3,
270 denominator: 7,
271 },
272 ),
273 minimum_amount: 3,
274 maximum_amount: 100,
275 net_of_transfers: true,
276 },
277 ),
278 ),
279 },
280 ],
281 },
282 )
283 "#]]
284 .assert_debug_eq(&tx)
285 }
286
287 #[test]
288 fn to_from_bytes() {
289 let tx = make_transaction();
290
291 let tx2 = AnyTransaction::from_bytes(&tx.to_bytes().unwrap()).unwrap();
292
293 let tx = transaction_body(tx);
294
295 let tx2 = transaction_body(tx2);
296
297 assert_eq!(tx, tx2);
298 }
299
300 #[test]
301 fn from_proto_body() {
302 let tx = services::TokenFeeScheduleUpdateTransactionBody {
303 token_id: Some(TOKEN_ID.to_protobuf()),
304 custom_fees: custom_fees().to_vec().to_protobuf(),
305 };
306
307 let data = TokenFeeScheduleUpdateTransactionData::from_protobuf(tx).unwrap();
308
309 assert_eq!(data.token_id, Some(TOKEN_ID));
310 assert_eq!(data.custom_fees, custom_fees());
311 }
312
313 #[test]
314 fn get_set_token_id() {
315 let mut tx = TokenFeeScheduleUpdateTransaction::new();
316 tx.token_id(TOKEN_ID);
317
318 assert_eq!(tx.get_token_id(), Some(TOKEN_ID));
319 }
320
321 #[test]
322 #[should_panic]
323 fn get_set_token_id_frozen_panic() {
324 make_transaction().token_id(TOKEN_ID);
325 }
326
327 #[test]
328 fn get_set_custom_fees() {
329 let mut tx = TokenFeeScheduleUpdateTransaction::new();
330 tx.custom_fees(custom_fees());
331
332 assert_eq!(tx.get_custom_fees(), custom_fees());
333 }
334
335 #[test]
336 #[should_panic]
337 fn get_set_custom_fees_frozen_panic() {
338 make_transaction().custom_fees(custom_fees());
339 }
340}