use alloy_primitives::{Address, Bytes, U256, address};
use alloy_sol_types::{SolCall, sol};
use crate::user_operation::UserOperation;
pub fn entry_point_address(chain_id: u64) -> Result<Address, anyhow::Error> {
match chain_id {
1 => Ok(address!(
"0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
)),
_ => Err(anyhow::anyhow!(
"Unsupported chain id: {}",
chain_id
)),
}
}
sol! {
contract EntryPoint {
function getNonce(address sender, uint192 key) external view returns (uint256 nonce);
function handleOps(HandleOpsUserOperation[] calldata ops, address payable beneficiary) external;
#[derive(Debug)]
struct PackedUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees;
bytes paymasterAndData;
}
struct HandleOpsUserOperation {
address sender;
uint256 nonce;
bytes initCode;
bytes callData;
bytes32 accountGasLimits;
uint256 preVerificationGas;
bytes32 gasFees;
bytes paymasterAndData;
bytes signature;
}
}
}
pub fn encode_handle_ops(op: &UserOperation, beneficiary: Address) -> Bytes {
let packed = EntryPoint::PackedUserOperation::from(op);
let handle_op = EntryPoint::HandleOpsUserOperation {
sender: packed.sender,
nonce: packed.nonce,
initCode: packed.initCode,
callData: packed.callData,
accountGasLimits: packed.accountGasLimits,
preVerificationGas: packed.preVerificationGas,
gasFees: packed.gasFees,
paymasterAndData: packed.paymasterAndData,
signature: op.signature.clone(),
};
EntryPoint::handleOpsCall {
ops: vec![handle_op],
beneficiary,
}
.abi_encode()
.into()
}
impl From<&UserOperation> for EntryPoint::PackedUserOperation {
fn from(op: &UserOperation) -> Self {
let account_gas_limits = pack_u256(op.verification_gas_limit, op.call_gas_limit).into();
let gas_fees = pack_u256(op.max_priority_fee_per_gas, op.max_fee_per_gas).into();
let init_code = pack_init_code(op.factory, op.factory_data.clone());
let paymaster_and_data = pack_paymaster_and_data(
op.paymaster,
op.paymaster_verification_gas_limit,
op.paymaster_post_op_gas_limit,
op.paymaster_data.clone(),
);
EntryPoint::PackedUserOperation {
sender: op.sender,
nonce: op.nonce,
initCode: init_code,
callData: op.call_data.clone(),
accountGasLimits: account_gas_limits,
preVerificationGas: U256::from(op.pre_verification_gas),
gasFees: gas_fees,
paymasterAndData: paymaster_and_data,
}
}
}
fn pack_u256(a: u128, b: u128) -> U256 {
let a = U256::from(a);
let b = U256::from(b);
(a << 128) | b
}
fn pack_init_code(factory: Option<Address>, factory_data: Option<Bytes>) -> Bytes {
let (Some(factory), Some(factory_data)) = (factory, factory_data) else {
return Bytes::new();
};
let mut init_code = Vec::new();
init_code.extend_from_slice(factory.as_slice());
init_code.extend_from_slice(&factory_data);
init_code.into()
}
fn pack_paymaster_and_data(
paymaster: Option<Address>,
verification_gas_limit: Option<u128>,
post_op_gas_limit: Option<u128>,
paymaster_data: Option<Bytes>,
) -> Bytes {
let (
Some(paymaster),
Some(verification_gas_limit),
Some(post_op_gas_limit),
Some(paymaster_data),
) = (
paymaster,
verification_gas_limit,
post_op_gas_limit,
paymaster_data,
)
else {
return Bytes::new();
};
let mut data = Vec::new();
data.extend_from_slice(paymaster.as_slice());
data.extend_from_slice(&verification_gas_limit.to_be_bytes());
data.extend_from_slice(&post_op_gas_limit.to_be_bytes());
data.extend_from_slice(&paymaster_data);
data.into()
}