use alloy_dyn_abi::Eip712Domain;
use alloy_eips::eip7702::Authorization;
use alloy_primitives::{Address, Bytes, U256};
use alloy_provider::{Provider, network::Ethereum};
use crate::{
bundler::{BundlerError, PimlicoBundler},
signable_user_operation::SignableUserOperation,
smart_account::{SmartAccountError, simple_smart_account::SimpleSmartAccount},
user_operation::{UserOperation, UserOperationGasEstimate},
};
pub struct UserOperationBuilder {
user_op: UserOperation,
gas_set: bool,
entry_point: Address,
domain: Eip712Domain,
}
impl UserOperationBuilder {
pub fn new(sender: Address, entry_point: Address, domain: Eip712Domain) -> Self {
Self {
user_op: UserOperation {
sender,
nonce: U256::ZERO,
factory: None,
factory_data: None,
call_data: Bytes::new(),
call_gas_limit: 0,
verification_gas_limit: 0,
pre_verification_gas: 0,
max_fee_per_gas: 0,
max_priority_fee_per_gas: 0,
paymaster: None,
paymaster_verification_gas_limit: None,
paymaster_post_op_gas_limit: None,
paymaster_data: None,
signature: Bytes::new(),
authorization: Default::default(),
},
entry_point,
domain,
gas_set: false,
}
}
pub async fn new_with_smart_account<P: Provider<Ethereum>>(
smart_account: &SimpleSmartAccount<P>,
) -> Result<Self, SmartAccountError> {
Ok(Self::new(
smart_account.address(),
smart_account.entry_point(),
smart_account.domain(),
)
.with_nonce(smart_account.nonce().await?)
.with_authorization(smart_account.authorization().await?)
.with_signature(smart_account.dummy_signature()))
}
pub fn with_calldata(mut self, calldata: Bytes) -> Self {
self.user_op.call_data = calldata;
self
}
pub fn with_paymaster(mut self, paymaster: Address, paymaster_data: Vec<u8>) -> Self {
self.user_op.paymaster = Some(paymaster);
self.user_op.paymaster_data = Some(paymaster_data.into());
self
}
pub fn with_nonce(mut self, nonce: U256) -> Self {
self.user_op.nonce = nonce;
self
}
pub fn with_authorization(mut self, auth: Authorization) -> Self {
self.user_op.authorization = crate::user_operation::Authorization::Eip7702(auth);
self
}
pub fn with_gas(mut self, gas: UserOperationGasEstimate) -> Self {
self.set_gas(gas);
self
}
pub async fn with_gas_estimate(
mut self,
bundler: &PimlicoBundler,
) -> Result<Self, BundlerError> {
let op = self.build();
let est = bundler.estimate_gas(&op).await?;
self.set_gas(est);
Ok(self)
}
pub fn with_factory(mut self, factory: Address, data: Bytes) -> Self {
self.user_op.factory = Some(factory);
self.user_op.factory_data = Some(data);
self
}
pub fn build(&self) -> SignableUserOperation {
SignableUserOperation {
user_op: self.user_op.clone(),
entry_point: self.entry_point,
domain: self.domain.clone(),
}
}
pub(crate) fn with_signature(mut self, signature: Bytes) -> Self {
self.user_op.signature = signature;
self
}
fn set_gas(&mut self, gas: UserOperationGasEstimate) {
self.gas_set = true;
self.user_op.call_gas_limit = gas.call_gas_limit;
self.user_op.verification_gas_limit = gas.verification_gas_limit;
self.user_op.pre_verification_gas = gas.pre_verification_gas;
self.user_op.paymaster_verification_gas_limit = gas.paymaster_verification_gas_limit;
self.user_op.paymaster_post_op_gas_limit = gas.paymaster_post_op_gas_limit;
self.user_op.max_fee_per_gas = gas.max_fee_per_gas;
self.user_op.max_priority_fee_per_gas = gas.max_priority_fee_per_gas;
}
}