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 input(&self) -> Option<&alloy::primitives::Bytes> {
287 TransactionBuilder::input(&self.base)
288 }
289
290 fn set_input<T: Into<alloy::primitives::Bytes>>(&mut self, input: T) {
291 TransactionBuilder::set_input(&mut self.base, input.into())
292 }
293
294 fn from(&self) -> Option<alloy::primitives::Address> {
295 TransactionBuilder::from(&self.base)
296 }
297
298 fn set_from(&mut self, from: alloy::primitives::Address) {
299 TransactionBuilder::set_from(&mut self.base, from)
300 }
301
302 fn kind(&self) -> Option<alloy::primitives::TxKind> {
303 TransactionBuilder::kind(&self.base)
304 }
305
306 fn clear_kind(&mut self) {
307 TransactionBuilder::clear_kind(&mut self.base)
308 }
309
310 fn set_kind(&mut self, kind: alloy::primitives::TxKind) {
311 TransactionBuilder::set_kind(&mut self.base, kind)
312 }
313
314 fn value(&self) -> Option<alloy::primitives::U256> {
315 TransactionBuilder::value(&self.base)
316 }
317
318 fn set_value(&mut self, value: alloy::primitives::U256) {
319 TransactionBuilder::set_value(&mut self.base, value)
320 }
321
322 fn gas_price(&self) -> Option<u128> {
323 TransactionBuilder::gas_price(&self.base)
324 }
325
326 fn set_gas_price(&mut self, gas_price: u128) {
327 TransactionBuilder::set_gas_price(&mut self.base, gas_price)
328 }
329
330 fn max_fee_per_gas(&self) -> Option<u128> {
331 TransactionBuilder::max_fee_per_gas(&self.base)
332 }
333
334 fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) {
335 TransactionBuilder::set_max_fee_per_gas(&mut self.base, max_fee_per_gas)
336 }
337
338 fn max_priority_fee_per_gas(&self) -> Option<u128> {
339 TransactionBuilder::max_priority_fee_per_gas(&self.base)
340 }
341
342 fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) {
343 TransactionBuilder::set_max_priority_fee_per_gas(&mut self.base, max_priority_fee_per_gas)
344 }
345
346 fn gas_limit(&self) -> Option<u64> {
347 TransactionBuilder::gas_limit(&self.base)
348 }
349
350 fn set_gas_limit(&mut self, gas_limit: u64) {
351 TransactionBuilder::set_gas_limit(&mut self.base, gas_limit)
352 }
353
354 fn access_list(&self) -> Option<&alloy::rpc::types::AccessList> {
355 TransactionBuilder::access_list(&self.base)
356 }
357
358 fn set_access_list(&mut self, access_list: alloy::rpc::types::AccessList) {
359 TransactionBuilder::set_access_list(&mut self.base, access_list)
360 }
361
362 fn complete_type(&self, ty: <Zksync as Network>::TxType) -> Result<(), Vec<&'static str>> {
363 match ty {
365 TxType::Eip712 => {
366 TransactionBuilder::complete_type(&self.base, alloy::consensus::TxType::Eip1559)
368 }
369 _ if ty.as_eth_type().is_some() => {
370 TransactionBuilder::complete_type(&self.base, ty.as_eth_type().unwrap())
371 }
372 _ => Err(vec!["Unsupported transaction type"]),
373 }
374 }
375
376 fn can_submit(&self) -> bool {
377 TransactionBuilder::can_submit(&self.base)
378 }
379
380 fn can_build(&self) -> bool {
381 if self.eip_712_meta.is_some() {
382 let common = self.base.gas.is_some() && self.base.nonce.is_some();
383 let eip1559 =
384 self.base.max_fee_per_gas.is_some() && self.base.max_priority_fee_per_gas.is_some();
385 return common && eip1559;
387 }
388
389 TransactionBuilder::can_build(&self.base)
390 }
391
392 fn output_tx_type(&self) -> <Zksync as Network>::TxType {
393 if self.eip_712_meta.is_some() {
394 return TxType::Eip712;
395 }
396
397 TransactionBuilder::output_tx_type(&self.base).into()
398 }
399
400 fn output_tx_type_checked(&self) -> Option<<Zksync as Network>::TxType> {
401 if self.eip_712_meta.is_some() {
402 if !self.can_build() {
403 return None;
404 }
405 return Some(TxType::Eip712);
406 }
407
408 TransactionBuilder::output_tx_type_checked(&self.base).map(Into::into)
409 }
410
411 fn prep_for_submission(&mut self) {
412 TransactionBuilder::prep_for_submission(&mut self.base);
414
415 if self.eip_712_meta.is_some() {
416 self.base.transaction_type = Some(TxType::Eip712 as u8);
417 self.base.gas_price = None;
418 self.base.blob_versioned_hashes = None;
419 self.base.sidecar = None;
420 }
421 }
422
423 fn build_unsigned(
424 self,
425 ) -> alloy::network::BuildResult<crate::network::unsigned_tx::TypedTransaction, Zksync> {
426 if self.eip_712_meta.is_some() {
427 let mut missing = Vec::new();
428 if self.base.max_fee_per_gas.is_none() {
430 missing.push("max_fee_per_gas");
431 }
432 if self.base.max_priority_fee_per_gas.is_none() {
433 missing.push("max_priority_fee_per_gas");
434 }
435
436 if !missing.is_empty() {
437 return Err(TransactionBuilderError::InvalidTransactionRequest(
438 TxType::Eip712,
439 missing,
440 )
441 .into_unbuilt(self));
442 }
443
444 let TxKind::Call(to) = self.base.to.unwrap_or_default() else {
445 return Err(TransactionBuilderError::InvalidTransactionRequest(
446 TxType::Eip712,
447 vec!["to (recipient) must be specified for EIP-712 transactions"],
448 )
449 .into_unbuilt(self));
450 };
451
452 let tx = TxEip712 {
454 chain_id: self.base.chain_id.unwrap(),
455 nonce: U256::from(self.base.nonce.unwrap()), gas: self.base.gas.unwrap(),
457 max_fee_per_gas: self.base.max_fee_per_gas.unwrap(),
458 max_priority_fee_per_gas: self.base.max_priority_fee_per_gas.unwrap(),
459 eip712_meta: self.eip_712_meta,
460 from: self.base.from.unwrap(),
461 to,
462 value: self.base.value.unwrap_or_default(),
463 input: self.base.input.into_input().unwrap_or_default(),
464 };
465 return Ok(crate::network::unsigned_tx::TypedTransaction::Eip712(tx));
466 }
467
468 use TransactionBuilderError::*;
469 let inner = self.base;
470
471 let result = TransactionBuilder::build_unsigned(inner);
472 match result {
473 Ok(tx) => Ok(crate::network::unsigned_tx::TypedTransaction::Native(tx)),
474 Err(err) => {
475 let UnbuiltTransactionError { request, error } = err;
476 let wrapped_request = Self {
477 base: request,
478 eip_712_meta: None,
479 };
480 let error = match error {
481 InvalidTransactionRequest(tx, fields) => {
482 InvalidTransactionRequest(tx.into(), fields)
483 }
484 UnsupportedSignatureType => UnsupportedSignatureType,
485 Signer(s) => Signer(s),
486 Custom(c) => Custom(c),
487 };
488
489 Err(UnbuiltTransactionError {
490 request: wrapped_request,
491 error,
492 })
493 }
494 }
495 }
496
497 async fn build<W: alloy::network::NetworkWallet<Zksync>>(
498 self,
499 wallet: &W,
500 ) -> Result<<Zksync as Network>::TxEnvelope, TransactionBuilderError<Zksync>> {
501 Ok(wallet.sign_request(self).await?)
502 }
503}
504
505impl From<alloy::rpc::types::transaction::TransactionRequest> for TransactionRequest {
506 fn from(value: alloy::rpc::types::transaction::TransactionRequest) -> Self {
507 Self {
508 base: value,
509 ..Default::default()
510 }
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517 use alloy::consensus::Transaction as _;
518 use alloy::primitives::U256;
519 use alloy::rpc::types::transaction::TransactionRequest as AlloyTransactionRequest;
520
521 #[test]
522 fn test_default_transaction_request() {
523 let tx_request = TransactionRequest::default();
524 assert_eq!(tx_request.base.transaction_type, Some(TxType::Eip712 as u8));
525 assert!(tx_request.eip_712_meta.is_none());
526 }
527
528 #[test]
529 fn test_set_gas_per_pubdata() {
530 let mut tx_request = TransactionRequest::default();
531 let gas_per_pubdata = U256::from(1000);
532 tx_request.set_gas_per_pubdata(gas_per_pubdata);
533 assert_eq!(tx_request.gas_per_pubdata(), Some(gas_per_pubdata));
534 }
535
536 #[test]
537 fn test_with_gas_per_pubdata() {
538 let gas_per_pubdata = U256::from(1000);
539 let tx_request = TransactionRequest::default().with_gas_per_pubdata(gas_per_pubdata);
540 assert_eq!(tx_request.gas_per_pubdata(), Some(gas_per_pubdata));
541 }
542
543 #[test]
544 fn test_set_factory_deps() {
545 let mut tx_request = TransactionRequest::default();
546 let factory_deps = vec![Bytes::from(vec![1, 2, 3])];
547 tx_request.set_factory_deps(factory_deps.clone());
548 assert_eq!(tx_request.factory_deps(), Some(&factory_deps));
549 }
550
551 #[test]
552 fn test_with_factory_deps() {
553 let factory_deps = vec![Bytes::from(vec![1, 2, 3])];
554 let tx_request = TransactionRequest::default().with_factory_deps(factory_deps.clone());
555 assert_eq!(tx_request.factory_deps(), Some(&factory_deps));
556 }
557
558 #[test]
559 fn test_set_custom_signature() {
560 let mut tx_request = TransactionRequest::default();
561 let custom_signature = Bytes::from(vec![1, 2, 3]);
562 tx_request.set_custom_signature(custom_signature.clone());
563 assert_eq!(tx_request.custom_signature(), Some(&custom_signature));
564 }
565
566 #[test]
567 fn test_with_custom_signature() {
568 let custom_signature = Bytes::from(vec![1, 2, 3]);
569 let tx_request =
570 TransactionRequest::default().with_custom_signature(custom_signature.clone());
571 assert_eq!(tx_request.custom_signature(), Some(&custom_signature));
572 }
573
574 #[test]
575 fn test_set_paymaster_params() {
576 let mut tx_request = TransactionRequest::default();
577 let paymaster_params = PaymasterParams::default();
578 tx_request.set_paymaster_params(paymaster_params.clone());
579 assert_eq!(tx_request.paymaster_params(), Some(&paymaster_params));
580 }
581
582 #[test]
583 fn test_with_paymaster_params() {
584 let paymaster_params = PaymasterParams::default();
585 let tx_request =
586 TransactionRequest::default().with_paymaster_params(paymaster_params.clone());
587 assert_eq!(tx_request.paymaster_params(), Some(&paymaster_params));
588 }
589
590 #[test]
591 fn test_from_alloy_transaction_request() {
592 let alloy_tx_request = AlloyTransactionRequest::default();
593 let tx_request: TransactionRequest = alloy_tx_request.clone().into();
594 assert_eq!(tx_request.base, alloy_tx_request);
595 }
596
597 #[test]
598 fn test_prep_for_submission_with_eip712_meta() {
599 let mut tx_request = TransactionRequest::default();
600 tx_request.set_gas_per_pubdata(U256::from(1000));
601 tx_request.prep_for_submission();
602 assert_eq!(tx_request.base.transaction_type, Some(TxType::Eip712 as u8));
603 assert!(tx_request.base.gas_price.is_none());
604 assert!(tx_request.base.blob_versioned_hashes.is_none());
605 assert!(tx_request.base.sidecar.is_none());
606 }
607
608 #[test]
609 fn test_can_build_with_eip712_meta() {
610 let mut tx_request = TransactionRequest::default();
611 tx_request.set_gas_per_pubdata(U256::from(1000));
612 tx_request.base.gas = Some(21000);
613 tx_request.base.nonce = Some(0);
614 tx_request.base.max_fee_per_gas = Some(100);
615 tx_request.base.max_priority_fee_per_gas = Some(1);
616 assert!(tx_request.can_build());
617 }
618
619 #[test]
620 fn test_cannot_build_without_gas() {
621 let mut tx_request = TransactionRequest::default();
622 tx_request.set_gas_per_pubdata(U256::from(1000));
623 tx_request.base.nonce = Some(0);
624 tx_request.base.max_fee_per_gas = Some(100);
625 tx_request.base.max_priority_fee_per_gas = Some(1);
626 assert!(!tx_request.can_build());
627 }
628
629 #[test]
630 fn test_cannot_build_without_nonce() {
631 let mut tx_request = TransactionRequest::default();
632 tx_request.set_gas_per_pubdata(U256::from(1000));
633 tx_request.base.gas = Some(21000);
634 tx_request.base.max_fee_per_gas = Some(100);
635 tx_request.base.max_priority_fee_per_gas = Some(1);
636 assert!(!tx_request.can_build());
637 }
638
639 #[test]
640 fn test_cannot_build_without_max_fee_per_gas() {
641 let mut tx_request = TransactionRequest::default();
642 tx_request.set_gas_per_pubdata(U256::from(1000));
643 tx_request.base.gas = Some(21000);
644 tx_request.base.nonce = Some(0);
645 tx_request.base.max_priority_fee_per_gas = Some(1);
646 assert!(!tx_request.can_build());
647 }
648
649 #[test]
650 fn test_cannot_build_without_max_priority_fee_per_gas() {
651 let mut tx_request = TransactionRequest::default();
652 tx_request.set_gas_per_pubdata(U256::from(1000));
653 tx_request.base.gas = Some(21000);
654 tx_request.base.nonce = Some(0);
655 tx_request.base.max_fee_per_gas = Some(100);
656 assert!(!tx_request.can_build());
657 }
658
659 #[test]
660 fn test_output_tx_type_checked_with_eip712_meta() {
661 let mut tx_request = TransactionRequest::default();
662 tx_request.set_gas_per_pubdata(U256::from(1000));
663 tx_request.base.gas = Some(21000);
664 tx_request.base.nonce = Some(0);
665 tx_request.base.max_fee_per_gas = Some(100);
666 tx_request.base.max_priority_fee_per_gas = Some(1);
667 assert_eq!(tx_request.output_tx_type_checked(), Some(TxType::Eip712));
668 }
669
670 #[test]
671 fn test_output_tx_type_checked_without_eip712_meta() {
672 let mut tx_request = TransactionRequest::default();
673 tx_request.base.gas = Some(21000);
674 tx_request.base.nonce = Some(0);
675 tx_request.base.max_fee_per_gas = Some(100);
676 tx_request.base.max_priority_fee_per_gas = Some(1);
677 assert_eq!(tx_request.output_tx_type_checked(), None);
678 }
679
680 #[test]
681 fn test_output_tx_type_checked_cannot_build() {
682 let mut tx_request = TransactionRequest::default();
683 tx_request.set_gas_per_pubdata(U256::from(1000));
684 tx_request.base.gas = Some(21000);
685 tx_request.base.nonce = Some(0);
686 assert_eq!(tx_request.output_tx_type_checked(), None);
687 }
688
689 #[test]
690 fn test_build_unsigned_with_eip712_meta() {
691 let mut tx_request = TransactionRequest::default();
692 tx_request.set_gas_per_pubdata(U256::from(1000));
693 tx_request.base.gas = Some(21000);
694 tx_request.base.nonce = Some(0);
695 tx_request.base.max_fee_per_gas = Some(100);
696 tx_request.base.max_priority_fee_per_gas = Some(1);
697 tx_request.base.to = Some(TxKind::Call(CONTRACT_DEPLOYER_ADDRESS));
698 tx_request.base.chain_id = Some(1);
699 tx_request.base.from = Some(CONTRACT_DEPLOYER_ADDRESS);
700 let result = tx_request.build_unsigned();
701 assert!(result.is_ok());
702 if let Ok(crate::network::unsigned_tx::TypedTransaction::Eip712(tx)) = result {
703 assert_eq!(tx.chain_id, 1);
704 assert_eq!(tx.nonce, U256::from(0));
705 assert_eq!(tx.gas, 21000);
706 assert_eq!(tx.max_fee_per_gas, 100);
707 assert_eq!(tx.max_priority_fee_per_gas, 1);
708 assert_eq!(tx.from, CONTRACT_DEPLOYER_ADDRESS);
709 } else {
710 panic!("Expected Eip712 transaction");
711 }
712 }
713
714 #[test]
715 fn test_build_unsigned_without_eip712_meta() {
716 let mut tx_request = TransactionRequest::default();
717 tx_request.base.gas = Some(21000);
718 tx_request.base.nonce = Some(0);
719 tx_request.base.max_fee_per_gas = Some(100);
720 tx_request.base.max_priority_fee_per_gas = Some(1);
721 tx_request.base.to = Some(TxKind::Call(CONTRACT_DEPLOYER_ADDRESS));
722 tx_request.base.chain_id = Some(1);
723 tx_request.base.from = Some(CONTRACT_DEPLOYER_ADDRESS);
724 let result = tx_request.build_unsigned();
725 assert!(result.is_ok());
726 if let Ok(crate::network::unsigned_tx::TypedTransaction::Native(tx)) = result {
727 assert_eq!(tx.chain_id(), Some(1));
728 assert_eq!(tx.nonce(), 0);
729 assert_eq!(tx.gas_limit(), 21000);
730 assert_eq!(tx.max_fee_per_gas(), 100);
731 assert_eq!(tx.max_priority_fee_per_gas(), Some(1));
732 assert_eq!(tx.to(), Some(CONTRACT_DEPLOYER_ADDRESS));
733 } else {
734 panic!("Expected Native transaction");
735 }
736 }
737
738 #[test]
739 fn test_build_unsigned_missing_fields() {
740 let tx_request = TransactionRequest::default();
741 let result = tx_request.build_unsigned();
742 assert!(result.is_err());
743 }
744}