diff --git a/.env.example b/.env.example index 4e8b0f1..bac9ae1 100644 --- a/.env.example +++ b/.env.example @@ -3,10 +3,7 @@ BOT_PRIVATE_KEY= # rpc BLOXROUTE_AUTH_HEADER= -WSS_RPC_URL_FOURMEME= -WSS_RPC_URL_PANCAKE= -HTTP_RPC_URL= -HTTP_SEND_TX_URL= +IPC_URL= # tg bot TELEGRAM_BOT_TOKEN= diff --git a/src/pvp/config.rs b/src/pvp/config.rs index e99b5c6..0a14f50 100644 --- a/src/pvp/config.rs +++ b/src/pvp/config.rs @@ -66,13 +66,8 @@ pub struct EnvConfig { pub log_level: LogLevel, // -------- rpc config - /// The WebSocket RPC URL - pub wss_rpc_url_fourmeme: String, - pub wss_rpc_url_pancake: String, - /// The HTTP RPC URL - pub http_rpc_url: String, - /// The HTTP send tx URL: used to prevent rate limiting - pub http_send_tx_url: String, + /// The IPC URL + pub ipc_url: String, /// The bloXroute auth header pub bloxroute_auth_header: String, @@ -131,10 +126,7 @@ impl Default for EnvConfig { tx_simulation: true, // -------- rpc config - wss_rpc_url_fourmeme: env::var("WSS_RPC_URL_FOURMEME").expect("WSS_RPC_URL_FOURMEME not set"), - wss_rpc_url_pancake: env::var("WSS_RPC_URL_PANCAKE").expect("WSS_RPC_URL_PANCAKE not set"), - http_rpc_url: env::var("HTTP_RPC_URL").expect("HTTP_RPC_URL not set"), - http_send_tx_url: env::var("HTTP_SEND_TX_URL").expect("HTTP_SEND_TX_URL not set"), + ipc_url: env::var("IPC_URL").expect("IPC_URL not set"), bloxroute_auth_header: env::var("BLOXROUTE_AUTH_HEADER").expect("BLOXROUTE_AUTH_HEADER not set"), // -------- database config @@ -265,23 +257,8 @@ impl EnvConfig { self } - pub fn with_wss_rpc_url_fourmeme(mut self, url: String) -> Self { - self.wss_rpc_url_fourmeme = url; - self - } - - pub fn with_wss_rpc_url_pancake(mut self, url: String) -> Self { - self.wss_rpc_url_pancake = url; - self - } - - pub fn with_http_rpc_url(mut self, url: String) -> Self { - self.http_rpc_url = url; - self - } - - pub fn with_http_send_tx_url(mut self, url: String) -> Self { - self.http_send_tx_url = url; + pub fn with_ipc_url(mut self, url: String) -> Self { + self.ipc_url = url; self } diff --git a/src/pvp/interface/mod.rs b/src/pvp/interface/mod.rs index 9366d65..b004d22 100644 --- a/src/pvp/interface/mod.rs +++ b/src/pvp/interface/mod.rs @@ -2,7 +2,6 @@ pub mod db; pub mod inner_pool_trader; pub mod oracle; pub mod outer_pool_trader; -pub mod sheet; pub use inner_pool_trader::IInnerPoolTrader; pub use oracle::IOracle; diff --git a/src/pvp/interface/sheet.rs b/src/pvp/interface/sheet.rs deleted file mode 100644 index 281b499..0000000 --- a/src/pvp/interface/sheet.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub use alloy::primitives::Address; - -/// Sheet interface -/// -/// This trait is about sheet related actions -pub struct ISheet { - _chain: u64, - _token: Address, -} diff --git a/src/pvp/service/auto_sell.rs b/src/pvp/service/auto_sell.rs index dc0251e..2f93516 100644 --- a/src/pvp/service/auto_sell.rs +++ b/src/pvp/service/auto_sell.rs @@ -5,6 +5,8 @@ //! //! Known issues: When checking latest_trade_info, there is a competitive relationship between auto_sell and strategy_pancake //! modules, so the same token may be sold continuously in one minute. +//! +//! This is optional. If you have an own node, you can remove this service. use std::{marker::PhantomData, str::FromStr, sync::Arc, time::Duration}; @@ -30,7 +32,7 @@ use crate::pvp::{ }, strategy::helper::{ StrategyType, get_diff_time_since_migration, get_initial_fourmeme_buy_amount, get_pair_wbnb_balance, - get_wbnb_amount_when_bought_token, + get_pair_wbnb_balance_when_bought_token, }, trade::outer_pool_trader::OuterPoolTrader, }; @@ -144,7 +146,7 @@ where current_pair_wbnb_balance: U256, strategy: &str, ) -> Result<(), CustomError> { - let wbnb_amount_when_bought = get_wbnb_amount_when_bought_token(&*self.db, meme_token) + let wbnb_amount_when_bought = get_pair_wbnb_balance_when_bought_token(&*self.db, meme_token) .await .unwrap_or_default(); diff --git a/src/pvp/start.rs b/src/pvp/start.rs index ff076f4..4302101 100644 --- a/src/pvp/start.rs +++ b/src/pvp/start.rs @@ -1,7 +1,7 @@ use alloy::{ eips::BlockNumberOrTag, primitives::{Address, U256}, - providers::{Provider, ProviderBuilder, WsConnect}, + providers::{IpcConnect, Provider, ProviderBuilder}, rpc::types::Filter, signers::local::PrivateKeySigner, sol_types::SolEvent, @@ -30,7 +30,7 @@ use crate::pvp::{ logger::Logger, nonce::NonceManager, }, - service::{auto_sell::AutoSeller, db_cleaner::start_db_cleaner}, + service::db_cleaner::start_db_cleaner, strategy::strategy::{Event, Strategy}, trade::{TxSubmitType, inner_pool_trader::InnerPoolTrader, outer_pool_trader::OuterPoolTrader}, }; @@ -114,16 +114,7 @@ fn print_config(env_config: &EnvConfig) { ("TX Simulation".to_string(), env_config.tx_simulation.to_string()), ("Log Level".to_string(), format!("{:?}", env_config.log_level)), // -------- rpc config - ( - "WSS RPC URL Fourmeme".to_string(), - env_config.wss_rpc_url_fourmeme.clone(), - ), - ( - "WSS RPC URL PancakeSwap".to_string(), - env_config.wss_rpc_url_pancake.clone(), - ), - ("HTTP RPC URL".to_string(), env_config.http_rpc_url.clone()), - ("HTTP Send TX URL".to_string(), env_config.http_send_tx_url.clone()), + ("IPC URL".to_string(), env_config.ipc_url.clone()), // -------- database config ("DB Path".to_string(), env_config.db_path.clone()), ]), @@ -213,50 +204,33 @@ pub async fn run() -> eyre::Result<()> { // 3. Setup signer let signer: PrivateKeySigner = env_config.bot_private_key.parse()?; - - // 4. Setup providers let wallet = alloy::network::EthereumWallet::from(signer.clone()); - let wss_provider_fourmeme = ProviderBuilder::new() - .wallet(wallet.clone()) - .connect_ws(WsConnect::new(&env_config.wss_rpc_url_fourmeme)) - .await?; - let wss_provider_pancake = ProviderBuilder::new() - .wallet(wallet.clone()) - .connect_ws(WsConnect::new(&env_config.wss_rpc_url_pancake)) - .await?; - let http_provider = ProviderBuilder::new() - .wallet(wallet.clone()) - .connect_http(env_config.http_rpc_url.parse()?); - let http_send_tx_provider = ProviderBuilder::new() - .wallet(wallet) - .connect_http(env_config.http_send_tx_url.parse()?); + // 4. Setup provider + let ipc = IpcConnect::new(env_config.ipc_url.clone()); + let ipc_provider = ProviderBuilder::new().wallet(wallet.clone()).connect_ipc(ipc).await?; // 5. Setup bloXroute service let bloxroute_service = Arc::new(BloXrouteService::new( env_config.bloxroute_auth_header.clone(), vec![signer.clone()], - http_send_tx_provider.clone(), + ipc_provider.clone(), )); // 6. Create contract instances - let fourmeme_contract = Arc::new(FourmemeInstance::new(FOURMEME_CONTRACT, wss_provider_fourmeme.clone())); - let pancake_router_contract = Arc::new(PancakeSwapRouterInstance::new( - PANCAKESWAP_ROUTER, - wss_provider_pancake.clone(), - )); + let fourmeme_contract = Arc::new(FourmemeInstance::new(FOURMEME_CONTRACT, ipc_provider.clone())); + let pancake_router_contract = Arc::new(PancakeSwapRouterInstance::new(PANCAKESWAP_ROUTER, ipc_provider.clone())); // 7. Create nonce manager for Private transaction mode let nonce_manager: Option>> = if env_config.tx_submit_type == TxSubmitType::Private { - let provider_for_nonce = http_send_tx_provider.clone(); let nm = Arc::new( - NonceManager::new(provider_for_nonce, env_config.bot_address) + NonceManager::new(ipc_provider.clone(), env_config.bot_address) .await .map_err(|e| eyre::eyre!("Failed to create nonce manager: {}", e))?, ); // Start background task to update nonce periodically (every 10 seconds) - let _handle = nm + let _ = nm .update_nonce(std::time::Duration::from_secs(10)) .await .map_err(|e| eyre::eyre!("Failed to start nonce update task: {}", e))?; @@ -279,19 +253,16 @@ pub async fn run() -> eyre::Result<()> { .map_err(|e| eyre::eyre!("Failed to create outer pool trader: {}", e))?; // 9. Start BNB balance monitor task - let monitor_provider = Arc::new(http_send_tx_provider.clone()); - let monitor_bot_address = env_config.bot_address; - let monitor_required_balance = - env_config.fourmeme_progress_buy_amount * U256::from(2) + ONE_ETHER / U256::from(1000u64); // buy_amount * 2 + 0.001 BNB - let monitor_telegram_bot_token = env_config.telegram_bot_token.clone(); - let monitor_telegram_bot_chat_id = env_config.telegram_bot_chat_id.clone(); + let monitor_required_balance = ONE_ETHER / U256::from(200u64); // 0.005 BNB + let ipc_provider_clone = ipc_provider.clone(); + let env_config_clone = env_config.clone(); tokio::spawn(async move { monitor_bnb_balance( - monitor_provider, - monitor_bot_address, + Arc::new(ipc_provider_clone), + env_config_clone.bot_address, monitor_required_balance, - monitor_telegram_bot_token, - monitor_telegram_bot_chat_id, + env_config_clone.telegram_bot_token.clone(), + env_config_clone.telegram_bot_chat_id.clone(), ) .await; }); @@ -318,18 +289,18 @@ pub async fn run() -> eyre::Result<()> { .from_block(BlockNumberOrTag::Latest); engine.add_collector(map_collector!( - LogCollector::new(Arc::new(wss_provider_fourmeme.clone()), fourmeme_filter), + LogCollector::new(Arc::new(ipc_provider.clone()), fourmeme_filter), Event::FourmemeLog )); engine.add_collector(map_collector!( - LogCollector::new(Arc::new(wss_provider_pancake.clone()), pancake_filter), + LogCollector::new(Arc::new(ipc_provider.clone()), pancake_filter), Event::PancakeSwapLog )); // 12. Add strategy let strategy = Strategy::new( Arc::new(env_config.clone()), - Arc::new(http_provider), + Arc::new(ipc_provider.clone()), db.clone(), execution_lock.clone(), ); @@ -352,20 +323,7 @@ pub async fn run() -> eyre::Result<()> { start_db_cleaner(db_for_cleaner, Arc::new(env_config_for_cleaner)).await; }); - // 15. Start auto sell service - let auto_sell_service = AutoSeller::new( - db.clone(), - http_send_tx_provider.clone(), - Arc::new(Mutex::new(outer_pool_trader)), - bloxroute_service, - Arc::new(env_config.clone()), - 60, - ); - tokio::spawn(async move { - auto_sell_service.start().await; - }); - - // 16. Run engine + // 15. Run engine Logger.success("Starting PVP bot engine..."); engine .run_and_join() diff --git a/src/pvp/strategy/helper.rs b/src/pvp/strategy/helper.rs index e695ab7..45d547a 100644 --- a/src/pvp/strategy/helper.rs +++ b/src/pvp/strategy/helper.rs @@ -237,7 +237,7 @@ pub async fn parse_token_from_migrated_info( )) } -pub async fn get_wbnb_amount_when_bought_token( +pub async fn get_pair_wbnb_balance_when_bought_token( db: &D, meme_address: Address, ) -> Result { diff --git a/src/pvp/strategy/strategy_fourmeme.rs b/src/pvp/strategy/strategy_fourmeme.rs index 9207c1a..ebe2d5a 100644 --- a/src/pvp/strategy/strategy_fourmeme.rs +++ b/src/pvp/strategy/strategy_fourmeme.rs @@ -51,7 +51,7 @@ pub async fn on_fourmeme_token_migrated_event< // [STRATEGY TOKEN MIGRATED] // if: - // - 2 minutes after the token is migrated + // - 2.5 minutes after the token is migrated // - the pair's wbnb balance is greater than `meme_migrated_model_b_threshold` // then buy the token let http_provider_clone = http_provider.clone(); @@ -115,6 +115,11 @@ pub async fn consider_selling + Clone + Send + Sync return Err(CustomError::StrategyDoNothing()); } + // [STRATEGY SELL] + // if: + // - curve progress is less than `fourmeme_bundle_sl_curve_progress` + // - we are in the fourmeme stage + // then sell all the tokens let more_info = format!("curve progress: {}", curve_progress); Logger.process(&format!( "Strategy {} considering selling: meme address: {}, bot meme balance: {}, curve progress: {} - sell all", @@ -123,7 +128,11 @@ pub async fn consider_selling + Clone + Send + Sync bot_meme_balance, curve_progress )); - return Ok(build_sell_pending_action(meme_address, bot_meme_balance, more_info)); + return Ok(build_bundle_sell_pending_action( + meme_address, + bot_meme_balance, + more_info, + )); } } @@ -162,6 +171,11 @@ pub async fn consider_buying + Clone + Send + Sync if curve_progress > env_config.fourmeme_progress_buy_curve_threshold { let pair_wbnb_balance = get_pair_wbnb_balance(&http_provider, meme_address).await?; let more_info = format!("curve progress: {}", curve_progress); + + // [STRATEGY BUY] + // if: + // - curve progress is greater than `fourmeme_progress_buy_curve_threshold` + // then buy the token return Ok(build_progress_buy_pending_action( meme_address, pair_wbnb_balance, @@ -178,6 +192,12 @@ pub async fn consider_buying + Clone + Send + Sync "bundle tx block: {}, bundle sender: {}", bundle_block_number, bundle_sender ); + + // [STRATEGY BUY] + // if: + // - curve progress is greater than `fourmeme_bundle_threshold` + // - there is a bundle transaction + // then buy the token return Ok(build_bundle_buy_pending_action( meme_address, pair_wbnb_balance, @@ -223,7 +243,7 @@ async fn ensure_pair_info_in_db( } /// Helper function: build the sell pending action -fn build_sell_pending_action(meme_address: Address, meme_amount: U256, more_info: String) -> PendingAction { +fn build_bundle_sell_pending_action(meme_address: Address, meme_amount: U256, more_info: String) -> PendingAction { PendingAction { strategy: StrategyType::Bundle.to_string(), pool_type: PoolType::Fourmeme, diff --git a/src/pvp/strategy/strategy_pancake.rs b/src/pvp/strategy/strategy_pancake.rs index 82e5cbf..5c7f3af 100644 --- a/src/pvp/strategy/strategy_pancake.rs +++ b/src/pvp/strategy/strategy_pancake.rs @@ -10,7 +10,7 @@ use crate::pvp::{ strategy::{ helper::{ StrategyType, check_token_has_bought, get_bot_meme_balance, get_bundle_info, get_diff_time_since_migration, - get_initial_fourmeme_buy_amount, get_pair_wbnb_balance, get_wbnb_amount_when_bought_token, + get_initial_fourmeme_buy_amount, get_pair_wbnb_balance, get_pair_wbnb_balance_when_bought_token, is_pass_internal, }, strategy::PendingAction, @@ -24,6 +24,52 @@ use alloy::{ use chrono::Duration; use std::sync::Arc; +pub async fn consider_buying + Clone + Send + Sync + 'static, N: Network>( + db: Arc, + env_config: Arc, + http_provider: P, + meme_address: Address, + block_number: u64, +) -> Result { + let has_bought = check_token_has_bought(&*db, meme_address).await?; + if has_bought.is_some() { + return Err(CustomError::StrategyDoNothing()); + } + + let bundle_info = get_bundle_info(&*db, meme_address, block_number, env_config.bundle_tx_count).await?; + if let Some((bundle_sender, bundle_block_number)) = bundle_info { + let pair_wbnb_balance = get_pair_wbnb_balance(&http_provider, meme_address).await?; + if pair_wbnb_balance < env_config.pancake_bundle_threshold { + return Err(CustomError::StrategyDoNothing()); + } + + // [STRATEGY BUY] + // if: + // - there is a bundle transaction + // - pair bnb balance is greater than `pancake_bundle_threshold` + // then buy the token + let more_info = format!( + "bundle tx block: {}, bundle sender: {}", + bundle_block_number, bundle_sender + ); + return Ok(PendingAction { + strategy: StrategyType::Bundle.to_string(), + pool_type: PoolType::PancakeV2, + pancake_wbnb_balance: pair_wbnb_balance, + is_buy: true, + meme_address, + meme_amount: U256::from(0), + bnb_amount: env_config.pancake_bundle_buy_amount, + more_info, + }); + } + + Logger.item(&format!("No strategy applied, meme address: {}", meme_address)); + Logger.sub_item(&format!("Block number: {}", block_number)); + + Err(CustomError::StrategyDoNothing()) +} + pub async fn consider_selling + Clone + Send + Sync + 'static, N: Network>( is_buy: bool, db: Arc, @@ -113,9 +159,9 @@ async fn handle_bundle_or_migrated_sell( current_pair_wbnb_balance: U256, strategy: &str, ) -> Result { - let wbnb_amount_when_bought = get_wbnb_amount_when_bought_token(&**db, meme_address).await?; + let pair_wbnb_balance_when_bought = get_pair_wbnb_balance_when_bought_token(&**db, meme_address).await?; - if current_pair_wbnb_balance < wbnb_amount_when_bought - U256::from(3) * ONE_ETHER { + if current_pair_wbnb_balance < pair_wbnb_balance_when_bought - U256::from(3) * ONE_ETHER { let more_info = format!("{} (-∞, X-3): sell all", strategy); Logger.process(&format!( "Strategy {} considering selling: meme address: {} - (-∞, X-3): sell all", @@ -128,7 +174,7 @@ async fn handle_bundle_or_migrated_sell( bot_meme_balance, more_info, )); - } else if current_pair_wbnb_balance > wbnb_amount_when_bought { + } else { let initial_fourmeme_buy_amount = get_initial_fourmeme_buy_amount(&**db, meme_address).await?; let five_percentage = initial_fourmeme_buy_amount / U256::from(20); @@ -138,6 +184,12 @@ async fn handle_bundle_or_migrated_sell( "Strategy {} considering selling: meme address: {} < 100 WBNB: do nothing", strategy, meme_address )); + + // [STRATEGY SELL] + // if: + // - bot meme balance is less than `initial_fourmeme_buy_amount / 20` --> 5% + // - pair bnb balance is less than `ONE_ETHER * 100` + // then do nothing return Err(CustomError::StrategyDoNothing()); } else { let more_info = format!("{}: pair reaches 100 WBNB, sell all", strategy); @@ -145,6 +197,12 @@ async fn handle_bundle_or_migrated_sell( "Strategy {} considering selling: meme address: {} >= 100 WBNB: sell all", strategy, meme_address )); + + // [STRATEGY SELL] + // if: + // - bot meme balance is greater than or equal to `initial_fourmeme_buy_amount / 20` --> 5% + // - pair bnb balance is greater than or equal to `ONE_ETHER * 100` + // then sell all the tokens return Ok(build_sell_pending_action( strategy, current_pair_wbnb_balance, @@ -161,6 +219,11 @@ async fn handle_bundle_or_migrated_sell( "Strategy {} considering selling: meme address: {} - (X, +∞): sell 1%", strategy, meme_address )); + + // [STRATEGY SELL] + // if: + // - bot meme balance is greater than or equal to `initial_fourmeme_buy_amount / 20` --> 5% + // then sell 1% of the tokens return Ok(build_sell_pending_action( strategy, current_pair_wbnb_balance, @@ -169,8 +232,6 @@ async fn handle_bundle_or_migrated_sell( more_info, )); } - - Err(CustomError::StrategyDoNothing()) } async fn handle_progress_sell( @@ -181,10 +242,13 @@ async fn handle_progress_sell( current_pair_wbnb_balance: U256, strategy: &str, ) -> Result { + // Do nothing after the token is migrated for 3 minutes, + // because many users will sell their token in this period. + // After it, the price may go up if let Some(time_since_migration) = get_diff_time_since_migration(&**db, meme_address).await? { - if time_since_migration < Duration::minutes(2) { + if time_since_migration < Duration::minutes(3) { Logger.process(&format!( - "Strategy {} considering selling: meme address: {} - within 2 minutes after the token migration: do nothing", + "Strategy {} considering selling: meme address: {} - within 3 minutes after the token migration: do nothing", strategy, meme_address )); return Err(CustomError::StrategyDoNothing()); @@ -197,6 +261,11 @@ async fn handle_progress_sell( "Strategy {} considering selling: meme address: {} - (-∞, pancake_sl_wbnb_amount): sell all", strategy, meme_address )); + + // [STRATEGY SELL] + // if: + // - pair bnb balance is less than `pancake_sl_wbnb_amount` + // then sell all the tokens return Ok(build_sell_pending_action( strategy, current_pair_wbnb_balance, @@ -214,6 +283,12 @@ async fn handle_progress_sell( "Strategy {} considering selling: meme address: {} - < 100 WBNB: do nothing", strategy, meme_address )); + + // [STRATEGY SELL] + // if: + // - bot meme balance is less than `initial_fourmeme_buy_amount / 20` --> 5% + // - pair bnb balance is less than `ONE_ETHER * 100` + // then do nothing return Err(CustomError::StrategyDoNothing()); } else { let more_info = format!("{}: pair reaches 100 WBNB, sell all", strategy); @@ -221,6 +296,12 @@ async fn handle_progress_sell( "Strategy {} considering selling: meme address: {} - >= 100 WBNB: sell all", strategy, meme_address )); + + // [STRATEGY SELL] + // if: + // - bot meme balance is greater than or equal to `initial_fourmeme_buy_amount / 20` --> 5% + // - pair bnb balance is greater than or equal to `ONE_ETHER * 100` + // then sell all the tokens return Ok(build_sell_pending_action( strategy, current_pair_wbnb_balance, @@ -237,6 +318,11 @@ async fn handle_progress_sell( "Strategy {} considering selling: meme address: {} - (X, +∞): sell 1%", strategy, meme_address )); + + // [STRATEGY SELL] + // if: + // - bot meme balance is greater than or equal to `initial_fourmeme_buy_amount / 20` --> 5% + // then sell 1% of the tokens return Ok(build_sell_pending_action( strategy, current_pair_wbnb_balance, @@ -266,48 +352,6 @@ fn build_sell_pending_action( } } -pub async fn consider_buying + Clone + Send + Sync + 'static, N: Network>( - db: Arc, - env_config: Arc, - http_provider: P, - meme_address: Address, - block_number: u64, -) -> Result { - let has_bought = check_token_has_bought(&*db, meme_address).await?; - - if has_bought.is_some() { - return Err(CustomError::StrategyDoNothing()); - } - - let bundle_info = get_bundle_info(&*db, meme_address, block_number, env_config.bundle_tx_count).await?; - if let Some((bundle_sender, bundle_block_number)) = bundle_info { - let pair_wbnb_balance = get_pair_wbnb_balance(&http_provider, meme_address).await?; - if pair_wbnb_balance < env_config.pancake_bundle_threshold { - return Err(CustomError::StrategyDoNothing()); - } - - let more_info = format!( - "bundle tx block: {}, bundle sender: {}", - bundle_block_number, bundle_sender - ); - return Ok(PendingAction { - strategy: StrategyType::Bundle.to_string(), - pool_type: PoolType::PancakeV2, - pancake_wbnb_balance: pair_wbnb_balance, - is_buy: true, - meme_address, - meme_amount: U256::from(0), - bnb_amount: env_config.pancake_bundle_buy_amount, - more_info, - }); - } - - Logger.item(&format!("No strategy applied, meme address: {}", meme_address)); - Logger.sub_item(&format!("Block number: {}", block_number)); - - Err(CustomError::StrategyDoNothing()) -} - fn log_no_strategy_applied(meme_address: Address, block_number: u64, tx_index: u64, tx_hash: &str) { Logger.item(&format!("No strategy applied, meme address: {}", meme_address)); Logger.sub_item(&format!("Block number: {}", block_number));