1use alloy::network::{
2 Network, TransactionBuilder, TransactionBuilderError, UnbuiltTransactionError,
3};
4use alloy::primitives::{B256, Bytes, TxKind, U256};
5
6use crate::contracts::l2::contract_deployer::CONTRACT_DEPLOYER_ADDRESS;
7use crate::network::{tx_type::TxType, unsigned_tx::eip712::TxEip712};
8
9use super::unsigned_tx::eip712::{BytecodeHashError, PaymasterParams, hash_bytecode};
10use super::{Zksync, unsigned_tx::eip712::Eip712Meta};
11
12#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct TransactionRequest {
21 #[serde(flatten)]
22 base: alloy::rpc::types::transaction::TransactionRequest,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 eip_712_meta: Option<Eip712Meta>,
25}
26
27impl Default for TransactionRequest {
28 fn default() -> Self {
29 Self {
30 base: alloy::rpc::types::transaction::TransactionRequest {
31 transaction_type: Some(TxType::Eip712 as u8),
32 ..Default::default()
33 },
34 eip_712_meta: Default::default(),
35 }
36 }
37}
38
39impl TransactionRequest {
40 pub fn gas_per_pubdata(&self) -> Option<U256> {
42 self.eip_712_meta.as_ref().map(|meta| meta.gas_per_pubdata)
43 }
44
45 pub fn set_gas_per_pubdata(&mut self, gas_per_pubdata: U256) {
47 self.eip_712_meta
48 .get_or_insert_with(Eip712Meta::default)
49 .gas_per_pubdata = gas_per_pubdata;
50 }
51
52 pub fn with_gas_per_pubdata(mut self, gas_per_pubdata: U256) -> Self {
54 self.set_gas_per_pubdata(gas_per_pubdata);
55 self
56 }
57
58 pub fn factory_deps(&self) -> Option<&Vec<Bytes>> {
60 self.eip_712_meta
61 .as_ref()
62 .map(|meta| meta.factory_deps.as_ref())
63 }
64
65 pub fn set_factory_deps(&mut self, factory_deps: Vec<Bytes>) {
67 self.eip_712_meta
68 .get_or_insert_with(Eip712Meta::default)
69 .factory_deps = factory_deps;
70 }
71
72 pub fn with_factory_deps(mut self, factory_deps: Vec<Bytes>) -> Self {
74 self.set_factory_deps(factory_deps);
75 self
76 }
77
78 pub fn custom_signature(&self) -> Option<&Bytes> {
80 self.eip_712_meta
81 .as_ref()
82 .and_then(|meta| meta.custom_signature.as_ref())
83 }
84
85 pub fn set_custom_signature(&mut self, custom_signature: Bytes) {
87 self.eip_712_meta
88 .get_or_insert_with(Eip712Meta::default)
89 .custom_signature = Some(custom_signature);
90 }
91
92 pub fn with_custom_signature(mut self, custom_signature: Bytes) -> Self {
94 self.set_custom_signature(custom_signature);
95 self
96 }
97
98 pub fn paymaster_params(&self) -> Option<&PaymasterParams> {
100 self.eip_712_meta
101 .as_ref()
102 .and_then(|meta| meta.paymaster_params.as_ref())
103 }
104
105 pub fn set_paymaster_params(&mut self, paymaster_params: PaymasterParams) {
107 self.eip_712_meta
108 .get_or_insert_with(Eip712Meta::default)
109 .paymaster_params = Some(paymaster_params);
110 }
111
112 pub fn with_paymaster_params(mut self, paymaster_params: PaymasterParams) -> Self {
114 self.set_paymaster_params(paymaster_params);
115 self
116 }
117
118 pub fn with_create2_params(
120 self,
121 salt: B256,
122 code: Vec<u8>,
123 constructor_data: Vec<u8>,
124 factory_deps: Vec<Vec<u8>>,
125 ) -> Result<Self, BytecodeHashError> {
126 let bytecode_hash = hash_bytecode(&code)?;
127 let factory_deps = factory_deps
128 .into_iter()
129 .chain(vec![code])
130 .map(Into::into)
131 .collect();
132 let input = crate::contracts::l2::contract_deployer::encode_create2_calldata(
133 salt,
134 bytecode_hash.into(),
135 constructor_data.into(),
136 );
137 Ok(self
138 .with_to(CONTRACT_DEPLOYER_ADDRESS)
139 .with_input(input)
140 .with_factory_deps(factory_deps))
141 }
142
143 pub fn with_create_params(
145 self,
146 code: Vec<u8>,
147 constructor_data: Vec<u8>,
148 factory_deps: Vec<Vec<u8>>,
149 ) -> Result<Self, BytecodeHashError> {
150 let bytecode_hash = hash_bytecode(&code)?;
151 let factory_deps = factory_deps
152 .into_iter()
153 .chain(vec![code])
154 .map(Into::into)
155 .collect();
156 let input = crate::contracts::l2::contract_deployer::encode_create_calldata(
157 bytecode_hash.into(),
158 constructor_data.into(),
159 );
160 Ok(self
161 .with_to(CONTRACT_DEPLOYER_ADDRESS)
162 .with_input(input)
163 .with_factory_deps(factory_deps))
164 }
165}
166
167impl TransactionRequest {
168 #[deprecated(note = "use `set_paymaster_params` instead")]
169 pub fn set_paymaster(&mut self, paymaster_params: PaymasterParams) {
170 self.eip_712_meta
171 .get_or_insert_with(Eip712Meta::default)
172 .paymaster_params = Some(paymaster_params);
173 }
174
175 #[deprecated(note = "use `with_paymaster_params` instead")]
176 pub fn with_paymaster(mut self, paymaster_params: PaymasterParams) -> Self {
177 #[allow(deprecated)]
178 self.set_paymaster(paymaster_params);
179 self
180 }
181
182 #[deprecated(note = "use `with_create_params` instead")]
183 pub fn zksync_deploy(
184 self,
185 code: Vec<u8>,
186 constructor_data: Vec<u8>,
187 factory_deps: Vec<Vec<u8>>,
188 ) -> Result<Self, BytecodeHashError> {
189 #[allow(deprecated)]
190 self.zksync_deploy_inner(None, code, constructor_data, factory_deps)
191 }
192
193 #[deprecated(note = "use `with_create2_params` instead")]
194 pub fn zksync_deploy_with_salt(
195 self,
196 salt: B256,
197 code: Vec<u8>,
198 constructor_data: Vec<u8>,
199 factory_deps: Vec<Vec<u8>>,
200 ) -> Result<Self, BytecodeHashError> {
201 #[allow(deprecated)]
202 self.zksync_deploy_inner(Some(salt), code, constructor_data, factory_deps)
203 }
204
205 #[deprecated(note = "use `with_create_params` or `with_create2_params` instead")]
206 fn zksync_deploy_inner(
207 self,
208 salt: Option<B256>,
209 code: Vec<u8>,
210 constructor_data: Vec<u8>,
211 factory_deps: Vec<Vec<u8>>,
212 ) -> Result<Self, BytecodeHashError> {
213 let bytecode_hash = hash_bytecode(&code)?;
214 let factory_deps = factory_deps
215 .into_iter()
216 .chain(vec![code])
217 .map(Into::into)
218 .collect();
219 let input = match salt {
220 Some(salt) => crate::contracts::l2::contract_deployer::encode_create2_calldata(
221 salt,
222 bytecode_hash.into(),
223 constructor_data.into(),
224 ),
225 None => crate::contracts::l2::contract_deployer::encode_create_calldata(
226 bytecode_hash.into(),
227 constructor_data.into(),
228 ),
229 };
230 Ok(self
231 .with_to(CONTRACT_DEPLOYER_ADDRESS)
232 .with_input(input)
233 .with_factory_deps(factory_deps))
234 }
235}
236
237impl From<crate::network::unsigned_tx::TypedTransaction> for TransactionRequest {
238 fn from(value: crate::network::unsigned_tx::TypedTransaction) -> Self {
239 match value {
240 crate::network::unsigned_tx::TypedTransaction::Native(inner) => Self {
241 base: inner.into(),
242 eip_712_meta: None,
243 },
244 crate::network::unsigned_tx::TypedTransaction::Eip712(inner) => Self {
245 base: inner.clone().into(),
246 eip_712_meta: inner.eip712_meta,
247 },
248 }
249 }
250}
251
252impl From<crate::network::tx_envelope::TxEnvelope> for TransactionRequest {
253 fn from(value: crate::network::tx_envelope::TxEnvelope) -> Self {
254 match value {
255 crate::network::tx_envelope::TxEnvelope::Native(inner) => Self {
256 base: inner.into(),
257 eip_712_meta: None,
258 },
259 crate::network::tx_envelope::TxEnvelope::Eip712(signed) => Self {
260 base: signed.tx().clone().into(),
261 eip_712_meta: signed.tx().clone().eip712_meta,
262 },
263 }
264 }
265}
266
267impl TransactionBuilder<Zksync> for TransactionRequest {
270 fn chain_id(&self) -> Option<alloy::primitives::ChainId> {
271 TransactionBuilder::chain_id(&self.base)
272 }
273
274 fn set_chain_id(&mut self, chain_id: alloy::primitives::ChainId) {
275 TransactionBuilder::set_chain_id(&mut self.base, chain_id)
276 }
277
278 fn nonce(&self) -> Option<u64> {
279 TransactionBuilder::nonce(&self.base)
280 }
281
282 fn set_nonce(&mut self, nonce: u64) {
283 TransactionBuilder::set_nonce(&mut self.base, nonce)
284 }
285
286 fn take_nonce(&mut self) -> Option<u64> {
287 TransactionBuilder::take_nonce(&mut self.base)
288 }
289
290 fn input(&self) -> Option<&alloy::primitives::Bytes> {
291 TransactionBuilder::input(&self.base)
292 }
293
294 fn set_input<T: Into<alloy::primitives::Bytes>>(&mut self, input: T) {
295 TransactionBuilder::set_input(&mut self.base, input.into())
296 }
297
298 fn from(&self) -> Option<alloy::primitives::Address> {
299 TransactionBuilder::from(&self.base)
300 }
301
302 fn set_from(&mut self, from: alloy::primitives::Address) {
303 TransactionBuilder::set_from(&mut self.base, from)
304 }
305
306 fn kind(&self) -> Option<alloy::primitives::TxKind> {
307 TransactionBuilder::kind(&self.base)
308 }
309
310 fn clear_kind(&mut self) {
311 TransactionBuilder::clear_kind(&mut self.base)
312 }
313
314 fn set_kind(&mut self, kind: alloy::primitives::TxKind) {
315 TransactionBuilder::set_kind(&mut self.base, kind)
316 }
317
318 fn value(&self) -> Option<alloy::primitives::U256> {
319 TransactionBuilder::value(&self.base)
320 }
321
322 fn set_value(&mut self, value: alloy::primitives::U256) {
323 TransactionBuilder::set_value(&mut self.base, value)
324 }
325
326 fn gas_price(&self) -> Option<u128> {
327 TransactionBuilder::gas_price(&self.base)
328 }
329
330 fn set_gas_price(&mut self, gas_price: u128) {
331 TransactionBuilder::set_gas_price(&mut self.base, gas_price)
332 }
333
334 fn max_fee_per_gas(&self) -> Option<u128> {
335 TransactionBuilder::max_fee_per_gas(&self.base)
336 }
337
338 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) {
339 TransactionBuilder::set_max_fee_per_gas(&mut self.base, max_fee_per_gas)
340 }
341
342 fn max_priority_fee_per_gas(&self) -> Option<u128> {
343 TransactionBuilder::max_priority_fee_per_gas(&self.base)
344 }
345
346 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) {
347 TransactionBuilder::set_max_priority_fee_per_gas(&mut self.base, max_priority_fee_per_gas)
348 }
349
350 fn gas_limit(&self) -> Option<u64> {
351 TransactionBuilder::gas_limit(&self.base)
352 }
353
354 fn set_gas_limit(&mut self, gas_limit: u64) {
355 TransactionBuilder::set_gas_limit(&mut self.base, gas_limit)
356 }
357
358 fn access_list(&self) -> Option<&alloy::rpc::types::AccessList> {
359 TransactionBuilder::access_list(&self.base)
360 }
361
362 fn set_access_list(&mut self, access_list: alloy::rpc::types::AccessList) {
363 TransactionBuilder::set_access_list(&mut self.base, access_list)
364 }
365
366 fn complete_type(&self, ty: <Zksync as Network>::TxType) -> Result<(), Vec<&'static str>> {
367 match ty {
369 TxType::Eip712 => {
370 TransactionBuilder::complete_type(&self.base, alloy::consensus::TxType::Eip1559)
372 }
373 _ if ty.as_eth_type().is_some() => {
374 TransactionBuilder::complete_type(&self.base, ty.as_eth_type().unwrap())
375 }
376 _ => Err(vec!["Unsupported transaction type"]),
377 }
378 }
379
380 fn can_submit(&self) -> bool {
381 TransactionBuilder::can_submit(&self.base)
382 }
383
384 fn can_build(&self) -> bool {
385 if self.eip_712_meta.is_some() {
386 let common = self.base.gas.is_some() && self.base.nonce.is_some();
387 let eip1559 =
388 self.base.max_fee_per_gas.is_some() && self.base.max_priority_fee_per_gas.is_some();
389 return common && eip1559;
391 }
392
393 TransactionBuilder::can_build(&self.base)
394 }
395
396 fn output_tx_type(&self) -> <Zksync as Network>::TxType {
397 if self.eip_712_meta.is_some() {
398 return TxType::Eip712;
399 }
400
401 TransactionBuilder::output_tx_type(&self.base).into()
402 }
403
404 fn output_tx_type_checked(&self) -> Option<<Zksync as Network>::TxType> {
405 if self.eip_712_meta.is_some() {
406 if !self.can_build() {
407 return None;
408 }
409 return Some(TxType::Eip712);
410 }
411
412 TransactionBuilder::output_tx_type_checked(&self.base).map(Into::into)
413 }
414
415 fn prep_for_submission(&mut self) {
416 TransactionBuilder::prep_for_submission(&mut self.base);
418
419 if self.eip_712_meta.is_some() {
420 self.base.transaction_type = Some(TxType::Eip712 as u8);
421 self.base.gas_price = None;
422 self.base.blob_versioned_hashes = None;
423 self.base.sidecar = None;
424 }
425 }
426
427 fn build_unsigned(
428 self,
429 ) -> alloy::network::BuildResult<crate::network::unsigned_tx::TypedTransaction, Zksync> {
430 if self.eip_712_meta.is_some() {
431 let mut missing = Vec::new();
432 if self.base.max_fee_per_gas.is_none() {
434 missing.push("max_fee_per_gas");
435 }
436 if self.base.max_priority_fee_per_gas.is_none() {
437 missing.push("max_priority_fee_per_gas");
438 }
439
440 if !missing.is_empty() {
441 return Err(TransactionBuilderError::InvalidTransactionRequest(
442 TxType::Eip712,
443 missing,
444 )
445 .into_unbuilt(self));
446 }
447
448 let TxKind::Call(to) = self.base.to.unwrap_or_default() else {
449 return Err(TransactionBuilderError::InvalidTransactionRequest(
450 TxType::Eip712,
451 vec!["to (recipient) must be specified for EIP-712 transactions"],
452 )
453 .into_unbuilt(self));
454 };
455
456 let tx = TxEip712 {
458 chain_id: self.base.chain_id.unwrap(),
459 nonce: U256::from(self.base.nonce.unwrap()), gas: self.base.gas.unwrap(),
461 max_fee_per_gas: self.base.max_fee_per_gas.unwrap(),
462 max_priority_fee_per_gas: self.base.max_priority_fee_per_gas.unwrap(),
463 eip712_meta: self.eip_712_meta,
464 from: self.base.from.unwrap(),
465 to,
466 value: self.base.value.unwrap_or_default(),
467 input: self.base.input.into_input().unwrap_or_default(),
468 };
469 return Ok(crate::network::unsigned_tx::TypedTransaction::Eip712(tx));
470 }
471
472 use TransactionBuilderError::*;
473 let inner = self.base;
474
475 let result = TransactionBuilder::build_unsigned(inner);
476 match result {
477 Ok(tx) => Ok(crate::network::unsigned_tx::TypedTransaction::Native(tx)),
478 Err(err) => {
479 let UnbuiltTransactionError { request, error } = err;
480 let wrapped_request = Self {
481 base: request,
482 eip_712_meta: None,
483 };
484 let error = match error {
485 InvalidTransactionRequest(tx, fields) => {
486 InvalidTransactionRequest(tx.into(), fields)
487 }
488 UnsupportedSignatureType => UnsupportedSignatureType,
489 Signer(s) => Signer(s),
490 Custom(c) => Custom(c),
491 };
492
493 Err(UnbuiltTransactionError {
494 request: wrapped_request,
495 error,
496 })
497 }
498 }
499 }
500
501 async fn build<W: alloy::network::NetworkWallet<Zksync>>(
502 self,
503 wallet: &W,
504 ) -> Result<<Zksync as Network>::TxEnvelope, TransactionBuilderError<Zksync>> {
505 Ok(wallet.sign_request(self).await?)
506 }
507}
508
509impl From<alloy::rpc::types::transaction::TransactionRequest> for TransactionRequest {
510 fn from(value: alloy::rpc::types::transaction::TransactionRequest) -> Self {
511 Self {
512 base: value,
513 ..Default::default()
514 }
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521 use alloy::consensus::Transaction as _;
522 use alloy::primitives::U256;
523 use alloy::rpc::types::transaction::TransactionRequest as AlloyTransactionRequest;
524
525 #[test]
526 fn test_default_transaction_request() {
527 let tx_request = TransactionRequest::default();
528 assert_eq!(tx_request.base.transaction_type, Some(TxType::Eip712 as u8));
529 assert!(tx_request.eip_712_meta.is_none());
530 }
531
532 #[test]
533 fn test_set_gas_per_pubdata() {
534 let mut tx_request = TransactionRequest::default();
535 let gas_per_pubdata = U256::from(1000);
536 tx_request.set_gas_per_pubdata(gas_per_pubdata);
537 assert_eq!(tx_request.gas_per_pubdata(), Some(gas_per_pubdata));
538 }
539
540 #[test]
541 fn test_with_gas_per_pubdata() {
542 let gas_per_pubdata = U256::from(1000);
543 let tx_request = TransactionRequest::default().with_gas_per_pubdata(gas_per_pubdata);
544 assert_eq!(tx_request.gas_per_pubdata(), Some(gas_per_pubdata));
545 }
546
547 #[test]
548 fn test_set_factory_deps() {
549 let mut tx_request = TransactionRequest::default();
550 let factory_deps = vec![Bytes::from(vec![1, 2, 3])];
551 tx_request.set_factory_deps(factory_deps.clone());
552 assert_eq!(tx_request.factory_deps(), Some(&factory_deps));
553 }
554
555 #[test]
556 fn test_with_factory_deps() {
557 let factory_deps = vec![Bytes::from(vec![1, 2, 3])];
558 let tx_request = TransactionRequest::default().with_factory_deps(factory_deps.clone());
559 assert_eq!(tx_request.factory_deps(), Some(&factory_deps));
560 }
561
562 #[test]
563 fn test_set_custom_signature() {
564 let mut tx_request = TransactionRequest::default();
565 let custom_signature = Bytes::from(vec![1, 2, 3]);
566 tx_request.set_custom_signature(custom_signature.clone());
567 assert_eq!(tx_request.custom_signature(), Some(&custom_signature));
568 }
569
570 #[test]
571 fn test_with_custom_signature() {
572 let custom_signature = Bytes::from(vec![1, 2, 3]);
573 let tx_request =
574 TransactionRequest::default().with_custom_signature(custom_signature.clone());
575 assert_eq!(tx_request.custom_signature(), Some(&custom_signature));
576 }
577
578 #[test]
579 fn test_set_paymaster_params() {
580 let mut tx_request = TransactionRequest::default();
581 let paymaster_params = PaymasterParams::default();
582 tx_request.set_paymaster_params(paymaster_params.clone());
583 assert_eq!(tx_request.paymaster_params(), Some(&paymaster_params));
584 }
585
586 #[test]
587 fn test_with_paymaster_params() {
588 let paymaster_params = PaymasterParams::default();
589 let tx_request =
590 TransactionRequest::default().with_paymaster_params(paymaster_params.clone());
591 assert_eq!(tx_request.paymaster_params(), Some(&paymaster_params));
592 }
593
594 #[test]
595 fn test_from_alloy_transaction_request() {
596 let alloy_tx_request = AlloyTransactionRequest::default();
597 let tx_request: TransactionRequest = alloy_tx_request.clone().into();
598 assert_eq!(tx_request.base, alloy_tx_request);
599 }
600
601 #[test]
602 fn test_prep_for_submission_with_eip712_meta() {
603 let mut tx_request = TransactionRequest::default();
604 tx_request.set_gas_per_pubdata(U256::from(1000));
605 tx_request.prep_for_submission();
606 assert_eq!(tx_request.base.transaction_type, Some(TxType::Eip712 as u8));
607 assert!(tx_request.base.gas_price.is_none());
608 assert!(tx_request.base.blob_versioned_hashes.is_none());
609 assert!(tx_request.base.sidecar.is_none());
610 }
611
612 #[test]
613 fn test_can_build_with_eip712_meta() {
614 let mut tx_request = TransactionRequest::default();
615 tx_request.set_gas_per_pubdata(U256::from(1000));
616 tx_request.base.gas = Some(21000);
617 tx_request.base.nonce = Some(0);
618 tx_request.base.max_fee_per_gas = Some(100);
619 tx_request.base.max_priority_fee_per_gas = Some(1);
620 assert!(tx_request.can_build());
621 }
622
623 #[test]
624 fn test_cannot_build_without_gas() {
625 let mut tx_request = TransactionRequest::default();
626 tx_request.set_gas_per_pubdata(U256::from(1000));
627 tx_request.base.nonce = Some(0);
628 tx_request.base.max_fee_per_gas = Some(100);
629 tx_request.base.max_priority_fee_per_gas = Some(1);
630 assert!(!tx_request.can_build());
631 }
632
633 #[test]
634 fn test_cannot_build_without_nonce() {
635 let mut tx_request = TransactionRequest::default();
636 tx_request.set_gas_per_pubdata(U256::from(1000));
637 tx_request.base.gas = Some(21000);
638 tx_request.base.max_fee_per_gas = Some(100);
639 tx_request.base.max_priority_fee_per_gas = Some(1);
640 assert!(!tx_request.can_build());
641 }
642
643 #[test]
644 fn test_cannot_build_without_max_fee_per_gas() {
645 let mut tx_request = TransactionRequest::default();
646 tx_request.set_gas_per_pubdata(U256::from(1000));
647 tx_request.base.gas = Some(21000);
648 tx_request.base.nonce = Some(0);
649 tx_request.base.max_priority_fee_per_gas = Some(1);
650 assert!(!tx_request.can_build());
651 }
652
653 #[test]
654 fn test_cannot_build_without_max_priority_fee_per_gas() {
655 let mut tx_request = TransactionRequest::default();
656 tx_request.set_gas_per_pubdata(U256::from(1000));
657 tx_request.base.gas = Some(21000);
658 tx_request.base.nonce = Some(0);
659 tx_request.base.max_fee_per_gas = Some(100);
660 assert!(!tx_request.can_build());
661 }
662
663 #[test]
664 fn test_output_tx_type_checked_with_eip712_meta() {
665 let mut tx_request = TransactionRequest::default();
666 tx_request.set_gas_per_pubdata(U256::from(1000));
667 tx_request.base.gas = Some(21000);
668 tx_request.base.nonce = Some(0);
669 tx_request.base.max_fee_per_gas = Some(100);
670 tx_request.base.max_priority_fee_per_gas = Some(1);
671 assert_eq!(tx_request.output_tx_type_checked(), Some(TxType::Eip712));
672 }
673
674 #[test]
675 fn test_output_tx_type_checked_without_eip712_meta() {
676 let mut tx_request = TransactionRequest::default();
677 tx_request.base.gas = Some(21000);
678 tx_request.base.nonce = Some(0);
679 tx_request.base.max_fee_per_gas = Some(100);
680 tx_request.base.max_priority_fee_per_gas = Some(1);
681 assert_eq!(tx_request.output_tx_type_checked(), None);
682 }
683
684 #[test]
685 fn test_output_tx_type_checked_cannot_build() {
686 let mut tx_request = TransactionRequest::default();
687 tx_request.set_gas_per_pubdata(U256::from(1000));
688 tx_request.base.gas = Some(21000);
689 tx_request.base.nonce = Some(0);
690 assert_eq!(tx_request.output_tx_type_checked(), None);
691 }
692
693 #[test]
694 fn test_build_unsigned_with_eip712_meta() {
695 let mut tx_request = TransactionRequest::default();
696 tx_request.set_gas_per_pubdata(U256::from(1000));
697 tx_request.base.gas = Some(21000);
698 tx_request.base.nonce = Some(0);
699 tx_request.base.max_fee_per_gas = Some(100);
700 tx_request.base.max_priority_fee_per_gas = Some(1);
701 tx_request.base.to = Some(TxKind::Call(CONTRACT_DEPLOYER_ADDRESS));
702 tx_request.base.chain_id = Some(1);
703 tx_request.base.from = Some(CONTRACT_DEPLOYER_ADDRESS);
704 let result = tx_request.build_unsigned();
705 assert!(result.is_ok());
706 if let Ok(crate::network::unsigned_tx::TypedTransaction::Eip712(tx)) = result {
707 assert_eq!(tx.chain_id, 1);
708 assert_eq!(tx.nonce, U256::from(0));
709 assert_eq!(tx.gas, 21000);
710 assert_eq!(tx.max_fee_per_gas, 100);
711 assert_eq!(tx.max_priority_fee_per_gas, 1);
712 assert_eq!(tx.from, CONTRACT_DEPLOYER_ADDRESS);
713 } else {
714 panic!("Expected Eip712 transaction");
715 }
716 }
717
718 #[test]
719 fn test_build_unsigned_without_eip712_meta() {
720 let mut tx_request = TransactionRequest::default();
721 tx_request.base.gas = Some(21000);
722 tx_request.base.nonce = Some(0);
723 tx_request.base.max_fee_per_gas = Some(100);
724 tx_request.base.max_priority_fee_per_gas = Some(1);
725 tx_request.base.to = Some(TxKind::Call(CONTRACT_DEPLOYER_ADDRESS));
726 tx_request.base.chain_id = Some(1);
727 tx_request.base.from = Some(CONTRACT_DEPLOYER_ADDRESS);
728 let result = tx_request.build_unsigned();
729 assert!(result.is_ok());
730 if let Ok(crate::network::unsigned_tx::TypedTransaction::Native(tx)) = result {
731 assert_eq!(tx.chain_id(), Some(1));
732 assert_eq!(tx.nonce(), 0);
733 assert_eq!(tx.gas_limit(), 21000);
734 assert_eq!(tx.max_fee_per_gas(), 100);
735 assert_eq!(tx.max_priority_fee_per_gas(), Some(1));
736 assert_eq!(tx.to(), Some(CONTRACT_DEPLOYER_ADDRESS));
737 } else {
738 panic!("Expected Native transaction");
739 }
740 }
741
742 #[test]
743 fn test_build_unsigned_missing_fields() {
744 let tx_request = TransactionRequest::default();
745 let result = tx_request.build_unsigned();
746 assert!(result.is_err());
747 }
748}