use crate::{Log, TransactionReceipt};
use alloc::vec::Vec;
use alloy_consensus::conditional::BlockConditionalAttributes;
use alloy_primitives::{
map::{AddressHashMap, HashMap},
Address, BlockNumber, Bytes, B256, U256,
};
#[derive(Debug, Clone, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct TransactionConditional {
#[cfg_attr(feature = "serde", serde(default))]
pub known_accounts: AddressHashMap<AccountStorage>,
#[cfg_attr(
feature = "serde",
serde(
default,
with = "alloy_serde::quantity::opt",
skip_serializing_if = "Option::is_none"
)
)]
pub block_number_min: Option<BlockNumber>,
#[cfg_attr(
feature = "serde",
serde(
default,
with = "alloy_serde::quantity::opt",
skip_serializing_if = "Option::is_none"
)
)]
pub block_number_max: Option<BlockNumber>,
#[cfg_attr(
feature = "serde",
serde(
default,
with = "alloy_serde::quantity::opt",
skip_serializing_if = "Option::is_none"
)
)]
pub timestamp_min: Option<u64>,
#[cfg_attr(
feature = "serde",
serde(
default,
with = "alloy_serde::quantity::opt",
skip_serializing_if = "Option::is_none"
)
)]
pub timestamp_max: Option<u64>,
}
impl TransactionConditional {
pub const fn has_exceeded_block_attributes(&self, block: &BlockConditionalAttributes) -> bool {
self.has_exceeded_block_number(block.number) || self.has_exceeded_timestamp(block.timestamp)
}
pub const fn has_exceeded_block_number(&self, block_number: BlockNumber) -> bool {
let Some(max_num) = self.block_number_max else { return false };
block_number > max_num
}
pub const fn has_exceeded_timestamp(&self, timestamp: u64) -> bool {
let Some(max_timestamp) = self.timestamp_max else { return false };
timestamp > max_timestamp
}
pub const fn matches_block_attributes(&self, block: &BlockConditionalAttributes) -> bool {
self.matches_block_number(block.number) && self.matches_timestamp(block.timestamp)
}
pub const fn matches_block_number(&self, block_number: BlockNumber) -> bool {
if let Some(min) = self.block_number_min {
if block_number < min {
return false;
}
}
if let Some(max) = self.block_number_max {
if block_number > max {
return false;
}
}
true
}
pub const fn matches_timestamp(&self, timestamp: u64) -> bool {
if let Some(min) = self.timestamp_min {
if timestamp < min {
return false;
}
}
if let Some(max) = self.timestamp_max {
if timestamp > max {
return false;
}
}
true
}
pub fn cost(&self) -> u64 {
let mut cost = 0;
for account in self.known_accounts.values() {
cost += 1;
match account {
AccountStorage::RootHash(_) => {
cost += 1;
}
AccountStorage::Slots(slots) => {
cost += slots.len() as u64;
}
}
}
if self.block_number_min.is_some() || self.block_number_max.is_some() {
cost += 1;
}
if self.timestamp_min.is_some() || self.timestamp_max.is_some() {
cost += 1;
}
cost
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum AccountStorage {
RootHash(B256),
Slots(HashMap<U256, B256>),
}
impl AccountStorage {
pub const fn is_root(&self) -> bool {
matches!(self, Self::RootHash(_))
}
pub const fn as_slots(&self) -> Option<&HashMap<U256, B256>> {
match self {
Self::Slots(slots) => Some(slots),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct UserOperation {
pub sender: Address,
pub nonce: U256,
pub init_code: Bytes,
pub call_data: Bytes,
pub call_gas_limit: U256,
pub verification_gas_limit: U256,
pub pre_verification_gas: U256,
pub max_fee_per_gas: U256,
pub max_priority_fee_per_gas: U256,
pub paymaster_and_data: Bytes,
pub signature: Bytes,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct PackedUserOperation {
pub sender: Address,
pub nonce: U256,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub factory: Option<Address>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub factory_data: Option<Bytes>,
pub call_data: Bytes,
pub call_gas_limit: U256,
pub verification_gas_limit: U256,
pub pre_verification_gas: U256,
pub max_fee_per_gas: U256,
pub max_priority_fee_per_gas: U256,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub paymaster: Option<Address>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub paymaster_verification_gas_limit: Option<U256>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub paymaster_post_op_gas_limit: Option<U256>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
pub paymaster_data: Option<Bytes>,
pub signature: Bytes,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SendUserOperation {
EntryPointV06(UserOperation),
EntryPointV07(PackedUserOperation),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct SendUserOperationResponse {
pub user_op_hash: Bytes,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct UserOperationReceipt {
pub user_op_hash: Bytes,
pub entry_point: Address,
pub sender: Address,
pub nonce: U256,
pub paymaster: Address,
pub actual_gas_cost: U256,
pub actual_gas_used: U256,
pub success: bool,
pub reason: Bytes,
pub logs: Vec<Log>,
pub receipt: TransactionReceipt,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct UserOperationGasEstimation {
pub pre_verification_gas: U256,
pub verification_gas: U256,
pub paymaster_verification_gas: U256,
pub call_gas_limit: U256,
}