1use crate::error::SignerError;
30use crate::ethereum::abi::{self, AbiValue};
31
32pub type Uint256 = [u8; 32];
34
35#[derive(Debug, Clone)]
42pub struct PackedUserOperation {
43 pub sender: [u8; 20],
45 pub nonce: [u8; 32],
47 pub init_code: Vec<u8>,
49 pub call_data: Vec<u8>,
51 pub account_gas_limits: [u8; 32],
53 pub pre_verification_gas: [u8; 32],
55 pub gas_fees: [u8; 32],
57 pub paymaster_and_data: Vec<u8>,
59 pub signature: Vec<u8>,
61}
62
63impl PackedUserOperation {
64 #[must_use]
68 pub fn pack(&self) -> Vec<u8> {
69 let mut buf = Vec::with_capacity(11 * 32);
70
71 buf.extend_from_slice(&pad_address(&self.sender));
74 buf.extend_from_slice(&self.nonce);
75 buf.extend_from_slice(&keccak256(&self.init_code));
76 buf.extend_from_slice(&keccak256(&self.call_data));
77 buf.extend_from_slice(&self.account_gas_limits);
78 buf.extend_from_slice(&self.pre_verification_gas);
79 buf.extend_from_slice(&self.gas_fees);
80 buf.extend_from_slice(&keccak256(&self.paymaster_and_data));
81
82 buf
83 }
84
85 #[must_use]
89 pub fn hash(&self, entry_point: &[u8; 20], chain_id: Uint256) -> [u8; 32] {
90 let packed_hash = keccak256(&self.pack());
91 let mut buf = Vec::with_capacity(3 * 32);
92 buf.extend_from_slice(&packed_hash);
93 buf.extend_from_slice(&pad_address(entry_point));
94 buf.extend_from_slice(&chain_id);
95 keccak256(&buf)
96 }
97
98 pub fn sign(
100 &self,
101 signer: &super::EthereumSigner,
102 entry_point: &[u8; 20],
103 chain_id: Uint256,
104 ) -> Result<super::EthereumSignature, SignerError> {
105 let hash = self.hash(entry_point, chain_id);
106 signer.sign_digest(&hash)
107 }
108
109 #[must_use]
113 pub fn pack_account_gas_limits(verification_gas_limit: u128, call_gas_limit: u128) -> [u8; 32] {
114 let mut buf = [0u8; 32];
115 buf[..16].copy_from_slice(&verification_gas_limit.to_be_bytes());
116 buf[16..].copy_from_slice(&call_gas_limit.to_be_bytes());
117 buf
118 }
119
120 #[must_use]
122 pub fn unpack_account_gas_limits(packed: &[u8; 32]) -> (u128, u128) {
123 let mut vgl = [0u8; 16];
124 let mut cgl = [0u8; 16];
125 vgl.copy_from_slice(&packed[..16]);
126 cgl.copy_from_slice(&packed[16..]);
127 (u128::from_be_bytes(vgl), u128::from_be_bytes(cgl))
128 }
129
130 #[must_use]
134 pub fn pack_gas_fees(max_priority_fee: u128, max_fee: u128) -> [u8; 32] {
135 let mut buf = [0u8; 32];
136 buf[..16].copy_from_slice(&max_priority_fee.to_be_bytes());
137 buf[16..].copy_from_slice(&max_fee.to_be_bytes());
138 buf
139 }
140
141 #[must_use]
143 pub fn unpack_gas_fees(packed: &[u8; 32]) -> (u128, u128) {
144 let mut mpf = [0u8; 16];
145 let mut mf = [0u8; 16];
146 mpf.copy_from_slice(&packed[..16]);
147 mf.copy_from_slice(&packed[16..]);
148 (u128::from_be_bytes(mpf), u128::from_be_bytes(mf))
149 }
150}
151
152pub const ENTRY_POINT_V07: [u8; 20] = [
158 0x00, 0x00, 0x00, 0x00, 0x71, 0x72, 0x7d, 0xe2, 0x2e, 0x5e, 0x9d, 0x8b, 0xaf, 0x0e, 0xda, 0xc6,
159 0xf3, 0x7d, 0xa0, 0x32,
160];
161
162#[must_use]
166pub fn encode_handle_ops(ops: &[PackedUserOperation], beneficiary: [u8; 20]) -> Vec<u8> {
167 let func = abi::Function::new(
168 "handleOps((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[],address)",
169 );
170
171 let op_tuples: Vec<AbiValue> = ops
172 .iter()
173 .map(|op| {
174 AbiValue::Tuple(vec![
175 AbiValue::Address(op.sender),
176 AbiValue::Uint256(op.nonce),
177 AbiValue::Bytes(op.init_code.clone()),
178 AbiValue::Bytes(op.call_data.clone()),
179 AbiValue::Uint256(op.account_gas_limits),
180 AbiValue::Uint256(op.pre_verification_gas),
181 AbiValue::Uint256(op.gas_fees),
182 AbiValue::Bytes(op.paymaster_and_data.clone()),
183 AbiValue::Bytes(op.signature.clone()),
184 ])
185 })
186 .collect();
187
188 func.encode(&[AbiValue::Array(op_tuples), AbiValue::Address(beneficiary)])
189}
190
191#[must_use]
197pub fn encode_paymaster_data(
198 paymaster: [u8; 20],
199 verification_gas_limit: u128,
200 post_op_gas_limit: u128,
201 data: &[u8],
202) -> Vec<u8> {
203 let mut buf = Vec::with_capacity(20 + 16 + 16 + data.len());
204 buf.extend_from_slice(&paymaster);
205 buf.extend_from_slice(&verification_gas_limit.to_be_bytes());
206 buf.extend_from_slice(&post_op_gas_limit.to_be_bytes());
207 buf.extend_from_slice(data);
208 buf
209}
210
211pub fn decode_paymaster_data(
215 encoded: &[u8],
216) -> Result<([u8; 20], u128, u128, Vec<u8>), SignerError> {
217 if encoded.len() < 52 {
218 return Err(SignerError::EncodingError(format!(
219 "paymasterAndData too short: {} bytes, need at least 52",
220 encoded.len()
221 )));
222 }
223 let mut paymaster = [0u8; 20];
224 paymaster.copy_from_slice(&encoded[..20]);
225
226 let mut vgl_bytes = [0u8; 16];
227 vgl_bytes.copy_from_slice(&encoded[20..36]);
228 let verification_gas_limit = u128::from_be_bytes(vgl_bytes);
229
230 let mut pogl_bytes = [0u8; 16];
231 pogl_bytes.copy_from_slice(&encoded[36..52]);
232 let post_op_gas_limit = u128::from_be_bytes(pogl_bytes);
233
234 let data = encoded[52..].to_vec();
235
236 Ok((paymaster, verification_gas_limit, post_op_gas_limit, data))
237}
238
239#[must_use]
245pub fn encode_execute(dest: [u8; 20], value: Uint256, func: &[u8]) -> Vec<u8> {
246 let f = abi::Function::new("execute(address,uint256,bytes)");
247 f.encode(&[
248 AbiValue::Address(dest),
249 AbiValue::Uint256(value),
250 AbiValue::Bytes(func.to_vec()),
251 ])
252}
253
254#[derive(Debug, Clone)]
256pub struct ExecuteCall {
257 pub target: [u8; 20],
259 pub value: Uint256,
261 pub data: Vec<u8>,
263}
264
265#[must_use]
269pub fn encode_execute_batch(calls: &[ExecuteCall]) -> Vec<u8> {
270 let f = abi::Function::new("executeBatch(address[],uint256[],bytes[])");
271 let dests: Vec<AbiValue> = calls.iter().map(|c| AbiValue::Address(c.target)).collect();
272 let values: Vec<AbiValue> = calls.iter().map(|c| AbiValue::Uint256(c.value)).collect();
273 let funcs: Vec<AbiValue> = calls
274 .iter()
275 .map(|c| AbiValue::Bytes(c.data.clone()))
276 .collect();
277 f.encode(&[
278 AbiValue::Array(dests),
279 AbiValue::Array(values),
280 AbiValue::Array(funcs),
281 ])
282}
283
284pub const ERC1271_MAGIC: [u8; 4] = [0x16, 0x26, 0xba, 0x7e];
290
291#[must_use]
295pub fn encode_is_valid_signature(hash: &[u8; 32], signature: &[u8]) -> Vec<u8> {
296 let f = abi::Function::new("isValidSignature(bytes32,bytes)");
297 f.encode(&[
298 AbiValue::Uint256(*hash),
299 AbiValue::Bytes(signature.to_vec()),
300 ])
301}
302
303#[must_use]
307pub fn is_valid_signature_magic(return_data: &[u8]) -> bool {
308 if return_data.len() < 32 {
309 return false;
310 }
311 return_data[..4] == [0u8; 4]
313 && return_data[4..28] == [0u8; 24]
314 && return_data[28..32] == ERC1271_MAGIC
315}
316
317#[must_use]
320pub fn is_valid_signature_magic_raw(return_data: &[u8]) -> bool {
321 if return_data.len() < 4 {
322 return false;
323 }
324 return_data[..4] == ERC1271_MAGIC
325}
326
327#[must_use]
333pub fn encode_create_account(owner: [u8; 20], salt: Uint256) -> Vec<u8> {
334 let f = abi::Function::new("createAccount(address,uint256)");
335 f.encode(&[AbiValue::Address(owner), AbiValue::Uint256(salt)])
336}
337
338#[must_use]
342pub fn encode_get_address(owner: [u8; 20], salt: Uint256) -> Vec<u8> {
343 let f = abi::Function::new("getAddress(address,uint256)");
344 f.encode(&[AbiValue::Address(owner), AbiValue::Uint256(salt)])
345}
346
347#[must_use]
351pub fn encode_get_nonce(sender: [u8; 20], key: Uint256) -> Vec<u8> {
352 let f = abi::Function::new("getNonce(address,uint192)");
353 f.encode(&[AbiValue::Address(sender), AbiValue::Uint256(key)])
354}
355
356fn keccak256(data: &[u8]) -> [u8; 32] {
359 super::keccak256(data)
360}
361
362fn pad_address(addr: &[u8; 20]) -> [u8; 32] {
363 let mut buf = [0u8; 32];
364 buf[12..32].copy_from_slice(addr);
365 buf
366}
367
368#[must_use]
370pub fn uint256_from_u64(value: u64) -> Uint256 {
371 let mut out = [0u8; 32];
372 out[24..].copy_from_slice(&value.to_be_bytes());
373 out
374}
375
376#[cfg(test)]
379#[allow(clippy::unwrap_used, clippy::expect_used)]
380mod tests {
381 use super::*;
382 use crate::traits::KeyPair;
383
384 fn sample_op() -> PackedUserOperation {
385 PackedUserOperation {
386 sender: [0xAA; 20],
387 nonce: [0u8; 32],
388 init_code: vec![],
389 call_data: vec![0x01, 0x02],
390 account_gas_limits: PackedUserOperation::pack_account_gas_limits(100_000, 200_000),
391 pre_verification_gas: {
392 let mut buf = [0u8; 32];
393 buf[24..32].copy_from_slice(&50_000u64.to_be_bytes());
394 buf
395 },
396 gas_fees: PackedUserOperation::pack_gas_fees(1_000_000_000, 2_000_000_000),
397 paymaster_and_data: vec![],
398 signature: vec![],
399 }
400 }
401
402 #[test]
405 fn test_pack_deterministic() {
406 let op = sample_op();
407 assert_eq!(op.pack(), op.pack());
408 }
409
410 #[test]
411 fn test_pack_length() {
412 let op = sample_op();
413 assert_eq!(op.pack().len(), 256);
415 }
416
417 #[test]
418 fn test_hash_deterministic() {
419 let op = sample_op();
420 let ep = [0xFF; 20];
421 assert_eq!(
422 op.hash(&ep, uint256_from_u64(1)),
423 op.hash(&ep, uint256_from_u64(1))
424 );
425 }
426
427 #[test]
428 fn test_hash_changes_with_chain_id() {
429 let op = sample_op();
430 let ep = [0xFF; 20];
431 assert_ne!(
432 op.hash(&ep, uint256_from_u64(1)),
433 op.hash(&ep, uint256_from_u64(137))
434 );
435 }
436
437 #[test]
438 fn test_hash_changes_with_entry_point() {
439 let op = sample_op();
440 assert_ne!(
441 op.hash(&[0xAA; 20], uint256_from_u64(1)),
442 op.hash(&[0xBB; 20], uint256_from_u64(1))
443 );
444 }
445
446 #[test]
447 fn test_entry_point_v07_canonical_value() {
448 assert_eq!(
449 ENTRY_POINT_V07,
450 [
451 0x00, 0x00, 0x00, 0x00, 0x71, 0x72, 0x7d, 0xe2, 0x2e, 0x5e, 0x9d, 0x8b, 0xaf, 0x0e,
452 0xda, 0xc6, 0xf3, 0x7d, 0xa0, 0x32,
453 ]
454 );
455 }
456
457 #[test]
458 fn test_hash_changes_with_calldata() {
459 let op1 = sample_op();
460 let mut op2 = sample_op();
461 op2.call_data = vec![0x03, 0x04];
462 assert_ne!(
463 op1.hash(&[0xFF; 20], uint256_from_u64(1)),
464 op2.hash(&[0xFF; 20], uint256_from_u64(1))
465 );
466 }
467
468 #[test]
469 fn test_hash_changes_with_sender() {
470 let op1 = sample_op();
471 let mut op2 = sample_op();
472 op2.sender = [0xBB; 20];
473 assert_ne!(
474 op1.hash(&[0xFF; 20], uint256_from_u64(1)),
475 op2.hash(&[0xFF; 20], uint256_from_u64(1))
476 );
477 }
478
479 #[test]
480 fn test_hash_changes_with_nonce() {
481 let op1 = sample_op();
482 let mut op2 = sample_op();
483 op2.nonce[31] = 1;
484 assert_ne!(
485 op1.hash(&[0xFF; 20], uint256_from_u64(1)),
486 op2.hash(&[0xFF; 20], uint256_from_u64(1))
487 );
488 }
489
490 #[test]
493 fn test_sign_produces_valid_signature() {
494 let signer = super::super::EthereumSigner::generate().unwrap();
495 let op = sample_op();
496 let sig = op.sign(&signer, &[0xFF; 20], uint256_from_u64(1)).unwrap();
497 assert!(sig.v == 27 || sig.v == 28);
498 assert_ne!(sig.r, [0u8; 32]);
499 }
500
501 #[test]
502 fn test_sign_recovers_correct_address() {
503 let signer = super::super::EthereumSigner::generate().unwrap();
504 let op = sample_op();
505 let sig = op.sign(&signer, &[0xFF; 20], uint256_from_u64(1)).unwrap();
506 let hash = op.hash(&[0xFF; 20], uint256_from_u64(1));
507 let recovered = super::super::ecrecover_digest(&hash, &sig).unwrap();
508 assert_eq!(recovered, signer.address());
509 }
510
511 #[test]
514 fn test_pack_account_gas_limits_roundtrip() {
515 let packed = PackedUserOperation::pack_account_gas_limits(100_000, 200_000);
516 let (vgl, cgl) = PackedUserOperation::unpack_account_gas_limits(&packed);
517 assert_eq!(vgl, 100_000);
518 assert_eq!(cgl, 200_000);
519 }
520
521 #[test]
522 fn test_pack_gas_fees_roundtrip() {
523 let packed = PackedUserOperation::pack_gas_fees(1_000_000_000, 2_000_000_000);
524 let (mpf, mf) = PackedUserOperation::unpack_gas_fees(&packed);
525 assert_eq!(mpf, 1_000_000_000);
526 assert_eq!(mf, 2_000_000_000);
527 }
528
529 #[test]
530 fn test_pack_gas_limits_zero() {
531 let packed = PackedUserOperation::pack_account_gas_limits(0, 0);
532 assert_eq!(packed, [0u8; 32]);
533 let (vgl, cgl) = PackedUserOperation::unpack_account_gas_limits(&packed);
534 assert_eq!(vgl, 0);
535 assert_eq!(cgl, 0);
536 }
537
538 #[test]
539 fn test_pack_gas_fees_max() {
540 let max = u128::MAX;
541 let packed = PackedUserOperation::pack_gas_fees(max, max);
542 let (mpf, mf) = PackedUserOperation::unpack_gas_fees(&packed);
543 assert_eq!(mpf, max);
544 assert_eq!(mf, max);
545 }
546
547 #[test]
550 fn test_encode_handle_ops_selector() {
551 let ops = vec![sample_op()];
552 let calldata = encode_handle_ops(&ops, [0xFF; 20]);
553 let expected = abi::function_selector(
554 "handleOps((address,uint256,bytes,bytes,bytes32,uint256,bytes32,bytes,bytes)[],address)",
555 );
556 assert_eq!(&calldata[..4], &expected);
557 }
558
559 #[test]
560 fn test_encode_handle_ops_empty() {
561 let calldata = encode_handle_ops(&[], [0xFF; 20]);
562 assert!(calldata.len() > 4);
563 }
564
565 #[test]
568 fn test_paymaster_data_roundtrip() {
569 let paymaster = [0xAA; 20];
570 let vgl = 100_000u128;
571 let pogl = 50_000u128;
572 let data = vec![0xDE, 0xAD];
573
574 let encoded = encode_paymaster_data(paymaster, vgl, pogl, &data);
575 let (dec_pm, dec_vgl, dec_pogl, dec_data) = decode_paymaster_data(&encoded).unwrap();
576
577 assert_eq!(dec_pm, paymaster);
578 assert_eq!(dec_vgl, vgl);
579 assert_eq!(dec_pogl, pogl);
580 assert_eq!(dec_data, data);
581 }
582
583 #[test]
584 fn test_paymaster_data_empty_extra_data() {
585 let encoded = encode_paymaster_data([0xAA; 20], 100, 200, &[]);
586 let (_, _, _, data) = decode_paymaster_data(&encoded).unwrap();
587 assert!(data.is_empty());
588 }
589
590 #[test]
591 fn test_paymaster_data_too_short() {
592 assert!(decode_paymaster_data(&[0u8; 51]).is_err());
593 assert!(decode_paymaster_data(&[]).is_err());
594 }
595
596 #[test]
597 fn test_paymaster_data_minimum_length() {
598 let encoded = encode_paymaster_data([0xBB; 20], 0, 0, &[]);
599 assert_eq!(encoded.len(), 52);
600 assert!(decode_paymaster_data(&encoded).is_ok());
601 }
602
603 #[test]
606 fn test_encode_execute_selector() {
607 let calldata = encode_execute([0xBB; 20], uint256_from_u64(0), &[]);
608 let expected = abi::function_selector("execute(address,uint256,bytes)");
609 assert_eq!(&calldata[..4], &expected);
610 }
611
612 #[test]
613 fn test_encode_execute_with_value() {
614 let calldata = encode_execute([0xBB; 20], uint256_from_u64(1_000_000), &[0xDE, 0xAD]);
615 assert!(calldata.len() > 4 + 3 * 32);
616 }
617
618 #[test]
619 fn test_encode_execute_batch_selector() {
620 let calls = vec![ExecuteCall {
621 target: [0xAA; 20],
622 value: uint256_from_u64(0),
623 data: vec![],
624 }];
625 let calldata = encode_execute_batch(&calls);
626 let expected = abi::function_selector("executeBatch(address[],uint256[],bytes[])");
627 assert_eq!(&calldata[..4], &expected);
628 }
629
630 #[test]
631 fn test_encode_execute_batch_multiple() {
632 let calls = vec![
633 ExecuteCall {
634 target: [0xAA; 20],
635 value: uint256_from_u64(100),
636 data: vec![0x01],
637 },
638 ExecuteCall {
639 target: [0xBB; 20],
640 value: uint256_from_u64(200),
641 data: vec![0x02],
642 },
643 ];
644 let calldata = encode_execute_batch(&calls);
645 assert!(calldata.len() > 4);
646 }
647
648 #[test]
649 fn test_encode_execute_batch_empty() {
650 let calldata = encode_execute_batch(&[]);
651 let expected = abi::function_selector("executeBatch(address[],uint256[],bytes[])");
652 assert_eq!(&calldata[..4], &expected);
653 }
654
655 #[test]
658 fn test_erc1271_magic_value() {
659 assert_eq!(ERC1271_MAGIC, [0x16, 0x26, 0xba, 0x7e]);
660 }
661
662 #[test]
663 fn test_encode_is_valid_signature_selector() {
664 let calldata = encode_is_valid_signature(&[0xAA; 32], &[0xBB; 65]);
665 let expected = abi::function_selector("isValidSignature(bytes32,bytes)");
666 assert_eq!(&calldata[..4], &expected);
667 }
668
669 #[test]
670 fn test_is_valid_signature_magic_true() {
671 let mut result = [0u8; 32];
673 result[28] = 0x16;
674 result[29] = 0x26;
675 result[30] = 0xba;
676 result[31] = 0x7e;
677 assert!(is_valid_signature_magic(&result));
678 }
679
680 #[test]
681 fn test_is_valid_signature_magic_false() {
682 let result = [0u8; 32]; assert!(!is_valid_signature_magic(&result));
684 }
685
686 #[test]
687 fn test_is_valid_signature_magic_too_short() {
688 assert!(!is_valid_signature_magic(&[0u8; 31]));
689 }
690
691 #[test]
692 fn test_is_valid_signature_magic_raw_true() {
693 assert!(is_valid_signature_magic_raw(&[0x16, 0x26, 0xba, 0x7e]));
694 }
695
696 #[test]
697 fn test_is_valid_signature_magic_raw_false() {
698 assert!(!is_valid_signature_magic_raw(&[0x00, 0x00, 0x00, 0x00]));
699 }
700
701 #[test]
702 fn test_is_valid_signature_magic_raw_too_short() {
703 assert!(!is_valid_signature_magic_raw(&[0x16, 0x26, 0xba]));
704 }
705
706 #[test]
709 fn test_encode_create_account_selector() {
710 let calldata = encode_create_account([0xAA; 20], uint256_from_u64(0));
711 let expected = abi::function_selector("createAccount(address,uint256)");
712 assert_eq!(&calldata[..4], &expected);
713 }
714
715 #[test]
716 fn test_encode_get_address_selector() {
717 let calldata = encode_get_address([0xAA; 20], uint256_from_u64(0));
718 let expected = abi::function_selector("getAddress(address,uint256)");
719 assert_eq!(&calldata[..4], &expected);
720 }
721
722 #[test]
725 fn test_encode_get_nonce_selector() {
726 let calldata = encode_get_nonce([0xAA; 20], uint256_from_u64(0));
727 let expected = abi::function_selector("getNonce(address,uint192)");
728 assert_eq!(&calldata[..4], &expected);
729 }
730
731 #[test]
734 fn test_pad_address() {
735 let addr = [0xAA; 20];
736 let padded = pad_address(&addr);
737 assert_eq!(&padded[..12], &[0u8; 12]);
738 assert_eq!(&padded[12..], &[0xAA; 20]);
739 }
740
741 #[test]
742 fn test_uint256_from_u64() {
743 let padded = uint256_from_u64(256);
744 assert_eq!(&padded[..24], &[0u8; 24]);
745 assert_eq!(&padded[24..], &256u64.to_be_bytes());
746 }
747
748 #[test]
751 fn test_hash_changes_with_init_code() {
752 let op1 = sample_op();
753 let mut op2 = sample_op();
754 op2.init_code = vec![0xFF; 20];
755 assert_ne!(
756 op1.hash(&[0xFF; 20], uint256_from_u64(1)),
757 op2.hash(&[0xFF; 20], uint256_from_u64(1))
758 );
759 }
760
761 #[test]
762 fn test_hash_changes_with_paymaster_data() {
763 let op1 = sample_op();
764 let mut op2 = sample_op();
765 op2.paymaster_and_data = vec![0xAA; 52];
766 assert_ne!(
767 op1.hash(&[0xFF; 20], uint256_from_u64(1)),
768 op2.hash(&[0xFF; 20], uint256_from_u64(1))
769 );
770 }
771
772 #[test]
773 fn test_hash_changes_with_gas_limits() {
774 let op1 = sample_op();
775 let mut op2 = sample_op();
776 op2.account_gas_limits = PackedUserOperation::pack_account_gas_limits(999, 888);
777 assert_ne!(
778 op1.hash(&[0xFF; 20], uint256_from_u64(1)),
779 op2.hash(&[0xFF; 20], uint256_from_u64(1))
780 );
781 }
782
783 #[test]
784 fn test_hash_changes_with_gas_fees() {
785 let op1 = sample_op();
786 let mut op2 = sample_op();
787 op2.gas_fees = PackedUserOperation::pack_gas_fees(999, 888);
788 assert_ne!(
789 op1.hash(&[0xFF; 20], uint256_from_u64(1)),
790 op2.hash(&[0xFF; 20], uint256_from_u64(1))
791 );
792 }
793}