zksync_web3_rs/eip712/
transaction.rs

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    // Check if this is necessary or if we can always use the default.
37    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
175// FIXME: Cleanup this.
176pub 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}