1use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};
2
3use super::{hash_bytecode, rlp_append_option, Eip712Meta};
4use crate::{
5 zks_utils::{
6 self, CONTRACT_DEPLOYER_ADDR, EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS,
7 },
8 zks_wallet::{DeployRequest, Overrides, TransferRequest, WithdrawRequest, ZKRequestError},
9};
10use ethers::{
11 abi::{Abi, HumanReadableParser, ParseError},
12 types::{
13 transaction::{eip2930::AccessList, eip712::Eip712Error},
14 Address, Bytes, Signature, U256,
15 },
16 utils::rlp::{Encodable, RlpStream},
17};
18use ethers_contract::encode_function_data;
19use serde::{Deserialize, Serialize};
20
21#[derive(Serialize, Deserialize, Clone, Debug)]
23#[serde(rename_all(serialize = "camelCase", deserialize = "camelCase"))]
24pub struct Eip712TransactionRequest {
25 pub to: Address,
27 pub from: Address,
28 pub nonce: U256,
29 pub gas: U256,
30 pub gas_price: U256,
31 pub data: Bytes,
32 pub value: U256,
33 pub chain_id: U256,
34 pub r#type: U256,
35 pub max_priority_fee_per_gas: U256,
36 #[serde(rename = "eip712Meta")]
37 pub custom_data: Eip712Meta,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub access_list: Option<AccessList>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
44 pub gas_limit: Option<U256>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub max_fee_per_gas: Option<U256>, pub ccip_read_enabled: bool,
49}
50
51impl Eip712TransactionRequest {
52 pub fn new() -> Self {
53 Self::default()
54 }
55
56 pub fn from_overrides(overrides: Overrides) -> Self {
57 let mut tx = Self::default();
58 if let Some(value) = overrides.value {
59 tx.value = value;
60 }
61 tx
62 }
63
64 pub fn to<T>(mut self, to: T) -> Self
65 where
66 T: Into<Address>,
67 {
68 self.to = to.into();
69 self
70 }
71
72 pub fn from<T>(mut self, from: T) -> Self
73 where
74 T: Into<Address>,
75 {
76 self.from = from.into();
77 self
78 }
79
80 pub fn nonce<T>(mut self, nonce: T) -> Self
81 where
82 T: Into<U256>,
83 {
84 self.nonce = nonce.into();
85 self
86 }
87
88 pub fn gas_limit<T>(mut self, gas_limit: T) -> Self
89 where
90 T: Into<U256>,
91 {
92 self.gas_limit = Some(gas_limit.into());
93 self
94 }
95
96 pub fn gas_price<T>(mut self, gas_price: T) -> Self
97 where
98 T: Into<U256>,
99 {
100 self.gas_price = gas_price.into();
101 self
102 }
103
104 pub fn data<T>(mut self, data: T) -> Self
105 where
106 T: Into<Bytes>,
107 {
108 self.data = data.into();
109 self
110 }
111
112 pub fn value<T>(mut self, value: T) -> Self
113 where
114 T: Into<U256>,
115 {
116 self.value = value.into();
117 self
118 }
119
120 pub fn chain_id<T>(mut self, chain_id: T) -> Self
121 where
122 T: Into<U256>,
123 {
124 self.chain_id = chain_id.into();
125 self
126 }
127
128 pub fn r#type<T>(mut self, r#type: T) -> Self
129 where
130 T: Into<U256>,
131 {
132 self.r#type = r#type.into();
133 self
134 }
135
136 pub fn access_list<T>(mut self, access_list: AccessList) -> Self {
137 self.access_list = Some(access_list);
138 self
139 }
140
141 pub fn max_priority_fee_per_gas<T>(mut self, max_priority_fee_per_gas: T) -> Self
142 where
143 T: Into<U256>,
144 {
145 self.max_priority_fee_per_gas = max_priority_fee_per_gas.into();
146 self
147 }
148
149 pub fn max_fee_per_gas<T>(mut self, max_fee_per_gas: T) -> Self
150 where
151 T: Into<U256>,
152 {
153 self.max_fee_per_gas = Some(max_fee_per_gas.into());
154 self
155 }
156
157 pub fn custom_data(mut self, custom_data: Eip712Meta) -> Self {
158 self.custom_data = custom_data;
159 self
160 }
161
162 pub fn ccip_read_enabled(mut self, ccip_read_enabled: bool) -> Self {
163 self.ccip_read_enabled = ccip_read_enabled;
164 self
165 }
166
167 pub fn rlp_unsigned(&self) -> Result<Bytes, Eip712Error> {
168 self.rlp(None)
169 }
170
171 pub fn rlp_signed(&self, signature: Signature) -> Result<Bytes, Eip712Error> {
172 self.rlp(Some(signature))
173 }
174
175 pub fn rlp(&self, signature: Option<Signature>) -> Result<Bytes, Eip712Error> {
176 let mut rlp = RlpStream::new();
177 rlp.begin_unbounded_list();
178 rlp.append(&self.nonce);
179 rlp.append(&self.max_priority_fee_per_gas);
180 rlp.append(&self.gas_price);
181 rlp_append_option(&mut rlp, self.gas_limit);
182 rlp.append(&self.to);
183 rlp.append(&self.value);
184 rlp.append(&self.data.0);
185 if let Some(sig) = signature {
186 rlp.append(&sig.v);
187 let mut bytes = [0_u8; 32]; sig.r.to_big_endian(&mut bytes);
190 rlp.append(&bytes.as_slice());
191 sig.s.to_big_endian(&mut bytes);
192 rlp.append(&bytes.as_slice());
193 }
194 rlp.append(&self.chain_id);
195 rlp.append(&self.from);
196 self.custom_data.rlp_append(&mut rlp);
197 rlp.finalize_unbounded_list();
198 Ok(rlp.out().freeze().into())
199 }
200}
201
202impl Default for Eip712TransactionRequest {
203 fn default() -> Self {
204 Self {
205 to: Default::default(),
206 from: Default::default(),
207 nonce: Default::default(),
208 gas: Default::default(),
209 gas_limit: Default::default(),
210 gas_price: Default::default(),
211 data: Default::default(),
212 value: Default::default(),
213 chain_id: ERA_CHAIN_ID.into(),
214 r#type: EIP712_TX_TYPE.into(),
215 access_list: Default::default(),
216 max_priority_fee_per_gas: MAX_PRIORITY_FEE_PER_GAS.into(),
217 max_fee_per_gas: Default::default(),
218 custom_data: Default::default(),
219 ccip_read_enabled: Default::default(),
220 }
221 }
222}
223
224impl TryFrom<WithdrawRequest> for Eip712TransactionRequest {
225 type Error = ZKRequestError;
226
227 fn try_from(request: WithdrawRequest) -> Result<Self, Self::Error> {
228 let contract_address =
229 Address::from_str(zks_utils::CONTRACTS_L2_ETH_TOKEN_ADDR).map_err(|e| {
230 ZKRequestError::CustomError(format!("Error getting L2 ETH token address {e:?}"))
231 })?;
232 let function_signature = "function withdraw(address _l1Receiver) external payable override";
233 let function = HumanReadableParser::parse_function(function_signature)
234 .map_err(ParseError::LexerError)?;
235 let function_args = function.decode_input(&zks_utils::encode_args(
236 &function,
237 &[format!("{:?}", request.to)],
238 )?)?;
239 let data: Bytes = function.encode_input(&function_args)?.into();
240
241 Ok(Eip712TransactionRequest::new()
242 .r#type(EIP712_TX_TYPE)
243 .to(contract_address)
244 .value(request.amount)
245 .from(request.from)
246 .data(data))
247 }
248}
249
250impl From<TransferRequest> for Eip712TransactionRequest {
251 fn from(request: TransferRequest) -> Self {
252 Eip712TransactionRequest::new()
253 .r#type(EIP712_TX_TYPE)
254 .to(request.to)
255 .value(request.amount)
256 .from(request.from)
257 }
258}
259
260impl TryFrom<DeployRequest> for Eip712TransactionRequest {
261 type Error = ZKRequestError;
262
263 fn try_from(request: DeployRequest) -> Result<Self, Self::Error> {
264 let mut contract_deployer_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
265 contract_deployer_path.push("src/abi/ContractDeployer.json");
266
267 let custom_data = Eip712Meta::new().factory_deps({
268 let mut factory_deps = Vec::new();
269 if let Some(factory_dependencies) = request.factory_deps {
270 factory_deps.extend(factory_dependencies);
271 }
272 factory_deps.push(request.contract_bytecode.clone());
273 factory_deps
274 });
275
276 let contract_deployer = Abi::load(BufReader::new(
277 File::open(contract_deployer_path).map_err(|e| {
278 ZKRequestError::CustomError(format!(
279 "Error opening contract deployer abi file {e:?}"
280 ))
281 })?,
282 ))?;
283 let create = contract_deployer.function("create")?;
284
285 let salt = [0_u8; 32];
287 let bytecode_hash = hash_bytecode(&request.contract_bytecode).map_err(|e| {
288 ZKRequestError::CustomError(format!("Error hashing contract bytecode {e:?}"))
289 })?;
290 let call_data: Bytes = match (
291 request.contract_abi.constructor(),
292 request.constructor_parameters.is_empty(),
293 ) {
294 (None, false) => {
295 return Err(ZKRequestError::CustomError(
296 "Constructor not present".to_owned(),
297 ))
298 }
299 (None, true) | (Some(_), true) => Bytes::default(),
300 (Some(constructor), false) => {
301 zks_utils::encode_constructor_args(constructor, &request.constructor_parameters)?
302 .into()
303 }
304 };
305
306 let data = encode_function_data(create, (salt, bytecode_hash, call_data))?;
307
308 let contract_deployer_address = Address::from_str(CONTRACT_DEPLOYER_ADDR).map_err(|e| {
309 ZKRequestError::CustomError(format!("Error getting contract deployer address {e:?}"))
310 })?;
311 Ok(Eip712TransactionRequest::new()
312 .r#type(EIP712_TX_TYPE)
313 .to(contract_deployer_address)
314 .custom_data(custom_data)
315 .data(data))
316 }
317}