neo3/neo_builder/transaction/transaction.rs
1use std::hash::{Hash, Hasher};
2
3use futures_util::TryFutureExt;
4use getset::{CopyGetters, Getters, MutGetters, Setters};
5use primitive_types::U256;
6use serde::{Deserialize, Deserializer, Serialize};
7use serde_json::Value;
8use serde_with::__private__::DeError;
9use tracing::info;
10
11use crate::{
12 builder::{init_logger, Signer, TransactionAttribute, TransactionError, Witness},
13 codec::{Decoder, Encoder, NeoSerializable, VarSizeTrait},
14 config::NeoConstants,
15 crypto::HashableForVec,
16 neo_clients::{APITrait, HttpProvider, JsonRpcProvider, RpcClient},
17 neo_error::NeoError,
18 neo_protocol::{ApplicationLog, RawTransaction},
19 neo_types::NameOrAddress,
20 Bytes,
21};
22
23/// A Neo N3 blockchain transaction.
24///
25/// The `Transaction` struct represents a transaction on the Neo N3 blockchain. It contains
26/// all the necessary information for a transaction, including version, nonce, validity period,
27/// signers, fees, attributes, script, and witnesses.
28///
29/// # Fields
30///
31/// * `network` - An optional reference to an RPC client for network operations.
32/// * `version` - The transaction version.
33/// * `nonce` - A random number to prevent transaction duplication.
34/// * `valid_until_block` - The block height until which the transaction is valid.
35/// * `signers` - A list of transaction signers.
36/// * `size` - The size of the transaction in bytes.
37/// * `sys_fee` - The system fee for the transaction.
38/// * `net_fee` - The network fee for the transaction.
39/// * `attributes` - Transaction attributes.
40/// * `script` - The transaction script.
41/// * `witnesses` - Transaction witnesses (signatures).
42/// * `block_count_when_sent` - The block count when the transaction was sent.
43///
44/// # Examples
45///
46/// ```rust,no_run
47/// use neo3::neo_builder::{Transaction, TransactionBuilder};
48/// use neo3::neo_clients::{HttpProvider, RpcClient};
49///
50/// fn example() -> Result<(), Box<dyn std::error::Error>> {
51/// // Create a new transaction (placeholder)
52/// // let tx = Transaction::new();
53///
54/// // Transactions are typically created using the TransactionBuilder
55/// let provider = HttpProvider::new("https://testnet1.neo.org:443")?;
56/// let client = RpcClient::new(provider);
57/// // let mut tx_builder = TransactionBuilder::with_client(&client);
58/// Ok(())
59/// }
60/// ```
61#[derive(Serialize, Getters, Setters, MutGetters, CopyGetters, Debug, Clone)]
62pub struct Transaction<'a, P: JsonRpcProvider + 'static> {
63 #[serde(skip)]
64 #[getset(get = "pub", set = "pub")]
65 pub network: Option<&'a RpcClient<P>>,
66
67 #[serde(rename = "version")]
68 #[getset(get = "pub", set = "pub")]
69 pub version: u8,
70
71 #[serde(rename = "nonce")]
72 #[getset(get = "pub", set = "pub")]
73 pub nonce: u32,
74
75 #[serde(rename = "validuntilblock")]
76 #[getset(get = "pub", set = "pub")]
77 pub valid_until_block: u32,
78
79 #[serde(rename = "signers")]
80 #[getset(get = "pub", set = "pub")]
81 pub signers: Vec<Signer>,
82
83 #[serde(rename = "size")]
84 #[getset(get = "pub", set = "pub")]
85 pub size: i32,
86
87 #[serde(rename = "sysfee")]
88 #[getset(get = "pub", set = "pub")]
89 pub sys_fee: i64,
90
91 #[serde(rename = "netfee")]
92 #[getset(get = "pub", set = "pub")]
93 pub net_fee: i64,
94
95 #[serde(rename = "attributes")]
96 #[getset(get = "pub", set = "pub")]
97 pub attributes: Vec<TransactionAttribute>,
98
99 #[serde(rename = "script")]
100 #[getset(get = "pub", set = "pub")]
101 pub script: Bytes,
102
103 #[serde(rename = "witnesses")]
104 #[getset(get = "pub", set = "pub")]
105 pub witnesses: Vec<Witness>,
106
107 // #[serde(rename = "blocktime")]
108 // #[getset(get = "pub", set = "pub")]
109 // pub block_time: Option<i32>,
110 #[serde(skip)]
111 pub(crate) block_count_when_sent: Option<u32>,
112}
113
114impl<'a, P: JsonRpcProvider + 'static> Default for Transaction<'a, P> {
115 fn default() -> Self {
116 Transaction {
117 network: None,
118 version: Default::default(),
119 nonce: Default::default(),
120 valid_until_block: Default::default(),
121 signers: Default::default(),
122 size: Default::default(),
123 sys_fee: Default::default(),
124 net_fee: Default::default(),
125 attributes: Default::default(),
126 script: Default::default(),
127 witnesses: Default::default(),
128 // block_time: Default::default(),
129 block_count_when_sent: None,
130 }
131 }
132}
133
134impl<'de, 'a, P: JsonRpcProvider + 'static> Deserialize<'de> for Transaction<'a, P> {
135 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
136 where
137 D: Deserializer<'de>,
138 {
139 let value = Value::deserialize(deserializer)?;
140
141 // Example for version, apply similar logic for other fields
142 let version = value
143 .get("version")
144 .ok_or(DeError::missing_field("version"))?
145 .as_u64()
146 .ok_or(DeError::custom("invalid type for version"))? as u8;
147
148 // Production-ready field deserialization with proper error handling
149 let nonce = value
150 .get("nonce")
151 .and_then(|v| v.as_i64())
152 .ok_or(DeError::custom("Missing or invalid nonce field"))? as u32;
153
154 let valid_until_block = value
155 .get("validuntilblock")
156 .and_then(|v| v.as_i64())
157 .ok_or(DeError::custom("Missing or invalid validuntilblock field"))?
158 as u32;
159 // Continue for other fields...
160
161 // For Vec<T> fields like signers, attributes, witnesses, you might deserialize them like this:
162 // This assumes that Signer, TransactionAttribute, Witness can be deserialized directly from serde_json::Value
163 let signers: Vec<Signer> =
164 serde_json::from_value(value["signers"].clone()).map_err(DeError::custom)?;
165 let attributes: Vec<TransactionAttribute> =
166 serde_json::from_value(value["attributes"].clone()).map_err(DeError::custom)?;
167 let witnesses: Vec<Witness> =
168 serde_json::from_value(value["witnesses"].clone()).map_err(DeError::custom)?;
169
170 // For bytes, assuming it's a Vec<u8> and stored as a base64 string in JSON
171 let script: Bytes = base64::decode(value["script"].as_str().unwrap_or_default())
172 .map_err(DeError::custom)?;
173
174 // For optional fields
175 let block_time = value["blocktime"].as_i64().map(|v| v as i32);
176
177 // Complete field deserialization with comprehensive error handling
178 let size = value
179 .get("size")
180 .and_then(|v| v.as_i64())
181 .ok_or(DeError::custom("Missing or invalid size field"))? as i32;
182
183 let sys_fee = value
184 .get("sysfee")
185 .and_then(|v| v.as_i64())
186 .ok_or(DeError::custom("Missing or invalid sysfee field"))?;
187
188 let net_fee = value
189 .get("netfee")
190 .and_then(|v| v.as_i64())
191 .ok_or(DeError::custom("Missing or invalid netfee field"))?;
192
193 Ok(Transaction {
194 network: None,
195 version,
196 nonce,
197 valid_until_block,
198 signers,
199 size,
200 sys_fee,
201 net_fee,
202 attributes,
203 script,
204 witnesses,
205 block_count_when_sent: None,
206 })
207 }
208}
209
210// impl<P: JsonRpcClient + 'static> DeserializeOwned for Transaction<P> {}
211
212impl<'a, P: JsonRpcProvider + 'static> Hash for Transaction<'a, P> {
213 fn hash<H: Hasher>(&self, state: &mut H) {
214 self.to_array().hash(state);
215 }
216}
217
218impl<'a, T: JsonRpcProvider + 'static> Transaction<'a, T> {
219 const HEADER_SIZE: usize = 25;
220 pub fn new() -> Self {
221 Self::default()
222 }
223
224 /// Convenience function for sending a new payment transaction to the receiver.
225 pub fn pay<K: Into<NameOrAddress>, V: Into<U256>>(_to: K, _value: V) -> Self {
226 Transaction { ..Default::default() }
227 }
228
229 pub fn add_witness(&mut self, witness: Witness) {
230 self.witnesses.push(witness);
231 }
232
233 pub async fn get_hash_data(&self) -> Result<Bytes, TransactionError> {
234 if self.network.is_none() {
235 return Err(TransactionError::TransactionConfiguration(
236 "Transaction network magic is not set".to_string(),
237 ));
238 }
239 let mut encoder = Encoder::new();
240 self.serialize_without_witnesses(&mut encoder);
241 let mut data = encoder.to_bytes().hash256();
242 let network_value = self.network.as_ref().unwrap().network().await?;
243 data.splice(0..0, network_value.to_be_bytes());
244
245 Ok(data)
246 }
247
248 fn get_tx_id(&self) -> Result<primitive_types::H256, TransactionError> {
249 let mut encoder = Encoder::new();
250 self.serialize_without_witnesses(&mut encoder);
251 let data = encoder.to_bytes().hash256();
252 let reversed_data = data.iter().rev().cloned().collect::<Vec<u8>>();
253 Ok(primitive_types::H256::from_slice(&reversed_data))
254 }
255
256 fn serialize_without_witnesses(&self, writer: &mut Encoder) {
257 writer.write_u8(self.version);
258 writer.write_u32(self.nonce);
259 writer.write_i64(self.sys_fee);
260 writer.write_i64(self.net_fee);
261 writer.write_u32(self.valid_until_block);
262 writer.write_serializable_variable_list(&self.signers);
263 writer.write_serializable_variable_list(&self.attributes);
264 writer.write_var_bytes(&self.script);
265 }
266
267 /// Sends the transaction to the Neo N3 network.
268 ///
269 /// This method validates the transaction, converts it to a hexadecimal string,
270 /// and sends it to the network using the RPC client. It also records the current
271 /// block count for transaction tracking purposes.
272 ///
273 /// # Returns
274 ///
275 /// A `Result` containing the `RawTransaction` response if successful,
276 /// or a `TransactionError` if an error occurs.
277 ///
278 /// # Errors
279 ///
280 /// Returns an error if:
281 /// * The number of signers does not match the number of witnesses
282 /// * The transaction exceeds the maximum transaction size
283 /// * The network client encounters an error when sending the transaction
284 ///
285 /// # Examples
286 ///
287 /// ```rust,no_run
288 /// use neo3::neo_builder::{ScriptBuilder, AccountSigner, TransactionBuilder, CallFlags};
289 /// use neo3::neo_clients::{HttpProvider, RpcClient, APITrait};
290 /// use neo3::neo_protocol::{Account, AccountTrait};
291 /// use neo3::neo_types::{Address, ContractParameter, ScriptHash, AddressExtension};
292 /// use std::str::FromStr;
293 ///
294 /// #[tokio::main]
295 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
296 /// // Connect to Neo N3 TestNet
297 /// let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
298 /// let client = RpcClient::new(provider);
299 ///
300 /// // Load your account from WIF or keystore
301 /// let private_key = "your_private_key_here";
302 /// let account = Account::from_wif(private_key)?;
303 ///
304 /// // Create a token transfer transaction
305 /// let from_address = account.get_address();
306 /// let to_address = "NdUL5oDPD159KeFpD5A9zw5xNF1xLX6nLT";
307 /// let amount = 1_000_000; // 1 GAS (with 8 decimals)
308 ///
309 /// let mut script_builder = ScriptBuilder::new();
310 /// let script = script_builder.contract_call(
311 /// &ScriptHash::from_str("d2a4cff31913016155e38e474a2c06d08be276cf")?, // GAS token
312 /// "transfer",
313 /// &[
314 /// ContractParameter::from(account.get_script_hash()),
315 /// ContractParameter::from(Address::from_str(to_address)?.address_to_script_hash()?),
316 /// ContractParameter::integer(amount),
317 /// ContractParameter::any()
318 /// ],
319 /// Some(CallFlags::All)
320 /// )?;
321 ///
322 /// // Build the transaction with proper fee calculation
323 /// let mut tx_builder = TransactionBuilder::with_client(&client);
324 /// tx_builder.extend_script(script.to_bytes());
325 /// let account_signer = AccountSigner::called_by_entry(&account)?;
326 /// tx_builder.set_signers(vec![account_signer.into()])?;
327 /// tx_builder.valid_until_block(client.get_block_count().await? + 2400)?; // ~1 hour validity
328 ///
329 /// // Sign the transaction and get signed transaction
330 /// let signed_transaction = tx_builder.sign().await?;
331 ///
332 /// // Send the transaction to the network
333 /// let mut transaction = signed_transaction;
334 /// let response = transaction.send_tx().await?;
335 /// println!("✅ Transaction sent successfully!");
336 /// println!("Transaction ID: {}", response.hash);
337 /// println!("Transferred {} GAS to {}", amount as f64 / 100_000_000.0, to_address);
338 ///
339 /// Ok(())
340 /// }
341 /// ```
342 pub async fn send_tx(&mut self) -> Result<RawTransaction, TransactionError>
343// where
344 // P: APITrait,
345 {
346 if self.signers.len() != self.witnesses.len() {
347 return Err(TransactionError::TransactionConfiguration(
348 "The transaction does not have the same number of signers and witnesses."
349 .to_string(),
350 ));
351 }
352 if self.size() > &(NeoConstants::MAX_TRANSACTION_SIZE as i32) {
353 return Err(TransactionError::TransactionConfiguration(
354 "The transaction exceeds the maximum transaction size.".to_string(),
355 ));
356 }
357 let hex = hex::encode(self.to_array());
358 // self.throw()?;
359 self.block_count_when_sent = Some(self.network().unwrap().get_block_count().await?);
360 self.network()
361 .unwrap()
362 .send_raw_transaction(hex)
363 .await
364 .map_err(|e| TransactionError::IllegalState(e.to_string()))
365 }
366
367 /// Tracks a transaction until it appears in a block.
368 ///
369 /// This method waits for the transaction to be included in a block by monitoring new blocks
370 /// as they are added to the blockchain. It returns when the transaction is found in a block.
371 ///
372 /// # Arguments
373 ///
374 /// * `max_blocks` - The maximum number of blocks to wait for the transaction to appear
375 ///
376 /// # Returns
377 ///
378 /// * `Ok(())` - If the transaction is found in a block
379 /// * `Err(TransactionError)` - If the transaction is not found after waiting for `max_blocks` blocks
380 ///
381 /// # Errors
382 ///
383 /// Returns an error if:
384 /// * The transaction has not been sent yet
385 /// * The maximum number of blocks is reached without finding the transaction
386 /// * There is an error communicating with the blockchain
387 ///
388 /// # Examples
389 ///
390 /// ```rust,no_run
391 /// use neo3::neo_builder::{ScriptBuilder, AccountSigner, TransactionBuilder, CallFlags};
392 /// use neo3::neo_clients::{HttpProvider, RpcClient, APITrait};
393 /// use neo3::neo_protocol::{Account, AccountTrait};
394 /// use neo3::neo_types::{ContractParameter, ScriptHash};
395 /// use std::str::FromStr;
396 ///
397 /// #[tokio::main]
398 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
399 /// // Initialize provider and client
400 /// let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
401 /// let client = RpcClient::new(provider);
402 ///
403 /// // Load account and create smart contract deployment transaction
404 /// let account = Account::from_wif("your_private_key_here")?;
405 ///
406 /// // Load contract NEF and manifest
407 /// let nef_bytes = std::fs::read("path/to/contract.nef")?;
408 /// let manifest_bytes = std::fs::read("path/to/contract.manifest.json")?;
409 ///
410 /// let mut script_builder = ScriptBuilder::new();
411 /// let deploy_script = script_builder.contract_call(
412 /// &ScriptHash::from_str("0xfffdc93764dbaddd97c48f252a53ea4643faa3fd")?, // ContractManagement
413 /// "deploy",
414 /// &[
415 /// ContractParameter::byte_array(nef_bytes),
416 /// ContractParameter::byte_array(manifest_bytes),
417 /// ContractParameter::any() // Optional data parameter
418 /// ],
419 /// Some(CallFlags::All)
420 /// )?;
421 ///
422 /// // Build transaction with appropriate settings for contract deployment
423 /// let mut tx_builder = TransactionBuilder::with_client(&client);
424 /// tx_builder.extend_script(deploy_script.to_bytes());
425 /// let account_signer = AccountSigner::called_by_entry(&account)?;
426 /// tx_builder.set_signers(vec![account_signer.into()])?;
427 /// tx_builder.valid_until_block(client.get_block_count().await? + 2400)?;
428 ///
429 /// // Sign the transaction and get signed transaction
430 /// let signed_transaction = tx_builder.sign().await?;
431 ///
432 /// // Send the transaction
433 /// let mut transaction = signed_transaction;
434 /// let response = transaction.send_tx().await?;
435 /// println!("✅ Contract deployment transaction sent!");
436 /// println!("Transaction ID: {}", response.hash);
437 ///
438 /// // Track the transaction until confirmation
439 /// println!("⏳ Waiting for transaction confirmation...");
440 /// transaction.track_tx(15).await?; // Wait up to 15 blocks (~15 seconds)
441 /// println!("🎉 Contract deployment confirmed!");
442 ///
443 /// Ok(())
444 /// }
445 /// ```
446 pub async fn track_tx(&self, max_blocks: u32) -> Result<(), TransactionError> {
447 let block_count_when_sent =
448 self.block_count_when_sent.ok_or(TransactionError::IllegalState(
449 "Cannot track transaction before it has been sent.".to_string(),
450 ))?;
451
452 let tx_id = self.get_tx_id()?;
453 let mut current_block = block_count_when_sent;
454 let max_block = block_count_when_sent + max_blocks;
455
456 while current_block <= max_block {
457 // Get the current block count
458 let latest_block = self.network().unwrap().get_block_count().await?;
459
460 // If there are new blocks, check them for our transaction
461 if latest_block > current_block {
462 for block_index in current_block..latest_block {
463 // Get the block hash for this index
464 let block_hash = self.network().unwrap().get_block_hash(block_index).await?;
465
466 // Get the block with full transaction details
467 let block = self.network().unwrap().get_block(block_hash, true).await?;
468
469 // Check if our transaction is in this block
470 if let Some(transactions) = &block.transactions {
471 for tx in transactions.iter() {
472 if tx.hash == tx_id {
473 return Ok(());
474 }
475 }
476 }
477
478 current_block = block_index + 1;
479 }
480 }
481
482 // Wait a bit before checking again
483 tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
484 }
485
486 Err(TransactionError::IllegalState(format!(
487 "Transaction {} not found after waiting for {} blocks",
488 tx_id, max_blocks
489 )))
490 }
491
492 /// Retrieves the application log for this transaction.
493 ///
494 /// Application logs contain detailed information about the execution of a transaction,
495 /// including notifications, stack items, and any exceptions that occurred during execution.
496 ///
497 /// # Arguments
498 ///
499 /// * `provider` - A provider implementing the `APITrait` to make the RPC call.
500 ///
501 /// # Returns
502 ///
503 /// A `Result` containing the `ApplicationLog` if successful,
504 /// or a `TransactionError` if an error occurs.
505 ///
506 /// # Errors
507 ///
508 /// Returns an error if:
509 /// * The transaction has not been sent yet
510 /// * The transaction ID cannot be calculated
511 /// * The provider encounters an error when retrieving the application log
512 ///
513 /// # Examples
514 ///
515 /// ```rust,no_run
516 /// use neo3::neo_builder::{ScriptBuilder, AccountSigner, TransactionBuilder, CallFlags};
517 /// use neo3::neo_clients::{HttpProvider, RpcClient, APITrait};
518 /// use neo3::neo_protocol::{Account, AccountTrait};
519 /// use neo3::neo_types::{ContractParameter, ScriptHash};
520 /// use std::str::FromStr;
521 ///
522 /// #[tokio::main]
523 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
524 /// // Setup client connection
525 /// let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
526 /// let client = RpcClient::new(provider);
527 ///
528 /// // Load account for contract interaction
529 /// let account = Account::from_wif("your_private_key_here")?;
530 /// let contract_hash = ScriptHash::from_str("your_contract_hash_here")?;
531 ///
532 /// // Create a contract invocation transaction
533 /// let mut script_builder = ScriptBuilder::new();
534 /// let invoke_script = script_builder.contract_call(
535 /// &contract_hash,
536 /// "setValue", // Contract method name
537 /// &[
538 /// ContractParameter::string("myKey".to_string()),
539 /// ContractParameter::string("myValue".to_string()),
540 /// ContractParameter::integer(42)
541 /// ],
542 /// Some(CallFlags::All)
543 /// )?;
544 ///
545 /// // Build and configure the transaction
546 /// let mut tx_builder = TransactionBuilder::with_client(&client);
547 /// tx_builder.extend_script(invoke_script.to_bytes());
548 /// let account_signer = AccountSigner::called_by_entry(&account)?;
549 /// tx_builder.set_signers(vec![account_signer.into()])?;
550 /// tx_builder.valid_until_block(client.get_block_count().await? + 1200)?; // 20 minutes validity
551 ///
552 /// // Sign the transaction and get signed transaction
553 /// let signed_transaction = tx_builder.sign().await?;
554 ///
555 /// // Send the transaction
556 /// let mut transaction = signed_transaction;
557 /// let response = transaction.send_tx().await?;
558 /// println!("📤 Smart contract invocation sent!");
559 /// println!("Transaction ID: {}", response.hash);
560 ///
561 /// // Wait for confirmation and get detailed execution results
562 /// println!("⏳ Waiting for transaction confirmation...");
563 /// transaction.track_tx(12).await?;
564 /// println!("✅ Transaction confirmed!");
565 ///
566 /// // Analyze the execution results
567 /// let app_log = transaction.get_application_log(&client).await?;
568 /// println!("🔍 Transaction execution analysis:");
569 /// if let Ok(execution) = app_log.get_first_execution() {
570 /// println!(" Execution state: {:?}", execution.state);
571 /// println!(" GAS consumed: {}", execution.gas_consumed);
572 ///
573 /// // Process contract notifications and events
574 /// if !execution.notifications.is_empty() {
575 /// println!("📋 Contract notifications:");
576 /// for (i, notification) in execution.notifications.iter().enumerate() {
577 /// println!(" {}. Event: {} from contract {}",
578 /// i + 1, notification.event_name, notification.contract);
579 /// println!(" State: {:?}", notification.state);
580 /// }
581 /// }
582 ///
583 /// // Check execution stack for return values
584 /// if !execution.stack.is_empty() {
585 /// println!("📊 Return values: {:?}", execution.stack);
586 /// }
587 ///
588 /// if execution.state.to_string() == "HALT" {
589 /// println!("🎉 Smart contract executed successfully!");
590 /// } else {
591 /// println!("❌ Smart contract execution failed");
592 /// if let Some(exception) = &execution.exception {
593 /// println!(" Exception: {}", exception);
594 /// }
595 /// }
596 /// }
597 ///
598 /// Ok(())
599 /// }
600 /// ```
601 pub async fn get_application_log<P>(
602 &self,
603 provider: &P,
604 ) -> Result<ApplicationLog, TransactionError>
605 where
606 P: APITrait,
607 {
608 init_logger();
609 if self.block_count_when_sent.is_none() {
610 return Err(TransactionError::IllegalState(
611 "Cannot get the application log before transaction has been sent.".to_string(),
612 ));
613 }
614
615 let hash = self.get_tx_id()?;
616 info!("hash: {:?}", hash);
617
618 // self.thro
619 provider
620 .get_application_log(hash)
621 .await
622 .map_err(|e| TransactionError::IllegalState(e.to_string()))
623 }
624}
625
626// This commented-out code has been replaced by the send_tx method above
627
628impl<'a, P: JsonRpcProvider + 'static> Eq for Transaction<'a, P> {}
629
630impl<'a, P: JsonRpcProvider + 'static> PartialEq for Transaction<'a, P> {
631 fn eq(&self, other: &Self) -> bool {
632 self.to_array() == other.to_array()
633 }
634}
635
636impl<'a, P: JsonRpcProvider + 'static> NeoSerializable for Transaction<'a, P> {
637 type Error = TransactionError;
638
639 fn size(&self) -> usize {
640 Transaction::<HttpProvider>::HEADER_SIZE
641 + self.signers.var_size()
642 + self.attributes.var_size()
643 + self.script.var_size()
644 + self.witnesses.var_size()
645 }
646
647 fn encode(&self, writer: &mut Encoder) {
648 self.serialize_without_witnesses(writer);
649 writer.write_serializable_variable_list(&self.witnesses);
650 }
651
652 fn decode(reader: &mut Decoder) -> Result<Self, Self::Error>
653 where
654 Self: Sized,
655 {
656 let version = reader.read_u8();
657 let nonce = reader.read_u32().map_err(|e| {
658 TransactionError::TransactionConfiguration(format!("Failed to read nonce: {}", e))
659 })?;
660 let system_fee = reader.read_i64().map_err(|e| {
661 TransactionError::TransactionConfiguration(format!("Failed to read system fee: {}", e))
662 })?;
663 let network_fee = reader.read_i64().map_err(|e| {
664 TransactionError::TransactionConfiguration(format!("Failed to read network fee: {}", e))
665 })?;
666 let valid_until_block = reader.read_u32().map_err(|e| {
667 TransactionError::TransactionConfiguration(format!(
668 "Failed to read valid until block: {}",
669 e
670 ))
671 })?;
672
673 // Read signers
674 let signers: Vec<Signer> = reader.read_serializable_list::<Signer>()?;
675
676 // Read attributes
677 let attributes: Vec<TransactionAttribute> =
678 reader.read_serializable_list::<TransactionAttribute>()?;
679
680 let script = reader.read_var_bytes()?.to_vec();
681
682 let mut witnesses = vec![];
683 if reader.available() > 0 {
684 witnesses.append(&mut reader.read_serializable_list::<Witness>()?);
685 }
686
687 Ok(Self {
688 network: None,
689 version,
690 nonce,
691 valid_until_block,
692 size: 0,
693 sys_fee: system_fee,
694 net_fee: network_fee,
695 signers,
696 attributes,
697 script,
698 witnesses,
699 // block_time: None,
700 block_count_when_sent: None,
701 })
702 }
703
704 fn to_array(&self) -> Vec<u8> {
705 let mut writer = Encoder::new();
706 self.encode(&mut writer);
707 writer.to_bytes()
708 }
709}