kohaku-userop-kit 0.0.1

EIP-4337 client
use alloy::rpc::types::state::StateOverride;

use crate::{
    builder::UserOperationBuilder,
    signable_user_operation::SignableUserOperation,
    signed_user_operation::SignedUserOperation,
    user_operation::{UserOperationGasEstimate, UserOperationHash, UserOperationReceipt},
};

pub mod pimlico;
mod rpc_client;

pub trait BundlerExt: Sized {
    /// Estimates and sets the gas for this UserOperation.
    ///
    /// Simulates against `state_override` if provided.
    fn with_gas_estimate<B: Bundler + ?Sized>(
        self,
        bundler: &B,
        state_override: Option<&StateOverride>,
    ) -> impl Future<Output = Result<Self, BundlerError>>;
}

/// A bundler provider for 4337 UserOperation JSON-RPC methods.
#[cfg_attr(native, async_trait::async_trait)]
#[cfg_attr(wasm, async_trait::async_trait(?Send))]
pub trait Bundler {
    /// Estimates gas for `op`.
    ///
    /// Simulates against `state_override` if provided.
    async fn estimate_gas(
        &self,
        op: &SignableUserOperation,
        state_override: Option<&StateOverride>,
    ) -> Result<UserOperationGasEstimate, BundlerError>;
    async fn send_user_operation(
        &self,
        op: &SignedUserOperation,
    ) -> Result<UserOperationHash, BundlerError>;
    async fn receipt(
        &self,
        hash: UserOperationHash,
    ) -> Result<Option<UserOperationReceipt>, BundlerError>;
    async fn wait_for_receipt(
        &self,
        hash: UserOperationHash,
    ) -> Result<UserOperationReceipt, BundlerError>;
}

#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum BundlerError {
    #[error("Timeout")]
    Timeout,
    #[error("Other: {0}")]
    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}

impl<S> BundlerExt for UserOperationBuilder<S> {
    async fn with_gas_estimate<B: Bundler + ?Sized>(
        self,
        bundler: &B,
        state_override: Option<&StateOverride>,
    ) -> Result<Self, BundlerError> {
        let op = self.build();
        let est = bundler.estimate_gas(&op, state_override).await?;

        Ok(self.with_gas(est))
    }
}