1use crate::ethereum::abi::{self, AbiValue};
20use crate::ethereum::keccak256;
21
22pub type Uint256 = [u8; 32];
24
25pub const ENTRY_POINT_V06: [u8; 20] = [
31 0x5f, 0xf1, 0x37, 0xd4, 0xb0, 0xfd, 0xcd, 0x49, 0xdc, 0xa3, 0x0c, 0x7c, 0xf5, 0x7e, 0x57, 0x8a,
32 0x02, 0x6d, 0x27, 0x89,
33];
34
35pub const ENTRY_POINT_V07: [u8; 20] = [
37 0x00, 0x00, 0x00, 0x00, 0x71, 0x72, 0x7d, 0xe2, 0x2e, 0x5e, 0x9d, 0x8b, 0xaf, 0x0e, 0xda, 0xc6,
38 0xf3, 0x7d, 0xa0, 0x32,
39];
40
41#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct UserOperation {
48 pub sender: [u8; 20],
50 pub nonce: Uint256,
52 pub init_code: Vec<u8>,
54 pub call_data: Vec<u8>,
56 pub call_gas_limit: Uint256,
58 pub verification_gas_limit: Uint256,
60 pub pre_verification_gas: Uint256,
62 pub max_fee_per_gas: Uint256,
64 pub max_priority_fee_per_gas: Uint256,
66 pub paymaster_and_data: Vec<u8>,
68 pub signature: Vec<u8>,
70}
71
72impl UserOperation {
73 #[must_use]
75 pub fn new(sender: [u8; 20]) -> Self {
76 Self {
77 sender,
78 nonce: uint256_from_u64(0),
79 init_code: Vec::new(),
80 call_data: Vec::new(),
81 call_gas_limit: uint256_from_u64(100_000),
82 verification_gas_limit: uint256_from_u64(100_000),
83 pre_verification_gas: uint256_from_u64(21_000),
84 max_fee_per_gas: uint256_from_u64(1_000_000_000), max_priority_fee_per_gas: uint256_from_u64(1_000_000_000),
86 paymaster_and_data: Vec::new(),
87 signature: Vec::new(),
88 }
89 }
90
91 #[must_use]
97 pub fn hash(&self, entry_point: &[u8; 20], chain_id: Uint256) -> [u8; 32] {
98 let packed_hash = self.pack_hash();
100
101 let mut entry_point_padded = [0u8; 32];
103 entry_point_padded[12..32].copy_from_slice(entry_point);
104
105 let encoded = abi::encode(&[
106 AbiValue::Uint256(packed_hash),
107 AbiValue::Uint256(entry_point_padded),
108 AbiValue::Uint256(chain_id),
109 ]);
110
111 keccak256(&encoded)
112 }
113
114 fn pack_hash(&self) -> [u8; 32] {
116 let mut sender_padded = [0u8; 32];
117 sender_padded[12..32].copy_from_slice(&self.sender);
118
119 let values = vec![
120 AbiValue::Uint256(sender_padded),
121 AbiValue::Uint256(self.nonce),
122 AbiValue::Uint256(keccak256(&self.init_code)),
123 AbiValue::Uint256(keccak256(&self.call_data)),
124 AbiValue::Uint256(self.call_gas_limit),
125 AbiValue::Uint256(self.verification_gas_limit),
126 AbiValue::Uint256(self.pre_verification_gas),
127 AbiValue::Uint256(self.max_fee_per_gas),
128 AbiValue::Uint256(self.max_priority_fee_per_gas),
129 AbiValue::Uint256(keccak256(&self.paymaster_and_data)),
130 ];
131
132 keccak256(&abi::encode(&values))
133 }
134
135 #[must_use]
139 pub fn encode(&self) -> Vec<u8> {
140 let mut sender_padded = [0u8; 32];
141 sender_padded[12..32].copy_from_slice(&self.sender);
142
143 abi::encode(&[
144 AbiValue::Uint256(sender_padded),
145 AbiValue::Uint256(self.nonce),
146 AbiValue::Bytes(self.init_code.clone()),
147 AbiValue::Bytes(self.call_data.clone()),
148 AbiValue::Uint256(self.call_gas_limit),
149 AbiValue::Uint256(self.verification_gas_limit),
150 AbiValue::Uint256(self.pre_verification_gas),
151 AbiValue::Uint256(self.max_fee_per_gas),
152 AbiValue::Uint256(self.max_priority_fee_per_gas),
153 AbiValue::Bytes(self.paymaster_and_data.clone()),
154 AbiValue::Bytes(self.signature.clone()),
155 ])
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
169pub struct PackedUserOperation {
170 pub sender: [u8; 20],
172 pub nonce: [u8; 32],
174 pub init_code: Vec<u8>,
176 pub call_data: Vec<u8>,
178 pub account_gas_limits: [u8; 32],
180 pub pre_verification_gas: Uint256,
182 pub gas_fees: [u8; 32],
184 pub paymaster_and_data: Vec<u8>,
186 pub signature: Vec<u8>,
188}
189
190#[must_use]
194pub fn pack_gas(high: u128, low: u128) -> [u8; 32] {
195 let mut result = [0u8; 32];
196 result[..16].copy_from_slice(&high.to_be_bytes());
197 result[16..].copy_from_slice(&low.to_be_bytes());
198 result
199}
200
201#[must_use]
203pub fn pack_account_gas_limits(verification_gas: u128, call_gas: u128) -> [u8; 32] {
204 pack_gas(verification_gas, call_gas)
205}
206
207#[must_use]
209pub fn pack_gas_fees(max_priority_fee: u128, max_fee: u128) -> [u8; 32] {
210 pack_gas(max_priority_fee, max_fee)
211}
212
213#[must_use]
221pub fn encode_execute(dest: &[u8; 20], value: Uint256, func: &[u8]) -> Vec<u8> {
222 let execute = abi::Function::new("execute(address,uint256,bytes)");
223 execute.encode(&[
224 AbiValue::Address(*dest),
225 AbiValue::Uint256(value),
226 AbiValue::Bytes(func.to_vec()),
227 ])
228}
229
230#[must_use]
234pub fn encode_execute_batch(targets: &[[u8; 20]], values: &[Uint256], data: &[Vec<u8>]) -> Vec<u8> {
235 let batch = abi::Function::new("executeBatch(address[],uint256[],bytes[])");
236
237 let targets_abi: Vec<AbiValue> = targets.iter().map(|t| AbiValue::Address(*t)).collect();
238 let values_abi: Vec<AbiValue> = values.iter().map(|v| AbiValue::Uint256(*v)).collect();
239 let data_abi: Vec<AbiValue> = data.iter().map(|d| AbiValue::Bytes(d.clone())).collect();
240
241 batch.encode(&[
242 AbiValue::Array(targets_abi),
243 AbiValue::Array(values_abi),
244 AbiValue::Array(data_abi),
245 ])
246}
247
248#[must_use]
250pub fn encode_erc20_approve(spender: &[u8; 20], amount: Uint256) -> Vec<u8> {
251 let approve = abi::Function::new("approve(address,uint256)");
252 approve.encode(&[AbiValue::Address(*spender), AbiValue::Uint256(amount)])
253}
254
255#[must_use]
257pub fn encode_erc20_transfer(to: &[u8; 20], amount: Uint256) -> Vec<u8> {
258 let transfer = abi::Function::new("transfer(address,uint256)");
259 transfer.encode(&[AbiValue::Address(*to), AbiValue::Uint256(amount)])
260}
261
262#[must_use]
264pub fn uint256_from_u64(value: u64) -> Uint256 {
265 let mut out = [0u8; 32];
266 out[24..].copy_from_slice(&value.to_be_bytes());
267 out
268}
269
270#[cfg(test)]
275#[allow(clippy::unwrap_used, clippy::expect_used)]
276mod tests {
277 use super::*;
278
279 const SENDER: [u8; 20] = [0xAA; 20];
280 const DEST: [u8; 20] = [0xBB; 20];
281
282 #[test]
283 fn test_new_user_op_defaults() {
284 let op = UserOperation::new(SENDER);
285 assert_eq!(op.sender, SENDER);
286 assert_eq!(op.nonce, uint256_from_u64(0));
287 assert!(op.init_code.is_empty());
288 assert!(op.call_data.is_empty());
289 assert!(op.paymaster_and_data.is_empty());
290 assert!(op.signature.is_empty());
291 }
292
293 #[test]
294 fn test_user_op_hash_deterministic() {
295 let op = UserOperation::new(SENDER);
296 let h1 = op.hash(&ENTRY_POINT_V06, uint256_from_u64(1));
297 let h2 = op.hash(&ENTRY_POINT_V06, uint256_from_u64(1));
298 assert_eq!(h1, h2);
299 }
300
301 #[test]
302 fn test_user_op_hash_different_chain_ids() {
303 let op = UserOperation::new(SENDER);
304 let h1 = op.hash(&ENTRY_POINT_V06, uint256_from_u64(1));
305 let h2 = op.hash(&ENTRY_POINT_V06, uint256_from_u64(137));
306 assert_ne!(h1, h2, "different chains should produce different hashes");
307 }
308
309 #[test]
310 fn test_user_op_hash_different_nonces() {
311 let mut op1 = UserOperation::new(SENDER);
312 let mut op2 = UserOperation::new(SENDER);
313 op1.nonce = uint256_from_u64(0);
314 op2.nonce = uint256_from_u64(1);
315 assert_ne!(
316 op1.hash(&ENTRY_POINT_V06, uint256_from_u64(1)),
317 op2.hash(&ENTRY_POINT_V06, uint256_from_u64(1)),
318 );
319 }
320
321 #[test]
322 fn test_user_op_encode_changes_with_data() {
323 let op1 = UserOperation::new(SENDER);
324 let mut op2 = UserOperation::new(SENDER);
325 op2.call_data = vec![0xDE, 0xAD];
326 assert_ne!(op1.encode(), op2.encode());
327 }
328
329 #[test]
330 fn test_encode_execute_selector() {
331 let data = encode_execute(&DEST, uint256_from_u64(0), &[]);
332 let expected = abi::function_selector("execute(address,uint256,bytes)");
333 assert_eq!(&data[..4], &expected);
334 }
335
336 #[test]
337 fn test_encode_execute_batch_selector() {
338 let data = encode_execute_batch(&[DEST], &[uint256_from_u64(0)], &[vec![]]);
339 let expected = abi::function_selector("executeBatch(address[],uint256[],bytes[])");
340 assert_eq!(&data[..4], &expected);
341 }
342
343 #[test]
344 fn test_encode_erc20_approve_selector() {
345 let data = encode_erc20_approve(&DEST, uint256_from_u64(1000));
346 let expected = abi::function_selector("approve(address,uint256)");
347 assert_eq!(&data[..4], &expected);
348 }
349
350 #[test]
351 fn test_encode_erc20_transfer_selector() {
352 let data = encode_erc20_transfer(&DEST, uint256_from_u64(500));
353 let expected = abi::function_selector("transfer(address,uint256)");
354 assert_eq!(&data[..4], &expected);
355 }
356
357 #[test]
358 fn test_pack_gas_basic() {
359 let packed = pack_gas(100, 200);
360 let high = u128::from_be_bytes(packed[..16].try_into().unwrap());
361 let low = u128::from_be_bytes(packed[16..].try_into().unwrap());
362 assert_eq!(high, 100);
363 assert_eq!(low, 200);
364 }
365
366 #[test]
367 fn test_entry_point_addresses_different() {
368 assert_ne!(ENTRY_POINT_V06, ENTRY_POINT_V07);
369 }
370
371 #[test]
372 fn test_e2e_user_op_with_execute() {
373 let mut op = UserOperation::new(SENDER);
374 op.nonce = uint256_from_u64(1);
375 op.call_data = encode_execute(&DEST, uint256_from_u64(1_000_000), &[]);
376 op.call_gas_limit = uint256_from_u64(200_000);
377 let hash = op.hash(&ENTRY_POINT_V06, uint256_from_u64(1));
378 assert_ne!(hash, [0u8; 32]);
379 }
380
381 #[test]
382 fn test_e2e_user_op_with_batch() {
383 let mut op = UserOperation::new(SENDER);
384 op.call_data = encode_execute_batch(
385 &[DEST, [0xCC; 20]],
386 &[uint256_from_u64(100), uint256_from_u64(200)],
387 &[
388 encode_erc20_transfer(&[0xDD; 20], uint256_from_u64(500)),
389 vec![],
390 ],
391 );
392 let hash = op.hash(&ENTRY_POINT_V06, uint256_from_u64(1));
393 assert_ne!(hash, [0u8; 32]);
394 }
395}