neo3/neo_builder/transaction/
transaction_builder.rs

1use ethereum_types::H256;
2/// A builder for constructing and configuring NEO blockchain transactions.
3///
4/// The `TransactionBuilder` provides a fluent interface for setting various transaction parameters
5/// such as version, nonce, validity period, signers, fees, and script. Once configured, it can
6/// generate an unsigned transaction.
7///
8/// # Fields
9///
10/// - `client`: An optional reference to an RPC client for network operations.
11/// - `version`: The transaction version.
12/// - `nonce`: A random number to prevent transaction duplication.
13/// - `valid_until_block`: The block height until which the transaction is valid.
14/// - `signers`: A list of transaction signers.
15/// - `additional_network_fee`: Additional network fee for the transaction.
16/// - `additional_system_fee`: Additional system fee for the transaction.
17/// - `attributes`: Transaction attributes.
18/// - `script`: The transaction script.
19/// - `fee_consumer`: An optional closure for fee-related operations.
20/// - `fee_error`: An optional error related to fee calculations.
21///
22/// # Example
23///
24/// ```rust,no_run
25/// use neo3::neo_builder::TransactionBuilder;
26/// use neo3::neo_clients::{HttpProvider, RpcClient};
27///
28/// #[tokio::main]
29/// async fn main() {
30///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
31///     let client = RpcClient::new(provider);
32///     let mut tx_builder = TransactionBuilder::with_client(&client);
33///     tx_builder.version(0);
34///     tx_builder.nonce(1).unwrap();
35///     tx_builder.valid_until_block(100).unwrap();
36///     tx_builder.extend_script(vec![0x01, 0x02, 0x03]);
37///
38///     let unsigned_tx = tx_builder.get_unsigned_tx().await.unwrap();
39/// }
40/// ```
41///
42/// # Note
43///
44/// This builder implements `Debug`, `Clone`, `Eq`, `PartialEq`, and `Hash` traits.
45/// It uses generics to allow for different types of JSON-RPC providers.
46use futures_util::TryFutureExt;
47use std::{
48	cell::RefCell,
49	collections::HashSet,
50	default,
51	fmt::Debug,
52	hash::{Hash, Hasher},
53	iter::Iterator,
54	str::FromStr,
55};
56
57use crate::builder::SignerTrait;
58use getset::{CopyGetters, Getters, MutGetters, Setters};
59use once_cell::sync::Lazy;
60use primitive_types::H160;
61use serde::{Deserialize, Serialize};
62use serde_json::Value;
63// Import from neo_types
64use crate::neo_types::{
65	Bytes, ContractParameter, InvocationResult, NameOrAddress, ScriptHash, ScriptHashExtension,
66};
67
68// Import transaction types from neo_builder
69use crate::neo_builder::{
70	transaction::{
71		Signer, SignerType, Transaction, TransactionAttribute, TransactionError,
72		VerificationScript, Witness, WitnessScope,
73	},
74	BuilderError,
75};
76
77// Import other modules
78use crate::{
79	neo_clients::{APITrait, JsonRpcProvider, RpcClient},
80	neo_codec::{Decoder, Encoder, NeoSerializable, VarSizeTrait},
81	neo_config::{NeoConstants, NEOCONFIG},
82	neo_crypto::{utils::ToHexString, HashableForVec, Secp256r1PublicKey},
83	neo_protocol::{AccountTrait, NeoNetworkFee},
84};
85
86// Helper functions
87use crate::neo_clients::public_key_to_script_hash;
88
89// Import Account from neo_protocol
90use crate::neo_protocol::Account;
91
92// Special module for initialization - conditionally include
93#[cfg(feature = "init")]
94use crate::prelude::init_logger;
95// Define a local replacement for when init feature is not enabled
96#[cfg(not(feature = "init"))]
97fn init_logger() {
98	// No-op when feature is not enabled
99}
100
101#[derive(Getters, Setters, MutGetters, CopyGetters, Default)]
102pub struct TransactionBuilder<'a, P: JsonRpcProvider + 'static> {
103	pub(crate) client: Option<&'a RpcClient<P>>,
104	version: u8,
105	nonce: u32,
106	valid_until_block: Option<u32>,
107	// setter and getter
108	#[getset(get = "pub")]
109	pub(crate) signers: Vec<Signer>,
110	#[getset(get = "pub", set = "pub")]
111	additional_network_fee: u64,
112	#[getset(get = "pub", set = "pub")]
113	additional_system_fee: u64,
114	#[getset(get = "pub")]
115	attributes: Vec<TransactionAttribute>,
116	#[getset(get = "pub", set = "pub")]
117	script: Option<Bytes>,
118	fee_consumer: Option<Box<dyn Fn(i64, i64)>>,
119	fee_error: Option<TransactionError>,
120}
121
122impl<'a, P: JsonRpcProvider + 'static> Debug for TransactionBuilder<'a, P> {
123	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124		f.debug_struct("TransactionBuilder")
125			.field("version", &self.version)
126			.field("nonce", &self.nonce)
127			.field("valid_until_block", &self.valid_until_block)
128			.field("signers", &self.signers)
129			.field("additional_network_fee", &self.additional_network_fee)
130			.field("additional_system_fee", &self.additional_system_fee)
131			.field("attributes", &self.attributes)
132			.field("script", &self.script)
133			// .field("fee_consumer", &self.fee_consumer)
134			.field("fee_error", &self.fee_error)
135			.finish()
136	}
137}
138
139impl<'a, P: JsonRpcProvider + 'static> Clone for TransactionBuilder<'a, P> {
140	fn clone(&self) -> Self {
141		Self {
142			client: self.client,
143			version: self.version,
144			nonce: self.nonce,
145			valid_until_block: self.valid_until_block,
146			signers: self.signers.clone(),
147			additional_network_fee: self.additional_network_fee,
148			additional_system_fee: self.additional_system_fee,
149			attributes: self.attributes.clone(),
150			script: self.script.clone(),
151			// fee_consumer: self.fee_consumer.clone(),
152			fee_consumer: None,
153			fee_error: None,
154		}
155	}
156}
157
158impl<'a, P: JsonRpcProvider + 'static> Eq for TransactionBuilder<'a, P> {}
159
160impl<'a, P: JsonRpcProvider + 'static> PartialEq for TransactionBuilder<'a, P> {
161	fn eq(&self, other: &Self) -> bool {
162		self.version == other.version
163			&& self.nonce == other.nonce
164			&& self.valid_until_block == other.valid_until_block
165			&& self.signers == other.signers
166			&& self.additional_network_fee == other.additional_network_fee
167			&& self.additional_system_fee == other.additional_system_fee
168			&& self.attributes == other.attributes
169			&& self.script == other.script
170	}
171}
172
173impl<'a, P: JsonRpcProvider + 'static> Hash for TransactionBuilder<'a, P> {
174	fn hash<H: Hasher>(&self, state: &mut H) {
175		self.version.hash(state);
176		self.nonce.hash(state);
177		self.valid_until_block.hash(state);
178		self.signers.hash(state);
179		self.additional_network_fee.hash(state);
180		self.additional_system_fee.hash(state);
181		self.attributes.hash(state);
182		self.script.hash(state);
183	}
184}
185
186pub static GAS_TOKEN_HASH: Lazy<ScriptHash> = Lazy::new(|| {
187	ScriptHash::from_str("d2a4cff31913016155e38e474a2c06d08be276cf")
188		.expect("GAS token hash is a valid script hash")
189});
190
191impl<'a, P: JsonRpcProvider + 'static> TransactionBuilder<'a, P> {
192	// const GAS_TOKEN_HASH: ScriptHash = ScriptHash::from_str("d2a4cff31913016155e38e474a2c06d08be276cf").unwrap();
193	pub const BALANCE_OF_FUNCTION: &'static str = "balanceOf";
194	pub const DUMMY_PUB_KEY: &'static str =
195		"02ec143f00b88524caf36a0121c2de09eef0519ddbe1c710a00f0e2663201ee4c0";
196
197	/// Creates a new `TransactionBuilder` instance with default values.
198	///
199	/// # Returns
200	///
201	/// A new `TransactionBuilder` instance with default values.
202	///
203	/// # Examples
204	///
205	/// ```rust
206	/// use neo3::neo_builder::TransactionBuilder;
207	/// use neo3::neo_clients::HttpProvider;
208	///
209	/// let tx_builder: TransactionBuilder<'_, HttpProvider> = TransactionBuilder::default();
210	/// ```
211	pub fn new() -> Self {
212		Self {
213			client: None,
214			version: 0,
215			nonce: 0,
216			valid_until_block: None,
217			signers: Vec::new(),
218			additional_network_fee: 0,
219			additional_system_fee: 0,
220			attributes: Vec::new(),
221			script: None,
222			fee_consumer: None,
223			fee_error: None,
224		}
225	}
226
227	/// Creates a new `TransactionBuilder` instance with a client reference.
228	///
229	/// # Arguments
230	///
231	/// * `client` - A reference to an RPC client for network operations.
232	///
233	/// # Returns
234	///
235	/// A new `TransactionBuilder` instance with the specified client.
236	///
237	/// # Examples
238	///
239	/// ```rust,no_run
240	/// use neo3::neo_builder::TransactionBuilder;
241	/// use neo3::neo_clients::{HttpProvider, RpcClient};
242	///
243	/// let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
244	/// let client = RpcClient::new(provider);
245	/// let tx_builder = TransactionBuilder::with_client(&client);
246	/// ```
247	pub fn with_client(client: &'a RpcClient<P>) -> Self {
248		Self {
249			client: Some(client),
250			version: 0,
251			nonce: 0,
252			valid_until_block: None,
253			signers: Vec::new(),
254			additional_network_fee: 0,
255			additional_system_fee: 0,
256			attributes: Vec::new(),
257			script: None,
258			fee_consumer: None,
259			fee_error: None,
260		}
261	}
262
263	/// Sets the version of the transaction.
264	///
265	/// # Arguments
266	///
267	/// * `version` - The transaction version (typically 0 for Neo N3).
268	///
269	/// # Returns
270	///
271	/// A mutable reference to the `TransactionBuilder` for method chaining.
272	///
273	/// # Examples
274	///
275	/// ```rust
276	/// use neo3::neo_builder::TransactionBuilder;
277	/// use neo3::neo_clients::{HttpProvider, RpcClient};
278	///
279	/// let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
280	/// let client = RpcClient::new(provider);
281	/// let mut tx_builder = TransactionBuilder::with_client(&client);
282	/// tx_builder.version(0);
283	/// ```
284	pub fn version(&mut self, version: u8) -> &mut Self {
285		self.version = version;
286		self
287	}
288
289	/// Sets the nonce of the transaction.
290	///
291	/// The nonce is a random number used to prevent transaction duplication.
292	///
293	/// # Arguments
294	///
295	/// * `nonce` - A random number to prevent transaction duplication.
296	///
297	/// # Returns
298	///
299	/// A `Result` containing a mutable reference to the `TransactionBuilder` for method chaining,
300	/// or a `TransactionError` if the nonce is invalid.
301	///
302	/// # Examples
303	///
304	/// ```rust
305	/// use neo3::neo_builder::TransactionBuilder;
306	/// use neo3::neo_clients::{HttpProvider, RpcClient};
307	///
308	/// let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
309	/// let client = RpcClient::new(provider);
310	/// let mut tx_builder = TransactionBuilder::with_client(&client);
311	/// tx_builder.nonce(1234567890).unwrap();
312	/// ```
313	pub fn nonce(&mut self, nonce: u32) -> Result<&mut Self, TransactionError> {
314		// u32 can't exceed u32::MAX, so this check is redundant
315		// Keeping the function signature for API compatibility
316		self.nonce = nonce;
317		Ok(self)
318	}
319
320	/// Sets the block height until which the transaction is valid.
321	///
322	/// In Neo N3, transactions have a limited validity period defined by block height.
323	/// This helps prevent transaction replay attacks and cleans up the memory pool.
324	///
325	/// # Arguments
326	///
327	/// * `block` - The block height until which the transaction is valid.
328	///
329	/// # Returns
330	///
331	/// A `Result` containing a mutable reference to the `TransactionBuilder` for method chaining,
332	/// or a `TransactionError` if the block height is invalid.
333	///
334	/// # Examples
335	///
336	/// ```rust,no_run
337	/// use neo3::neo_builder::TransactionBuilder;
338	/// use neo3::neo_clients::{HttpProvider, RpcClient};
339	/// use neo3::neo_clients::APITrait;
340	///
341	/// #[tokio::main]
342	/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
343	///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
344	///     let client = RpcClient::new(provider);
345	///     
346	///     let current_height = client.get_block_count().await?;
347	///     
348	///     let mut tx_builder = TransactionBuilder::with_client(&client);
349	///     tx_builder.valid_until_block(current_height + 5760)?; // Valid for ~1 day
350	///     
351	///     Ok(())
352	/// }
353	/// ```
354	pub fn valid_until_block(&mut self, block: u32) -> Result<&mut Self, TransactionError> {
355		if block == 0 {
356			return Err(TransactionError::InvalidBlock);
357		}
358
359		self.valid_until_block = Some(block);
360		Ok(self)
361	}
362
363	// Set script
364	// pub fn set_script(&mut self, script: Vec<u8>) -> &mut Self {
365	// 	self.script = Some(script);
366	// 	self
367	// }
368
369	pub fn first_signer(&mut self, sender: &Account) -> Result<&mut Self, TransactionError> {
370		self.first_signer_by_hash(&sender.get_script_hash())
371	}
372
373	pub fn first_signer_by_hash(&mut self, sender: &H160) -> Result<&mut Self, TransactionError> {
374		if self.signers.iter().any(|s| s.get_scopes().contains(&WitnessScope::None)) {
375			return Err(TransactionError::ScriptFormat("This transaction contains a signer with fee-only witness scope that will cover the fees. Hence, the order of the signers does not affect the payment of the fees.".to_string()));
376		}
377		if let Some(pos) = self.signers.iter().position(|s| s.get_signer_hash() == sender) {
378			let s = self.signers.remove(pos);
379			self.signers.insert(0, s);
380			Ok(self)
381		} else {
382			Err(TransactionError::ScriptFormat(format!("Could not find a signer with script hash {}. Make sure to add the signer before calling this method.", sender.to_string()).into()))
383		}
384	}
385
386	pub fn extend_script(&mut self, script: Vec<u8>) -> &mut Self {
387		if let Some(ref mut existing_script) = self.script {
388			existing_script.extend(script);
389		} else {
390			self.script = Some(script);
391		}
392		self
393	}
394
395	pub async fn call_invoke_script(&self) -> Result<InvocationResult, TransactionError> {
396		if self.script.is_none() || self.script.as_ref().unwrap().is_empty() {
397			return Err((TransactionError::NoScript));
398		}
399		let result = self
400			.client
401			.unwrap()
402			.rpc_client()
403			.invoke_script(self.script.clone().unwrap().to_hex_string(), self.signers.clone())
404			.await
405			.map_err(|e| TransactionError::ProviderError(e))?;
406		Ok((result))
407	}
408
409	/// Builds a transaction from the current builder configuration
410	///
411	/// # Returns
412	///
413	/// A `Result` containing the built transaction or a `TransactionError`
414	///
415	/// # Examples
416	///
417	/// ```rust,no_run
418	/// use neo3::neo_builder::TransactionBuilder;
419	/// use neo3::neo_clients::{HttpProvider, RpcClient};
420	///
421	/// #[tokio::main]
422	/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
423	///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
424	///     let client = RpcClient::new(provider);
425	///     
426	///     let mut tx_builder = TransactionBuilder::with_client(&client);
427	///     tx_builder.version(0)
428	///               .nonce(1234567890)?
429	///               .valid_until_block(100)?;
430	///     
431	///     let tx = tx_builder.build().await?;
432	///     
433	///     Ok(())
434	/// }
435	/// ```
436	pub async fn build(&mut self) -> Result<Transaction<P>, TransactionError> {
437		self.get_unsigned_tx().await
438	}
439
440	// Get unsigned transaction
441	pub async fn get_unsigned_tx(&mut self) -> Result<Transaction<P>, TransactionError> {
442		// Validate configuration
443		if self.signers.is_empty() {
444			return Err(TransactionError::NoSigners);
445		}
446
447		if self.script.is_none() {
448			return Err(TransactionError::NoScript);
449		}
450		let len = self.signers.len();
451		self.signers.dedup();
452
453		// Validate no duplicate signers
454		if len != self.signers.len() {
455			return Err(TransactionError::DuplicateSigner);
456		}
457
458		// Check signer limits
459		if self.signers.len() > NeoConstants::MAX_SIGNER_SUBITEMS as usize {
460			return Err(TransactionError::TooManySigners);
461		}
462
463		// Validate script
464		if let Some(script) = &self.script {
465			if script.is_empty() {
466				return Err(TransactionError::EmptyScript);
467			}
468		} else {
469			return Err(TransactionError::NoScript);
470		}
471
472		if self.valid_until_block.is_none() {
473			self.valid_until_block = Some(
474				self.fetch_current_block_count().await?
475					+ self.client.unwrap().max_valid_until_block_increment()
476					- 1,
477			)
478		}
479
480		// Check committe member
481		if self.is_high_priority() && !self.is_allowed_for_high_priority().await {
482			return Err(TransactionError::IllegalState("This transaction does not have a committee member as signer. Only committee members can send transactions with high priority.".to_string()));
483		}
484
485		// if self.fee_consumer.is_some() {
486
487		// }
488
489		// Get fees
490		// let script = self.script.as_ref().unwrap();
491		// let response = self
492		// 	.client
493		// 	.unwrap()
494		// 	.invoke_script(script.to_hex(), vec![self.signers[0].clone()])
495		// 	.await
496		// 	.map_err(|e| TransactionError::ProviderError(e))?;
497
498		let system_fee = self.get_system_fee().await? + self.additional_system_fee as i64;
499
500		let network_fee = self.get_network_fee().await? + self.additional_network_fee as i64;
501
502		// Check sender balance if needed
503		let mut tx = Transaction {
504			network: Some(self.client.unwrap()),
505			version: self.version,
506			nonce: self.nonce,
507			valid_until_block: self.valid_until_block.unwrap_or(100),
508			size: 0,
509			sys_fee: system_fee,
510			net_fee: network_fee,
511			signers: self.signers.clone(),
512			attributes: self.attributes.clone(),
513			script: self.script.clone().unwrap(), // We've already checked for None case above
514			witnesses: vec![],
515			// block_time: None,
516			block_count_when_sent: None,
517		};
518
519		// It's impossible to calculate network fee when the tx is unsigned, because there is no witness
520		// let network_fee = Box::pin(self.client.unwrap().calculate_network_fee(base64::encode(tx.to_array()))).await?;
521		if self.fee_error.is_some()
522			&& !self.can_send_cover_fees(system_fee as u64 + network_fee as u64).await?
523		{
524			if let Some(supplier) = &self.fee_error {
525				return Err(supplier.clone());
526			}
527		} else if let Some(fee_consumer) = &self.fee_consumer {
528			let sender_balance = self.get_sender_balance().await?.try_into().unwrap(); // self.get_sender_balance().await.unwrap();
529			if network_fee + system_fee > sender_balance {
530				fee_consumer(network_fee + system_fee, sender_balance);
531			}
532		}
533		// tx.set_net_fee(network_fee);
534
535		Ok(tx)
536	}
537
538	async fn get_system_fee(&self) -> Result<i64, TransactionError> {
539		let script = self.script.as_ref().ok_or_else(|| TransactionError::NoScript)?;
540
541		let client = self
542			.client
543			.ok_or_else(|| TransactionError::IllegalState("Client is not set".to_string()))?;
544
545		let response = client
546			.invoke_script(script.to_hex_string(), vec![self.signers[0].clone()])
547			.await
548			.map_err(|e| TransactionError::ProviderError(e))?;
549
550		// Check if the VM execution resulted in a fault
551		if response.has_state_fault() {
552			// Get the current configuration for allowing transmission on fault
553			let allows_fault = NEOCONFIG
554				.lock()
555				.map_err(|_| {
556					TransactionError::IllegalState("Failed to lock NEOCONFIG".to_string())
557				})?
558				.allows_transmission_on_fault;
559
560			// If transmission on fault is not allowed, return an error
561			if !allows_fault {
562				return Err(TransactionError::TransactionConfiguration(format!(
563					"The vm exited due to the following exception: {}",
564					response.exception.unwrap_or_else(|| "Unknown exception".to_string())
565				)));
566			}
567			// Otherwise, we continue with the transaction despite the fault
568		}
569
570		Ok(i64::from_str(&response.gas_consumed).map_err(|_| {
571			TransactionError::IllegalState("Failed to parse gas consumed".to_string())
572		})?)
573	}
574
575	async fn get_network_fee(&mut self) -> Result<i64, TransactionError> {
576		// Check sender balance if needed
577		let client = self
578			.client
579			.ok_or_else(|| TransactionError::IllegalState("Client is not set".to_string()))?;
580
581		let script = self.script.clone().unwrap_or_default(); // Use default if None
582
583		let valid_until_block = self.valid_until_block.unwrap_or(100);
584
585		let mut tx = Transaction {
586			network: Some(client),
587			version: self.version,
588			nonce: self.nonce,
589			valid_until_block,
590			size: 0,
591			sys_fee: 0,
592			net_fee: 0,
593			signers: self.signers.clone(),
594			attributes: self.attributes.clone(),
595			script,
596			witnesses: vec![],
597			block_count_when_sent: None,
598		};
599		let mut has_atleast_one_signing_account = false;
600
601		for signer in self.signers.iter() {
602			match signer {
603				Signer::ContractSigner(contract_signer) => {
604					// Create contract witness and add it to the transaction
605					let witness =
606						Witness::create_contract_witness(contract_signer.verify_params().to_vec())
607							.map_err(|e| {
608								TransactionError::IllegalState(format!(
609									"Failed to create contract witness: {}",
610									e
611								))
612							})?;
613					tx.add_witness(witness);
614				},
615				Signer::AccountSigner(account_signer) => {
616					// Get the account from AccountSigner
617					let account = account_signer.account();
618					let verification_script;
619
620					// Check if the account is multi-signature or single-signature
621					if account.is_multi_sig() {
622						// Create a fake multi-signature verification script
623						verification_script = self
624							.create_fake_multi_sig_verification_script(account)
625							.map_err(|e| {
626								TransactionError::IllegalState(format!(
627									"Failed to create multi-sig verification script: {}",
628									e
629								))
630							})?;
631					} else {
632						// Create a fake single-signature verification script
633						verification_script =
634							self.create_fake_single_sig_verification_script().map_err(|e| {
635								TransactionError::IllegalState(format!(
636									"Failed to create single-sig verification script: {}",
637									e
638								))
639							})?;
640					}
641
642					// Add a witness with an empty signature and the verification script
643					tx.add_witness(Witness::from_scripts(
644						vec![],
645						verification_script.script().to_vec(),
646					));
647					has_atleast_one_signing_account = true;
648				},
649				// If there's a case for TransactionSigner, it can be handled here if necessary.
650				_ => {
651					// Handle any other cases, if necessary (like TransactionSigner)
652				},
653			}
654		}
655		if (!has_atleast_one_signing_account) {
656			return Err(TransactionError::TransactionConfiguration("A transaction requires at least one signing account (i.e. an AccountSigner). None was provided.".to_string()))
657		}
658
659		let fee = client.calculate_network_fee(tx.to_array().to_hex_string()).await?;
660		Ok(fee.network_fee)
661	}
662
663	async fn fetch_current_block_count(&mut self) -> Result<u32, TransactionError> {
664		let client = self
665			.client
666			.ok_or_else(|| TransactionError::IllegalState("Client is not set".to_string()))?;
667		let count = client.get_block_count().await?;
668		Ok(count)
669	}
670
671	async fn get_sender_balance(&self) -> Result<u64, TransactionError> {
672		// Call network
673		let sender = &self.signers[0];
674
675		if Self::is_account_signer(sender) {
676			let client = self
677				.client
678				.ok_or_else(|| TransactionError::IllegalState("Client is not set".to_string()))?;
679
680			let balance = client
681				.invoke_function(
682					&GAS_TOKEN_HASH,
683					Self::BALANCE_OF_FUNCTION.to_string(),
684					vec![ContractParameter::from(sender.get_signer_hash())],
685					None,
686				)
687				.await
688				.map_err(|e| TransactionError::ProviderError(e))?
689				.stack[0]
690				.clone();
691
692			return Ok(balance.as_int().ok_or_else(|| {
693				TransactionError::IllegalState("Failed to parse balance as integer".to_string())
694			})? as u64);
695		}
696		Err(TransactionError::InvalidSender)
697	}
698
699	fn create_fake_single_sig_verification_script(
700		&self,
701	) -> Result<VerificationScript, TransactionError> {
702		// Vector to store dummy public keys
703		let dummy_public_key =
704			Secp256r1PublicKey::from_encoded(Self::DUMMY_PUB_KEY).ok_or_else(|| {
705				TransactionError::IllegalState("Failed to create dummy public key".to_string())
706			})?;
707		// Create and return the VerificationScript with the pub_keys and signing threshold
708		Ok(VerificationScript::from_public_key(&dummy_public_key))
709	}
710
711	fn create_fake_multi_sig_verification_script(
712		&self,
713		account: &Account,
714	) -> Result<VerificationScript, TransactionError> {
715		// Vector to store dummy public keys
716		let mut pub_keys: Vec<Secp256r1PublicKey> = Vec::new();
717
718		// Get the number of participants
719		let nr_of_participants = account.get_nr_of_participants().map_err(|e| {
720			TransactionError::IllegalState(format!("Failed to get number of participants: {}", e))
721		})?;
722
723		// Loop to add dummy public keys based on the number of participants
724		for _ in 0..nr_of_participants {
725			// Create a dummy public key
726			let dummy_public_key = Secp256r1PublicKey::from_encoded(Self::DUMMY_PUB_KEY)
727				.ok_or_else(|| {
728					TransactionError::IllegalState("Failed to create dummy public key".to_string())
729				})?;
730			pub_keys.push(dummy_public_key);
731		}
732
733		// Get the signing threshold
734		let threshold_value = account.get_signing_threshold().map_err(|e| {
735			TransactionError::IllegalState(format!("Failed to get signing threshold: {}", e))
736		})?;
737		let signing_threshold = u8::try_from(threshold_value).map_err(|_| {
738			TransactionError::IllegalState(
739				"Signing threshold value out of range for u8".to_string(),
740			)
741		})?;
742
743		// Create and return the VerificationScript with the pub_keys and signing threshold
744		// This method returns a VerificationScript directly, not a Result
745		let script = VerificationScript::from_multi_sig(&mut pub_keys[..], signing_threshold);
746
747		Ok(script)
748	}
749
750	fn is_account_signer(signer: &Signer) -> bool {
751		if signer.get_type() == SignerType::AccountSigner {
752			return true;
753		}
754		return false;
755	}
756
757	/// Signs the transaction with the provided signers.
758	///
759	/// This method creates an unsigned transaction, signs it with the appropriate signers,
760	/// and returns the signed transaction. For account signers, it uses the account's private key
761	/// to create a signature. For contract signers, it creates a contract witness.
762	///
763	/// # Returns
764	///
765	/// A `Result` containing the signed `Transaction` if successful,
766	/// or a `BuilderError` if an error occurs during signing.
767	///
768	/// # Errors
769	///
770	/// Returns an error if:
771	/// - The transaction cannot be built (see `get_unsigned_tx`)
772	/// - A multi-signature account is used (these require manual signing)
773	/// - An account does not have a private key
774	/// - Witness creation fails
775	///
776	/// # Examples
777	///
778	/// ```ignore
779	/// use neo3::neo_builder::{TransactionBuilder, ScriptBuilder, AccountSigner};
780	/// use neo3::neo_clients::{HttpProvider, RpcClient};
781	/// use neo3::neo_protocol::{Account, AccountTrait};
782	/// use neo3::neo_types::ContractParameter;
783	/// use std::str::FromStr;
784	/// use neo3::neo_clients::APITrait;
785	///
786	/// #[tokio::main]
787	/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
788	///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
789	///     let client = RpcClient::new(provider);
790	///     
791	///     // Create an account for signing
792	///     let account = Account::from_wif("YOUR_WIF_HERE")?;
793	///     
794	///     // Create a proper contract invocation script
795	///     let contract_hash = neo3::neo_types::ScriptHash::from_str("0xd2a4cff31913016155e38e474a2c06d08be276cf")?; // GAS
796	///     let from_address = account.get_script_hash();
797	///     let to_address = neo3::neo_types::ScriptHash::from_str("0x0000000000000000000000000000000000000000")?;
798	///     let amount = 1_000_000i64;
799	///     let mut script_builder = ScriptBuilder::new();
800	///     script_builder.contract_call(
801	///         &contract_hash,
802	///         "transfer",
803	///         &[
804	///             ContractParameter::from(from_address),
805	///             ContractParameter::from(to_address),
806	///             ContractParameter::integer(amount),
807	///             ContractParameter::any()
808	///         ],
809	///         None
810	///     )?;
811	///     let script = script_builder.to_bytes();
812	///     
813	///     // Create and configure the transaction
814	///     let mut tx_builder = TransactionBuilder::with_client(&client);
815	///     tx_builder.extend_script(script);
816	///     let account_signer = AccountSigner::called_by_entry(&account)?;
817	///     tx_builder.set_signers(vec![account_signer.into()])?;
818	///     tx_builder.valid_until_block(client.get_block_count().await? + 5760)?; // Valid for ~1 day
819	///
820	///     // Sign the transaction
821	///     let signed_tx = tx_builder.sign().await?;
822	///     
823	///     // Send the transaction to the network
824	///     let tx_response = signed_tx.send_tx().await?;
825	///     println!("Transaction sent: {:?}", tx_response);
826	///     
827	///     Ok(())
828	/// }
829	/// ```
830	pub async fn sign(&mut self) -> Result<Transaction<P>, BuilderError> {
831		init_logger();
832		let mut unsigned_tx = self.get_unsigned_tx().await?;
833		let tx_bytes = unsigned_tx.get_hash_data().await?;
834
835		let mut witnesses_to_add = Vec::new();
836		for signer in &mut unsigned_tx.signers {
837			if Self::is_account_signer(signer) {
838				let account_signer = signer.as_account_signer().ok_or_else(|| {
839					BuilderError::IllegalState("Failed to get account signer".to_string())
840				})?;
841				let acc = &account_signer.account;
842				if acc.is_multi_sig() {
843					return Err(BuilderError::IllegalState(
844						"Transactions with multi-sig signers cannot be signed automatically."
845							.to_string(),
846					));
847				}
848				let key_pair = acc.key_pair().as_ref().ok_or_else(|| {
849                    BuilderError::InvalidConfiguration(
850                        format!("Cannot create transaction signature because account {} does not hold a private key.", acc.get_address()),
851                    )
852                })?;
853				witnesses_to_add.push(Witness::create(tx_bytes.clone(), key_pair)?);
854			} else {
855				let contract_signer = signer.as_contract_signer().ok_or_else(|| {
856					BuilderError::IllegalState(
857						"Expected contract signer but found another type".to_string(),
858					)
859				})?;
860				witnesses_to_add.push(Witness::create_contract_witness(
861					contract_signer.verify_params().clone(),
862				)?);
863			}
864		}
865		for witness in witnesses_to_add {
866			unsigned_tx.add_witness(witness);
867		}
868
869		Ok(unsigned_tx)
870	}
871
872	fn signers_contain_multi_sig_with_committee_member(&self, committee: &HashSet<H160>) -> bool {
873		for signer in &self.signers {
874			if let Some(account_signer) = signer.as_account_signer() {
875				if account_signer.is_multi_sig() {
876					if let Some(script) = &account_signer.account().verification_script() {
877						// Get public keys, returning false if there's an error instead of unwrapping
878						if let Ok(public_keys) = script.get_public_keys() {
879							for pubkey in public_keys {
880								let hash = public_key_to_script_hash(&pubkey);
881								if committee.contains(&hash) {
882									return true;
883								}
884							}
885						}
886					}
887				}
888			}
889		}
890
891		false
892	}
893
894	/// Sets the signers for the transaction.
895	///
896	/// Signers are entities that authorize the transaction. They can be accounts, contracts,
897	/// or other entities that can provide a signature or verification method.
898	///
899	/// # Arguments
900	///
901	/// * `signers` - A vector of `Signer` objects representing the transaction signers.
902	///
903	/// # Returns
904	///
905	/// A `Result` containing a mutable reference to the `TransactionBuilder` for method chaining,
906	/// or a `TransactionError` if there are duplicate signers or if adding the signers would
907	/// exceed the maximum allowed number of attributes.
908	///
909	/// # Examples
910	///
911	/// ```rust
912	/// use neo3::neo_builder::{TransactionBuilder, Signer, AccountSigner};
913	/// use neo3::neo_protocol::{Account, AccountTrait};
914	///
915	/// let account = Account::create().unwrap();
916	/// let account_signer = AccountSigner::called_by_entry(&account).unwrap();
917	/// let signer: Signer = account_signer.into();
918	///
919	/// # use neo3::neo_clients::HttpProvider;
920	/// let mut tx_builder: TransactionBuilder<'_, HttpProvider> = TransactionBuilder::default();
921	/// tx_builder.set_signers(vec![signer]).unwrap();
922	/// ```
923	pub fn set_signers(&mut self, signers: Vec<Signer>) -> Result<&mut Self, TransactionError> {
924		if self.contains_duplicate_signers(&signers) {
925			return Err(TransactionError::TransactionConfiguration(
926				"Cannot add multiple signers concerning the same account.".to_string(),
927			));
928		}
929
930		self.check_and_throw_if_max_attributes_exceeded(signers.len(), self.attributes.len())?;
931
932		self.signers = signers;
933		Ok(self)
934	}
935
936	/// Adds transaction attributes to the transaction.
937	///
938	/// Transaction attributes provide additional metadata or functionality to the transaction.
939	/// This method checks for duplicate attribute types and ensures the total number of attributes
940	/// does not exceed the maximum allowed.
941	///
942	/// # Arguments
943	///
944	/// * `attributes` - A vector of `TransactionAttribute` objects to add to the transaction.
945	///
946	/// # Returns
947	///
948	/// A `Result` containing a mutable reference to the `TransactionBuilder` for method chaining,
949	/// or a `TransactionError` if adding the attributes would exceed the maximum allowed number
950	/// of attributes or if an attribute of the same type already exists.
951	///
952	/// # Examples
953	///
954	/// ```rust
955	/// use neo3::neo_builder::{TransactionBuilder, TransactionAttribute};
956	/// use neo3::neo_clients::{HttpProvider, RpcClient};
957	///
958	/// let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
959	/// let client = RpcClient::new(provider);
960	/// let mut tx_builder = TransactionBuilder::with_client(&client);
961	///
962	/// // Add a high-priority attribute
963	/// let high_priority_attr = TransactionAttribute::HighPriority;
964	///
965	/// // Add a not-valid-before attribute
966	/// let not_valid_before_attr = TransactionAttribute::NotValidBefore { height: 1000 };
967	///
968	/// tx_builder.add_attributes(vec![high_priority_attr, not_valid_before_attr]).unwrap();
969	/// ```
970	pub fn add_attributes(
971		&mut self,
972		attributes: Vec<TransactionAttribute>,
973	) -> Result<&mut Self, TransactionError> {
974		self.check_and_throw_if_max_attributes_exceeded(
975			self.signers.len(),
976			self.attributes.len() + attributes.len(),
977		)?;
978		for attr in attributes {
979			match attr {
980				TransactionAttribute::HighPriority => {
981					self.add_high_priority_attribute(attr)?;
982				},
983				TransactionAttribute::NotValidBefore { height } => {
984					self.add_not_valid_before_attribute(attr)?;
985				},
986				TransactionAttribute::Conflicts { hash } => {
987					self.add_conflicts_attribute(attr)?;
988				},
989				// TransactionAttribute::OracleResponse(oracle_response) => {
990				//     self.add_oracle_response_attribute(oracle_response);
991				// },
992				_ => {
993					// For other cases or any default, just add the attribute directly to the Vec
994					self.attributes.push(attr);
995				},
996			}
997		}
998		Ok(self)
999	}
1000
1001	fn add_high_priority_attribute(
1002		&mut self,
1003		attr: TransactionAttribute,
1004	) -> Result<(), TransactionError> {
1005		if self.is_high_priority() {
1006			return Err(TransactionError::TransactionConfiguration(
1007				"A transaction can only have one HighPriority attribute.".to_string(),
1008			));
1009		}
1010		// Add the attribute to the attributes vector
1011		self.attributes.push(attr);
1012		Ok(())
1013	}
1014
1015	fn add_not_valid_before_attribute(
1016		&mut self,
1017		attr: TransactionAttribute,
1018	) -> Result<(), TransactionError> {
1019		if self.has_attribute_of_type(TransactionAttribute::NotValidBefore { height: 0 }) {
1020			return Err(TransactionError::TransactionConfiguration(
1021				"A transaction can only have one NotValidBefore attribute.".to_string(),
1022			));
1023		}
1024		// Add the attribute to the attributes vector
1025		self.attributes.push(attr);
1026		Ok(())
1027	}
1028
1029	fn add_conflicts_attribute(
1030		&mut self,
1031		attr: TransactionAttribute,
1032	) -> Result<(), TransactionError> {
1033		if self.has_attribute(&attr) {
1034			let hash = attr.get_hash().ok_or_else(|| {
1035				TransactionError::IllegalState(
1036					"Expected Conflicts attribute to have a hash".to_string(),
1037				)
1038			})?;
1039
1040			return Err(TransactionError::TransactionConfiguration(format!(
1041				"There already exists a conflicts attribute for the hash {} in this transaction.",
1042				hash
1043			)));
1044		}
1045		// Add the attribute to the attributes vector
1046		self.attributes.push(attr);
1047		Ok(())
1048	}
1049
1050	// Check if the attributes vector has an attribute of the specified type
1051	fn has_attribute_of_type(&self, attr_type: TransactionAttribute) -> bool {
1052		self.attributes.iter().any(|attr| match (attr, &attr_type) {
1053			(
1054				TransactionAttribute::NotValidBefore { .. },
1055				TransactionAttribute::NotValidBefore { .. },
1056			) => true,
1057			(TransactionAttribute::HighPriority, TransactionAttribute::HighPriority) => true,
1058			_ => false,
1059		})
1060	}
1061
1062	fn has_attribute(&self, attr: &TransactionAttribute) -> bool {
1063		self.attributes.iter().any(|a| a == attr)
1064	}
1065
1066	// Check specifically for the HighPriority attribute
1067	fn is_high_priority(&self) -> bool {
1068		self.has_attribute_of_type(TransactionAttribute::HighPriority)
1069	}
1070
1071	fn contains_duplicate_signers(&self, signers: &Vec<Signer>) -> bool {
1072		let signer_list: Vec<H160> = signers.iter().map(|s| s.get_signer_hash().clone()).collect();
1073		let signer_set: HashSet<_> = signer_list.iter().collect();
1074		signer_list.len() != signer_set.len()
1075	}
1076
1077	fn check_and_throw_if_max_attributes_exceeded(
1078		&self,
1079		total_signers: usize,
1080		total_attributes: usize,
1081	) -> Result<(), TransactionError> {
1082		let max_attributes = NeoConstants::MAX_TRANSACTION_ATTRIBUTES.try_into().map_err(|e| {
1083			TransactionError::IllegalState(format!(
1084				"Failed to convert MAX_TRANSACTION_ATTRIBUTES to usize: {}",
1085				e
1086			))
1087		})?;
1088
1089		if total_signers + total_attributes > max_attributes {
1090			return Err(TransactionError::TransactionConfiguration(format!(
1091				"A transaction cannot have more than {} attributes (including signers).",
1092				NeoConstants::MAX_TRANSACTION_ATTRIBUTES
1093			)));
1094		}
1095		Ok(())
1096	}
1097
1098	// pub fn is_high_priority(&self) -> bool {
1099	// 	self.attributes
1100	// 		.iter()
1101	// 		.any(|attr| matches!(attr, TransactionAttribute::HighPriority))
1102	// }
1103
1104	async fn is_allowed_for_high_priority(&self) -> bool {
1105		let client = match self.client {
1106			Some(client) => client,
1107			None => return false, // If no client is available, we can't verify committee membership
1108		};
1109
1110		let response =
1111			match client.get_committee().await.map_err(|e| TransactionError::ProviderError(e)) {
1112				Ok(response) => response,
1113				Err(_) => return false, // If we can't get committee info, assume not allowed
1114			};
1115
1116		// Map the Vec<String> response to Vec<Hash160>
1117		let committee: HashSet<H160> = response
1118			.iter()
1119			.filter_map(|key_str| {
1120				// Convert the String to Hash160
1121				let public_key = Secp256r1PublicKey::from_encoded(key_str)?;
1122				Some(public_key_to_script_hash(&public_key)) // Handle potential parsing errors gracefully
1123			})
1124			.collect();
1125
1126		let signers_contain_committee_member = self
1127			.signers
1128			.iter()
1129			.map(|signer| signer.get_signer_hash())
1130			.any(|script_hash| committee.contains(&script_hash));
1131
1132		if signers_contain_committee_member {
1133			return true;
1134		}
1135
1136		return self.signers_contain_multi_sig_with_committee_member(&committee);
1137	}
1138
1139	/// Checks if the sender account of this transaction can cover the network and system fees.
1140	/// If not, executes the given consumer supplying it with the required fee and the sender's GAS balance.
1141	///
1142	/// The check and potential execution of the consumer is only performed when the transaction is built, i.e., when calling `TransactionBuilder::sign` or `TransactionBuilder::get_unsigned_transaction`.
1143	/// - Parameter consumer: The consumer
1144	/// - Returns: This transaction builder (self)
1145	/// Checks if the sender account can cover the transaction fees and executes a callback if not.
1146	///
1147	/// This method allows you to provide a callback function that will be executed if the sender
1148	/// account does not have enough GAS to cover the network and system fees. The callback
1149	/// receives the required fee amount and the sender's current balance.
1150	///
1151	/// # Arguments
1152	///
1153	/// * `consumer` - A callback function that takes two `i64` parameters: the required fee and
1154	///   the sender's current balance.
1155	///
1156	/// # Returns
1157	///
1158	/// A `Result` containing a mutable reference to the `TransactionBuilder` for method chaining,
1159	/// or a `TransactionError` if a fee error handler is already set.
1160	///
1161	/// # Examples
1162	///
1163	/// ```rust,no_run
1164	/// use neo3::neo_builder::TransactionBuilder;
1165	/// use neo3::neo_clients::{HttpProvider, RpcClient};
1166	///
1167	/// #[tokio::main]
1168	/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
1169	///     let provider = HttpProvider::new("https://testnet1.neo.org:443").unwrap();
1170	///     let client = RpcClient::new(provider);
1171	///     
1172	///     let mut tx_builder = TransactionBuilder::with_client(&client);
1173	///     
1174	///     // Add a callback for insufficient funds
1175	///     tx_builder.do_if_sender_cannot_cover_fees(|required_fee, balance| {
1176	///         println!("Insufficient funds: Required {} GAS, but only have {} GAS",
1177	///             required_fee as f64 / 100_000_000.0,
1178	///             balance as f64 / 100_000_000.0);
1179	///     })?;
1180	///     
1181	///     Ok(())
1182	/// }
1183	/// ```
1184	pub fn do_if_sender_cannot_cover_fees<F>(
1185		&mut self,
1186		mut consumer: F,
1187	) -> Result<&mut Self, TransactionError>
1188	where
1189		F: FnMut(i64, i64) + Send + Sync + 'static,
1190	{
1191		if self.fee_error.is_some() {
1192			return Err(TransactionError::IllegalState(
1193                "Cannot handle a consumer for this case, since an exception will be thrown if the sender cannot cover the fees.".to_string(),
1194            ));
1195		}
1196		let consumer = RefCell::new(consumer);
1197		self.fee_consumer = Some(Box::new(move |fee, balance| {
1198			let mut consumer = consumer.borrow_mut();
1199			consumer(fee, balance);
1200		}));
1201		Ok(self)
1202	}
1203
1204	/// Checks if the sender account of this transaction can cover the network and system fees.
1205	/// If not, otherwise throw an error created by the provided supplier.
1206	///
1207	/// The check and potential throwing of the exception is only performed when the transaction is built, i.e., when calling `TransactionBuilder::sign` or `TransactionBuilder::get_unsigned_transaction`.
1208	/// - Parameter error: The error to throw
1209	/// - Returns: This transaction builder (self)
1210	pub fn throw_if_sender_cannot_cover_fees(
1211		&mut self,
1212		error: TransactionError,
1213	) -> Result<&mut Self, TransactionError> {
1214		if self.fee_consumer.is_some() {
1215			return Err(TransactionError::IllegalState(
1216                "Cannot handle a supplier for this case, since a consumer will be executed if the sender cannot cover the fees.".to_string(),
1217            ));
1218		}
1219		self.fee_error = Some(error);
1220		Ok(self)
1221	}
1222
1223	async fn can_send_cover_fees(&self, fees: u64) -> Result<bool, BuilderError> {
1224		let balance = self.get_sender_balance().await?;
1225		Ok(balance >= fees)
1226	}
1227
1228	// async fn get_sender_gas_balance(&self) -> Result<u64, BuilderError> {
1229	// 	let sender_hash = self.signers[0].get_signer_hash();
1230	// 	let result = NEO_INSTANCE
1231	// 		.read()
1232	// 		.unwrap()
1233	// 		.invoke_function(
1234	// 			&H160::from(Self::GAS_TOKEN_HASH),
1235	// 			Self::BALANCE_OF_FUNCTION.to_string(),
1236	// 			vec![sender_hash.into()],
1237	// 			vec![],
1238	// 		)
1239	// 		.request()
1240	// 		.await?;
1241	//
1242	// 	Ok(result.stack[0].as_int().unwrap() as u64)
1243	// }
1244}