1use super::{hash_bytecode, Eip712TransactionRequest};
2use crate::zks_utils::{DEFAULT_GAS_PER_PUBDATA_LIMIT, EIP712_TX_TYPE};
3use ethers::{
4 abi::encode,
5 types::{
6 transaction::eip712::{
7 encode_data, encode_type, EIP712Domain, Eip712, Eip712DomainType, Eip712Error, Types,
8 },
9 Address, Bytes, U256,
10 },
11 utils::keccak256,
12};
13use serde::{Deserialize, Serialize};
14use serde_json::json;
15
16#[derive(Serialize, Deserialize, Debug, Clone)]
17#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
18pub struct Eip712Transaction {
19 pub tx_type: U256,
20 pub from: Address,
21 pub to: Address,
22 pub gas_limit: U256,
23 pub gas_per_pubdata_byte_limit: U256,
24 pub max_fee_per_gas: U256,
25 pub max_priority_fee_per_gas: U256,
26 pub paymaster: Address,
27 pub nonce: U256,
28 pub value: U256,
29 pub data: Bytes,
30 pub factory_deps: Vec<Bytes>,
31 pub paymaster_input: Bytes,
32 pub chain_id: U256,
33}
34
35impl Eip712Transaction {
36 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn tx_type<T>(mut self, tx_type: T) -> Self
42 where
43 T: Into<U256>,
44 {
45 self.tx_type = tx_type.into();
46 self
47 }
48
49 pub fn to<T>(mut self, to: T) -> Self
50 where
51 T: Into<Address>,
52 {
53 self.to = to.into();
54 self
55 }
56
57 pub fn from<T>(mut self, from: T) -> Self
58 where
59 T: Into<Address>,
60 {
61 self.from = from.into();
62 self
63 }
64
65 pub fn nonce<T>(mut self, nonce: T) -> Self
66 where
67 T: Into<U256>,
68 {
69 self.nonce = nonce.into();
70 self
71 }
72
73 pub fn gas_limit<T>(mut self, gas_limit: T) -> Self
74 where
75 T: Into<U256>,
76 {
77 self.gas_limit = gas_limit.into();
78 self
79 }
80
81 pub fn gas_per_pubdata_byte_limit<T>(mut self, gas_per_pubdata_byte_limit: T) -> Self
82 where
83 T: Into<U256>,
84 {
85 self.gas_per_pubdata_byte_limit = gas_per_pubdata_byte_limit.into();
86 self
87 }
88
89 pub fn max_fee_per_gas<T>(mut self, max_fee_per_gas: T) -> Self
90 where
91 T: Into<U256>,
92 {
93 self.max_fee_per_gas = max_fee_per_gas.into();
94 self
95 }
96
97 pub fn max_priority_fee_per_gas<T>(mut self, max_priority_fee_per_gas: T) -> Self
98 where
99 T: Into<U256>,
100 {
101 self.max_priority_fee_per_gas = max_priority_fee_per_gas.into();
102 self
103 }
104
105 pub fn paymaster<T>(mut self, paymaster: T) -> Self
106 where
107 T: Into<Address>,
108 {
109 self.paymaster = paymaster.into();
110 self
111 }
112
113 pub fn value<T>(mut self, value: T) -> Self
114 where
115 T: Into<U256>,
116 {
117 self.value = value.into();
118 self
119 }
120
121 pub fn data<T>(mut self, data: T) -> Self
122 where
123 T: Into<Bytes>,
124 {
125 self.data = data.into();
126 self
127 }
128
129 pub fn factory_deps<T>(mut self, factory_deps: T) -> Self
130 where
131 T: Into<Vec<Bytes>>,
132 {
133 self.factory_deps = factory_deps.into();
134 self
135 }
136
137 pub fn paymaster_input<T>(mut self, paymaster_input: T) -> Self
138 where
139 T: Into<Bytes>,
140 {
141 self.paymaster_input = paymaster_input.into();
142 self
143 }
144
145 pub fn chain_id<T>(mut self, chain_id: T) -> Self
146 where
147 T: Into<U256>,
148 {
149 self.chain_id = chain_id.into();
150 self
151 }
152}
153
154impl Default for Eip712Transaction {
155 fn default() -> Self {
156 Self {
157 tx_type: EIP712_TX_TYPE.into(),
158 from: Default::default(),
159 to: Default::default(),
160 gas_limit: Default::default(),
161 gas_per_pubdata_byte_limit: DEFAULT_GAS_PER_PUBDATA_LIMIT.into(),
162 max_fee_per_gas: Default::default(),
163 max_priority_fee_per_gas: Default::default(),
164 paymaster: Default::default(),
165 nonce: Default::default(),
166 value: Default::default(),
167 data: Default::default(),
168 factory_deps: <Vec<Bytes>>::default(),
169 paymaster_input: Default::default(),
170 chain_id: Default::default(),
171 }
172 }
173}
174
175pub fn eip712_transaction_types() -> Types {
177 let mut types = Types::new();
178
179 types.insert(
180 "Transaction".to_owned(),
181 vec![
182 Eip712DomainType {
183 name: "txType".to_owned(),
184 r#type: "uint256".to_owned(),
185 },
186 Eip712DomainType {
187 name: "from".to_owned(),
188 r#type: "uint256".to_owned(),
189 },
190 Eip712DomainType {
191 name: "to".to_owned(),
192 r#type: "uint256".to_owned(),
193 },
194 Eip712DomainType {
195 name: "gasLimit".to_owned(),
196 r#type: "uint256".to_owned(),
197 },
198 Eip712DomainType {
199 name: "gasPerPubdataByteLimit".to_owned(),
200 r#type: "uint256".to_owned(),
201 },
202 Eip712DomainType {
203 name: "maxFeePerGas".to_owned(),
204 r#type: "uint256".to_owned(),
205 },
206 Eip712DomainType {
207 name: "maxPriorityFeePerGas".to_owned(),
208 r#type: "uint256".to_owned(),
209 },
210 Eip712DomainType {
211 name: "paymaster".to_owned(),
212 r#type: "uint256".to_owned(),
213 },
214 Eip712DomainType {
215 name: "nonce".to_owned(),
216 r#type: "uint256".to_owned(),
217 },
218 Eip712DomainType {
219 name: "value".to_owned(),
220 r#type: "uint256".to_owned(),
221 },
222 Eip712DomainType {
223 name: "data".to_owned(),
224 r#type: "bytes".to_owned(),
225 },
226 Eip712DomainType {
227 name: "factoryDeps".to_owned(),
228 r#type: "bytes32[]".to_owned(),
229 },
230 Eip712DomainType {
231 name: "paymasterInput".to_owned(),
232 r#type: "bytes".to_owned(),
233 },
234 ],
235 );
236 types
237}
238
239impl Eip712 for Eip712Transaction {
240 type Error = Eip712Error;
241
242 fn domain(&self) -> Result<EIP712Domain, Self::Error> {
243 Ok(EIP712Domain {
244 name: Some(String::from("zkSync")),
245 version: Some(String::from("2")),
246 chain_id: Some(self.chain_id),
247 verifying_contract: None,
248 salt: None,
249 })
250 }
251
252 fn type_hash() -> Result<[u8; 32], Self::Error> {
253 Ok(keccak256(encode_type(
254 "Transaction",
255 &eip712_transaction_types(),
256 )?))
257 }
258
259 fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
260 let hash = keccak256(encode(&encode_data(
261 "Transaction",
262 &json!(self),
263 &eip712_transaction_types(),
264 )?));
265 Ok(hash)
266 }
267}
268
269impl TryFrom<Eip712TransactionRequest> for Eip712Transaction {
270 type Error = Eip712Error;
271
272 fn try_from(tx: Eip712TransactionRequest) -> Result<Self, Self::Error> {
273 let mut eip712_transaction = Eip712Transaction::default()
274 .tx_type(tx.r#type)
275 .from(tx.from)
276 .to(tx.to)
277 .max_priority_fee_per_gas(tx.max_priority_fee_per_gas)
278 .nonce(tx.nonce)
279 .value(tx.value)
280 .data(tx.data)
281 .factory_deps(
282 tx.custom_data
283 .factory_deps
284 .into_iter()
285 .map(|dependency_bytecode| {
286 hash_bytecode(&Bytes::from(dependency_bytecode)).map(Bytes::from)
287 })
288 .collect::<Result<Vec<Bytes>, _>>()?,
289 )
290 .chain_id(tx.chain_id);
291
292 if let Some(paymaster_params) = tx.custom_data.paymaster_params {
293 eip712_transaction = eip712_transaction
294 .paymaster(paymaster_params.paymaster)
295 .paymaster_input(paymaster_params.paymaster_input);
296 }
297
298 if let Some(gas_limit) = tx.gas_limit {
299 eip712_transaction = eip712_transaction.gas_limit(gas_limit)
300 } else {
301 return Err(Eip712Error::Message("gas_limit is missing".to_owned()));
302 }
303
304 if let Some(max_fee_per_gas) = tx.max_fee_per_gas {
305 eip712_transaction = eip712_transaction.max_fee_per_gas(max_fee_per_gas)
306 } else {
307 return Err(Eip712Error::Message(
308 "max_fee_per_gas is missing".to_owned(),
309 ));
310 }
311
312 Ok(eip712_transaction)
313 }
314}