diff --git a/Cargo.lock b/Cargo.lock index d9a7b81b..d0ea97d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1991,7 +1991,7 @@ dependencies = [ [[package]] name = "ethers" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "ethers-addressbook 2.0.14 (git+https://github.com/Vid201/ethers-rs?branch=feat/patch)", "ethers-contract 2.0.14 (git+https://github.com/Vid201/ethers-rs?branch=feat/patch)", @@ -2018,7 +2018,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "ethers-core 2.0.14 (git+https://github.com/Vid201/ethers-rs?branch=feat/patch)", "once_cell", @@ -2048,7 +2048,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "const-hex", "ethers-contract-abigen 2.0.14 (git+https://github.com/Vid201/ethers-rs?branch=feat/patch)", @@ -2090,7 +2090,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "Inflector", "const-hex", @@ -2129,7 +2129,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "Inflector", "const-hex", @@ -2174,7 +2174,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "arrayvec", "bytes", @@ -2245,7 +2245,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "chrono", "ethers-core 2.0.14 (git+https://github.com/Vid201/ethers-rs?branch=feat/patch)", @@ -2322,7 +2322,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "async-trait", "auto_impl", @@ -2385,7 +2385,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "async-recursion", "async-trait", @@ -2442,7 +2442,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "async-trait", "coins-bip32", @@ -2492,7 +2492,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "2.0.14" -source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#c9de1525d69f601790e7d5ae44d34d85b615d62f" +source = "git+https://github.com/Vid201/ethers-rs?branch=feat/patch#14d410da533c8cdc3dc647349eac2e2f3141da6c" dependencies = [ "cfg-if", "const-hex", @@ -3334,7 +3334,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -3393,7 +3393,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.51.1", ] [[package]] diff --git a/Cross.toml b/Cross.toml index c65b3608..e7d99827 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,6 +1,6 @@ [build] pre-build = [ - "apt-get update && apt-get -y upgrade && apt-get install -y wget pkg-config llvm-dev libclang-6.0-dev clang-6.0 libssl-dev", + "apt-get update && apt-get -y upgrade && apt-get install -y wget pkg-config llvm-dev libclang-6.0-dev clang-6.0 libssl-dev ca-certificates", "wget -c https://github.com/ethereum/solidity/releases/download/v0.8.20/solc-static-linux && mv solc-static-linux /usr/local/bin/solc && chmod a+x /usr/local/bin/solc" ] diff --git a/bin/silius/src/bundler.rs b/bin/silius/src/bundler.rs index b5c412ce..dead9a36 100644 --- a/bin/silius/src/bundler.rs +++ b/bin/silius/src/bundler.rs @@ -8,7 +8,7 @@ use crate::{ use alloy_chains::{Chain, NamedChain}; use ethers::{providers::Middleware, types::Address}; use parking_lot::RwLock; -use silius_bundler::{ConditionalClient, EthereumClient, FlashbotsClient}; +use silius_bundler::{ConditionalClient, EthereumClient, FastlaneClient, FlashbotsClient}; use silius_contracts::EntryPoint; use silius_grpc::{ bundler_client::BundlerClient, bundler_service_run, uo_pool_client::UoPoolClient, @@ -24,7 +24,7 @@ use silius_metrics::{launch_metrics_exporter, mempool::MetricsHandler}; use silius_primitives::{ bundler::SendStrategy, constants::{ - entry_point, flashbots_relay_endpoints, + entry_point, fastlane_relay_endpoints, flashbots_relay_endpoints, storage::DATABASE_FOLDER_NAME, supported_chains::CHAINS, validation::reputation::{ @@ -208,6 +208,32 @@ where args.enable_access_list, ); } + SendStrategy::Fastlane => { + let relay_endpoints: Vec = + match chain_conn.named().expect("Fastlane is only supported on Polygon") { + NamedChain::Polygon => { + vec![fastlane_relay_endpoints::FASTLANE_POLYGON.into()] + } + _ => panic!("Fastlane is only supported on Polygon"), + }; + + let client = + Arc::new(FastlaneClient::new(eth_client.clone(), relay_endpoints, wallet.clone())); + bundler_service_run( + SocketAddr::new(args.bundler_addr, args.bundler_port), + wallet, + entry_points, + chain_conn, + args.beneficiary, + args.min_balance, + args.bundle_interval, + eth_client, + client, + uopool_grpc_client, + metrics_args.enable_metrics, + args.enable_access_list, + ); + } } info!("Started bundler gRPC service at {:?}:{:?}", args.bundler_addr, args.bundler_port); @@ -490,7 +516,7 @@ where if let Some(chain) = chain { if !CHAINS.contains(&chain) { warn!("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - warn!("Chain {:?} is not officially supported yet! You could possibly meet a lot of problems with silius. Use at your own risk!!", chain); + warn!("Chain {:?} is not officially supported yet! You could possibly meet a lot of problems with Silius. Use at your own risk!", chain); warn!("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); } diff --git a/crates/bundler/src/conditional.rs b/crates/bundler/src/conditional.rs index d0fc7729..fd79ed56 100644 --- a/crates/bundler/src/conditional.rs +++ b/crates/bundler/src/conditional.rs @@ -1,4 +1,5 @@ use crate::bundler::SendBundleOp; +use alloy_chains::{Chain, NamedChain}; use ethers::{ middleware::SignerMiddleware, providers::Middleware, @@ -52,10 +53,18 @@ where let signed_tx = self.0.sign_transaction(bundle).await?; + let prefix: Option = + if self.0.get_chainid().await? == Chain::from_named(NamedChain::Polygon).id().into() { + Some("bor".to_string()) + } else { + None + }; + let tx = self .0 .send_raw_transaction_conditional( signed_tx, + prefix, ConditionalOptions { known_accounts, ..Default::default() }, ) .await? diff --git a/crates/bundler/src/fastlane.rs b/crates/bundler/src/fastlane.rs new file mode 100644 index 00000000..f5268346 --- /dev/null +++ b/crates/bundler/src/fastlane.rs @@ -0,0 +1,103 @@ +use crate::bundler::SendBundleOp; +use ethers::{ + middleware::SignerMiddleware, + providers::Middleware, + signers::LocalWallet, + types::{ + transaction::{ + conditional::{AccountStorage, ConditionalOptions}, + eip2718::TypedTransaction, + }, + Address, BlockNumber, H256, + }, +}; +use silius_primitives::{simulation::StorageMap, Wallet}; +use std::{collections::HashMap, sync::Arc, time::Duration}; +use tracing::trace; + +/// A type alias for the Ethereum Conditional Signer client +#[derive(Clone)] +pub struct FastlaneClient { + pub client: SignerMiddleware, LocalWallet>, + pub relay_endpoints: Vec, +} + +#[async_trait::async_trait] +impl SendBundleOp for FastlaneClient +where + M: Middleware + 'static, +{ + /// Send a bundle of user operations to the Ethereum execution client + /// over conditional RPC method. + /// + /// # Arguments + /// * `bundle` - Bundle of user operations as [TypedTransaction](TypedTransaction). + /// * 'storage_map' - Storage map + /// + /// # Returns + /// * `H256` - The transaction hash + async fn send_bundle( + &self, + bundle: TypedTransaction, + storage_map: StorageMap, + ) -> eyre::Result { + trace!("Sending transaction to the conditional endpoint: {bundle:?}"); + + let mut known_accounts: HashMap = HashMap::default(); + + for (k, v) in storage_map.root_hashes { + known_accounts.insert(k, AccountStorage::RootHash(v)); + } + + for (k, v) in storage_map.slots { + known_accounts.insert(k, AccountStorage::SlotValues(v)); + } + + let signed_tx = self.client.sign_transaction(bundle).await?; + + let prefix: Option = Some("pfl".to_string()); + let block = self.client.get_block(BlockNumber::Latest).await?; + + let mut options = ConditionalOptions { known_accounts, ..Default::default() }; + + if let Some(block) = block { + if let Some(block_number) = block.number { + options.block_number_min = Some(block_number.into()); + options.block_number_max = Some((block_number + 100).into()); // around 10 minutes + options.timestamp_min = Some(block.timestamp.as_u64()); + options.timestamp_max = Some(block.timestamp.as_u64() + 420); // around 15 minutes + } + } + + let tx = self + .client + .send_raw_transaction_conditional(signed_tx, prefix, options) + .await? + .interval(Duration::from_millis(75)); + let tx_hash = tx.tx_hash(); + + let tx_receipt = tx.await?; + + trace!("Transaction receipt: {tx_receipt:?}"); + + Ok(tx_hash) + } +} + +impl FastlaneClient +where + M: Middleware + 'static, +{ + /// Create an Conditional client + /// + /// # Arguments + /// * `eth_client` - Connection to the Ethereum execution client + /// * `wallet` - A [Wallet](Wallet) instance + /// + /// # Returns + /// * `ConditionalClient` - A [Ethereum Signer Middleware](ConditionalClient) + pub fn new(eth_client: Arc, relay_endpoints: Vec, wallet: Wallet) -> Self { + let signer = SignerMiddleware::new(eth_client, wallet.signer); + Self { client: signer, relay_endpoints } + } +} diff --git a/crates/bundler/src/lib.rs b/crates/bundler/src/lib.rs index d1c2f3f6..2f347c37 100644 --- a/crates/bundler/src/lib.rs +++ b/crates/bundler/src/lib.rs @@ -4,9 +4,11 @@ mod bundler; mod conditional; mod ethereum; +mod fastlane; mod flashbots; pub use bundler::{Bundler, SendBundleOp}; pub use conditional::ConditionalClient; pub use ethereum::EthereumClient; +pub use fastlane::FastlaneClient; pub use flashbots::FlashbotsClient; diff --git a/crates/mempool/src/validate/sanity/sender.rs b/crates/mempool/src/validate/sanity/sender.rs index d84005e5..dc909813 100644 --- a/crates/mempool/src/validate/sanity/sender.rs +++ b/crates/mempool/src/validate/sanity/sender.rs @@ -51,11 +51,15 @@ impl SanityCheck for Sender { return Ok(()); } - let uo_prev = mempool - .get_all_by_sender(&uo.sender) - .iter() - .find(|uo_prev| uo_prev.nonce == uo.nonce) - .cloned(); + let mut uo_prev: Option = None; + + if !helper.val_config.ignore_prev { + uo_prev = mempool + .get_all_by_sender(&uo.sender) + .iter() + .find(|uo_prev| uo_prev.nonce == uo.nonce) + .cloned(); + } if let Some(uo_prev) = uo_prev { if uo.max_fee_per_gas < diff --git a/crates/mempool/src/validate/validator.rs b/crates/mempool/src/validate/validator.rs index 5f660a3e..53ae1677 100644 --- a/crates/mempool/src/validate/validator.rs +++ b/crates/mempool/src/validate/validator.rs @@ -251,6 +251,7 @@ where min_stake: Some(reputation.min_stake()), min_unstake_delay: Some(reputation.min_unstake_delay()), topic: None, + ignore_prev: false, }; } @@ -269,6 +270,7 @@ where if let Some(uo) = mempool.get_prev_by_sender(uo) { out.prev_hash = Some(uo.hash); } + debug!("Simulate user operation from {:?}", uo.sender); let sim_res = self.simulate_validation(uo).await?; diff --git a/crates/p2p/src/service/mod.rs b/crates/p2p/src/service/mod.rs index 02248dcf..0548d1d0 100644 --- a/crates/p2p/src/service/mod.rs +++ b/crates/p2p/src/service/mod.rs @@ -304,6 +304,7 @@ impl Network { min_stake: Some(canonical_mempool_config.min_stake), min_unstake_delay: None, topic: Some(message.topic.to_string()), + ignore_prev: false, } }) .expect("mempool channel should be open all the time"); @@ -424,7 +425,7 @@ impl Network { verified_at_block_hash, validation_config, } => { - info!("Received user {user_operation:?} from ep {ep:?} verified in {verified_at_block_hash:?} to gossip over p2p!"); + info!("Received user operation (verified at {verified_at_block_hash:?}) to gossip over p2p: {user_operation:?}"); let user_op = VerifiedUserOperation::new( user_operation.user_operation.clone(), @@ -444,6 +445,7 @@ impl Network { min_stake: Some(first_mempool_config.min_stake), min_unstake_delay: None, topic: Some(first_mempool_topic.to_string()), + ignore_prev: true, }, }) .expect("mempool channel should be open all the time"); @@ -463,6 +465,7 @@ impl Network { min_stake: Some(canonical_mempool_config.min_stake), min_unstake_delay: None, topic: Some(canonical_mempool_topic.to_string()), + ignore_prev: true, }, }) .expect("mempool channel should be open all the time"); diff --git a/crates/p2p/src/service/utils.rs b/crates/p2p/src/service/utils.rs index 154054d4..df93dc38 100644 --- a/crates/p2p/src/service/utils.rs +++ b/crates/p2p/src/service/utils.rs @@ -48,7 +48,8 @@ pub fn save_private_key_to_file(key: &Keypair, path: &PathBuf) { /// Fetch mempool configuration from IPFS. pub async fn fetch_mempool_config(cid: String) -> Result { - let body = reqwest::get(format!("{IPFS_GATEWAY}/{cid}")).await?.text().await?; + let body = + reqwest::Client::new().get(format!("{IPFS_GATEWAY}/{cid}")).send().await?.text().await?; let mempool_config: MempoolConfig = serde_yml::from_str(&body)?; Ok(mempool_config) } diff --git a/crates/primitives/src/bundler.rs b/crates/primitives/src/bundler.rs index 9388a92a..fb6461e4 100644 --- a/crates/primitives/src/bundler.rs +++ b/crates/primitives/src/bundler.rs @@ -24,4 +24,6 @@ pub enum SendStrategy { Flashbots, /// Send the bundle to the Ethereum execution client over conditional RPC method Conditional, + /// Sends the bundle to the Fastlane relay + Fastlane, } diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index 50bf4291..f16803b0 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -85,6 +85,12 @@ pub mod flashbots_relay_endpoints { pub const FLASHBOTS_SEPOLIA: &str = "https://relay-sepolia.flashbots.net"; } +/// Fastlane relay endpoints +pub mod fastlane_relay_endpoints { + // polygon + pub const FASTLANE_POLYGON: &str = "https://polygon-test-rpc.fastlane.xyz"; +} + /// Supported chains pub mod supported_chains { use alloy_chains::NamedChain; diff --git a/crates/primitives/src/simulation.rs b/crates/primitives/src/simulation.rs index f6e2be7d..4928d950 100644 --- a/crates/primitives/src/simulation.rs +++ b/crates/primitives/src/simulation.rs @@ -45,6 +45,7 @@ pub struct ValidationConfig { pub min_stake: Option, pub min_unstake_delay: Option, pub topic: Option, + pub ignore_prev: bool, } /// Code hash - hash of the code of the contract