diff --git a/.env.example b/.env.example
index 12a1f74..4e8b0f1 100644
--- a/.env.example
+++ b/.env.example
@@ -3,7 +3,8 @@ BOT_PRIVATE_KEY=
# rpc
BLOXROUTE_AUTH_HEADER=
-WSS_RPC_URL=
+WSS_RPC_URL_FOURMEME=
+WSS_RPC_URL_PANCAKE=
HTTP_RPC_URL=
HTTP_SEND_TX_URL=
diff --git a/src/pvp/config.rs b/src/pvp/config.rs
index a5e8406..e99b5c6 100644
--- a/src/pvp/config.rs
+++ b/src/pvp/config.rs
@@ -67,7 +67,8 @@ pub struct EnvConfig {
// -------- rpc config
/// The WebSocket RPC URL
- pub wss_rpc_url: String,
+ 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
@@ -130,7 +131,8 @@ impl Default for EnvConfig {
tx_simulation: true,
// -------- rpc config
- wss_rpc_url: env::var("WSS_RPC_URL").expect("WSS_RPC_URL not set"),
+ 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"),
bloxroute_auth_header: env::var("BLOXROUTE_AUTH_HEADER").expect("BLOXROUTE_AUTH_HEADER not set"),
@@ -263,8 +265,13 @@ impl EnvConfig {
self
}
- pub fn with_wss_rpc_url(mut self, url: String) -> Self {
- self.wss_rpc_url = url;
+ 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
}
diff --git a/src/pvp/service/auto_sell.rs b/src/pvp/service/auto_sell.rs
index c357fff..dc0251e 100644
--- a/src/pvp/service/auto_sell.rs
+++ b/src/pvp/service/auto_sell.rs
@@ -6,33 +6,35 @@
//! 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.
-use chrono::Duration as ChronoDuration;
-use std::marker::PhantomData;
-use std::time::Duration;
-use std::{str::FromStr, sync::Arc};
+use std::{marker::PhantomData, str::FromStr, sync::Arc, time::Duration};
-use crate::pvp::abi::IERC20::IERC20Instance;
-use crate::pvp::config::EnvConfig;
-use crate::pvp::interface::outer_pool_trader::IOuterPoolTrader;
-use crate::pvp::misc::constant::{ONE_ETHER, WBNB_CONTRACT};
-use crate::pvp::misc::error::CustomError;
-use crate::pvp::misc::logger::Logger;
-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,
-};
-use crate::pvp::trade::outer_pool_trader::OuterPoolTrader;
-use crate::pvp::{interface::db::IDatabase, misc::constant::BSC_CHAIN_ID};
-use alloy::primitives::{Address, U256};
use alloy::{
network::Network,
+ primitives::{Address, U256},
providers::{Provider, WalletProvider},
rpc::types::{TransactionReceipt, TransactionRequest},
};
-use chrono::Utc;
+use chrono::{Duration as ChronoDuration, Utc};
use harpoon::bloXroute_private_tx::BloXrouteService;
use tokio::{sync::Mutex, time};
+use crate::pvp::{
+ abi::IERC20::IERC20Instance,
+ config::EnvConfig,
+ db::db::TradeInfo,
+ interface::{db::IDatabase, outer_pool_trader::IOuterPoolTrader},
+ misc::{
+ constant::{BSC_CHAIN_ID, ONE_ETHER, WBNB_CONTRACT},
+ error::CustomError,
+ logger::Logger,
+ },
+ strategy::helper::{
+ StrategyType, get_diff_time_since_migration, get_initial_fourmeme_buy_amount, get_pair_wbnb_balance,
+ get_wbnb_amount_when_bought_token,
+ },
+ trade::outer_pool_trader::OuterPoolTrader,
+};
+
pub struct AutoSeller
{
db: Arc,
http_provider: P,
@@ -68,6 +70,7 @@ where
}
}
+ /// Entry point of the auto sell service
pub async fn start(&self) {
let mut interval = time::interval(Duration::from_secs(self.interval_seconds));
@@ -75,221 +78,225 @@ where
interval.tick().await;
let all_bought_tokens = self.db.get_all_token_addresses(BSC_CHAIN_ID).await.unwrap_or_default();
+
for token in all_bought_tokens {
let meme_token = Address::from_str(&token).unwrap_or_default();
- match self.sell_token(meme_token).await {
- Ok(_) => {
- // prevent bloxroute rate limite
- // known issue: it will conflict with the executor module when we use private transactions type
- time::sleep(Duration::from_secs(5)).await;
- }
- Err(_) => {}
+
+ if let Ok(_) = self.sell_token(meme_token).await {
+ // prevent bloxroute rate limit
+ // known issue: it will conflict with the executor module when we use private transactions type
+ time::sleep(Duration::from_secs(5)).await;
}
}
}
}
+ /// Sell meme tokens based on the strategy
async fn sell_token(&self, meme_token: Address) -> Result<(), CustomError> {
- // 1. get the latest trade info of that token
let latest_trade_info = self
.db
.get_latest_trade_info(BSC_CHAIN_ID, &meme_token.to_string())
.await
.unwrap_or_default();
- // 2. check if the latest trade has exceeded interval_seconds in the past, no trade also counts as exceeding interval_seconds.
- let now = Utc::now();
- let exceeded_one_minute = match latest_trade_info.clone() {
- None => true, // no trade also counts as exceeding interval_seconds
- Some(trade) => {
- let duration = now.signed_duration_since(trade.updated_at);
- duration.num_seconds() >= 60
- }
- };
- if !exceeded_one_minute {
+ // check we have passed the time threshold
+ if !self.has_exceeded_time_threshold(&latest_trade_info).await {
return Err(CustomError::StrategyDoNothing());
}
- // 3. check if the bot still holds the token
- let erc20_contract = IERC20Instance::new(meme_token, self.http_provider.clone());
- let bot_meme_balance = erc20_contract
- .balanceOf(self.env_config.bot_address)
- .call()
- .await
- .unwrap_or_default();
+ // check the bot's meme balance
+ let bot_meme_balance = self.get_bot_meme_balance(meme_token).await?;
if bot_meme_balance == U256::from(0) {
return Err(CustomError::StrategyDoNothing());
}
- // 4. get the current pair's wbnb balance
+ // check the current pair's wbnb balance
let current_pair_wbnb_balance = get_pair_wbnb_balance(&self.http_provider, meme_token)
.await
.unwrap_or_default();
- // the pair's wbnb balance is 0, it means it is still in the fourmeme stage, so skip it
if current_pair_wbnb_balance == U256::from(0) {
return Err(CustomError::StrategyDoNothing());
}
- // 4. different strategies, different sell logic
let strategy = latest_trade_info.map(|trade| trade.strategy).unwrap_or_default();
match strategy.as_str() {
s if s == StrategyType::Bundle.to_string() || s == StrategyType::TokenMigrated.to_string() => {
- let wbnb_amount_when_bought = get_wbnb_amount_when_bought_token(&*self.db, meme_token)
+ self.handle_bundle_or_migrated_strategy(
+ meme_token,
+ bot_meme_balance,
+ current_pair_wbnb_balance,
+ &strategy,
+ )
+ .await
+ }
+ s if s == StrategyType::Progress.to_string() => {
+ self.handle_progress_strategy(meme_token, bot_meme_balance, current_pair_wbnb_balance, &strategy)
.await
- .unwrap_or_default();
-
- if current_pair_wbnb_balance < wbnb_amount_when_bought - U256::from(3) * ONE_ETHER {
- let mut trader = self.outer_pool_trader.lock().await;
- trader
- .sell_token(
- strategy.clone(),
- &self.bloxroute_service,
- self.env_config.tx_submit_type,
- meme_token,
- WBNB_CONTRACT,
- bot_meme_balance,
- self.env_config.slippage,
- )
- .await
- .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
-
- Logger.event(&format!("Auto sell token {}: sell all", meme_token));
- return Ok(());
- } else if current_pair_wbnb_balance > wbnb_amount_when_bought {
- let initial_fourmeme_buy_amount = get_initial_fourmeme_buy_amount(&*self.db, meme_token)
- .await
- .unwrap_or_default();
- let five_percentage = initial_fourmeme_buy_amount / U256::from(20);
+ }
+ _ => Err(CustomError::StrategyDoNothing()),
+ }
+ }
- if bot_meme_balance < five_percentage {
- if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
- return Err(CustomError::StrategyDoNothing());
- } else {
- let mut trader = self.outer_pool_trader.lock().await;
- trader
- .sell_token(
- strategy.clone(),
- &self.bloxroute_service,
- self.env_config.tx_submit_type,
- meme_token,
- WBNB_CONTRACT,
- bot_meme_balance,
- self.env_config.slippage,
- )
- .await
- .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
+ async fn handle_bundle_or_migrated_strategy(
+ &self,
+ meme_token: Address,
+ bot_meme_balance: U256,
+ current_pair_wbnb_balance: U256,
+ strategy: &str,
+ ) -> Result<(), CustomError> {
+ let wbnb_amount_when_bought = get_wbnb_amount_when_bought_token(&*self.db, meme_token)
+ .await
+ .unwrap_or_default();
- Logger.event(&format!(
- "Auto sell token {}: pair reaches 100 WBNB, sell all",
- meme_token
- ));
- return Ok(());
- }
- }
+ if current_pair_wbnb_balance < wbnb_amount_when_bought - U256::from(3) * ONE_ETHER {
+ self.sell_all_tokens(meme_token, bot_meme_balance, strategy, "sell all")
+ .await
+ } else if current_pair_wbnb_balance > wbnb_amount_when_bought {
+ let initial_fourmeme_buy_amount = get_initial_fourmeme_buy_amount(&*self.db, meme_token)
+ .await
+ .unwrap_or_default();
+ let five_percentage = initial_fourmeme_buy_amount / U256::from(20);
- let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(50);
- let mut trader = self.outer_pool_trader.lock().await;
- trader
- .sell_token(
- strategy.clone(),
- &self.bloxroute_service,
- self.env_config.tx_submit_type,
+ if bot_meme_balance < five_percentage {
+ if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
+ return Err(CustomError::StrategyDoNothing());
+ } else {
+ return self
+ .sell_all_tokens(
meme_token,
- WBNB_CONTRACT,
- meme_amount_to_sell,
- self.env_config.slippage,
+ bot_meme_balance,
+ strategy,
+ "pair reaches 100 WBNB, sell all",
)
- .await
- .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
-
- Logger.event(&format!("Auto sell token {}: (X, +∞): sell 2%", meme_token));
- return Ok(());
+ .await;
}
+ }
+ let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(50);
+ self.sell_partial_tokens(meme_token, meme_amount_to_sell, strategy, "(X, +∞): sell 2%")
+ .await
+ } else {
+ Err(CustomError::StrategyDoNothing())
+ }
+ }
+
+ async fn handle_progress_strategy(
+ &self,
+ meme_token: Address,
+ bot_meme_balance: U256,
+ current_pair_wbnb_balance: U256,
+ strategy: &str,
+ ) -> Result<(), CustomError> {
+ if let Some(time_since_migration) = get_diff_time_since_migration(&*self.db, meme_token)
+ .await
+ .unwrap_or_default()
+ {
+ if time_since_migration < ChronoDuration::minutes(2) {
return Err(CustomError::StrategyDoNothing());
}
- s if s == StrategyType::Progress.to_string() => {
- // we don't trade within 2 minutes after the token migration
- if let Some(time_since_migration) = get_diff_time_since_migration(&*self.db, meme_token)
- .await
- .unwrap_or_default()
- {
- if time_since_migration < ChronoDuration::minutes(2) {
- return Err(CustomError::StrategyDoNothing());
- }
- }
+ }
- if current_pair_wbnb_balance < U256::from(self.env_config.pancake_sl_wbnb_amount) {
- let mut trader = self.outer_pool_trader.lock().await;
- trader
- .sell_token(
- strategy.clone(),
- &self.bloxroute_service,
- self.env_config.tx_submit_type,
- meme_token,
- WBNB_CONTRACT,
- bot_meme_balance,
- self.env_config.slippage,
- )
- .await
- .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
+ if current_pair_wbnb_balance < U256::from(self.env_config.pancake_sl_wbnb_amount) {
+ self.sell_all_tokens(
+ meme_token,
+ bot_meme_balance,
+ strategy,
+ "(-∞, pancake_sl_wbnb_amount): sell all",
+ )
+ .await
+ } else {
+ let initial_fourmeme_buy_amount = get_initial_fourmeme_buy_amount(&*self.db, meme_token)
+ .await
+ .unwrap_or_default();
+ let five_percentage = initial_fourmeme_buy_amount / U256::from(20);
- Logger.event(&format!(
- "Auto sell token {}: (-∞, pancake_sl_wbnb_amount): sell all",
- meme_token
- ));
- return Ok(());
+ if bot_meme_balance < five_percentage {
+ if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
+ return Err(CustomError::StrategyDoNothing());
} else {
- let initial_fourmeme_buy_amount = get_initial_fourmeme_buy_amount(&*self.db, meme_token)
- .await
- .unwrap_or_default();
- let five_percentage = initial_fourmeme_buy_amount / U256::from(20);
-
- if bot_meme_balance < five_percentage {
- if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
- return Err(CustomError::StrategyDoNothing());
- } else {
- let mut trader = self.outer_pool_trader.lock().await;
- trader
- .sell_token(
- strategy.clone(),
- &self.bloxroute_service,
- self.env_config.tx_submit_type,
- meme_token,
- WBNB_CONTRACT,
- bot_meme_balance,
- self.env_config.slippage,
- )
- .await
- .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
-
- Logger.event(&format!("Auto sell token {}: >= 100 WBNB: sell all", meme_token));
- return Ok(());
- }
- }
-
- let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(50);
- let mut trader = self.outer_pool_trader.lock().await;
- trader
- .sell_token(
- strategy.clone(),
- &self.bloxroute_service,
- self.env_config.tx_submit_type,
- meme_token,
- WBNB_CONTRACT,
- meme_amount_to_sell,
- self.env_config.slippage,
- )
- .await
- .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
-
- Logger.event(&format!("Auto sell token {}: (X, +∞): sell 2%", meme_token));
- return Ok(());
+ return self
+ .sell_all_tokens(meme_token, bot_meme_balance, strategy, ">= 100 WBNB: sell all")
+ .await;
}
}
- _ => {
- return Err(CustomError::StrategyDoNothing());
+
+ let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(50);
+ self.sell_partial_tokens(meme_token, meme_amount_to_sell, strategy, "(X, +∞): sell 2%")
+ .await
+ }
+ }
+
+ /// Helper function: check if we have passed the time threshold
+ async fn has_exceeded_time_threshold(&self, latest_trade_info: &Option) -> bool {
+ let now = Utc::now();
+ match latest_trade_info {
+ None => true,
+ Some(trade) => {
+ let duration = now.signed_duration_since(trade.updated_at);
+ duration.num_seconds() >= 60
}
}
}
+
+ /// Helper function: get the bot's meme balance
+ async fn get_bot_meme_balance(&self, meme_token: Address) -> Result {
+ let erc20_contract = IERC20Instance::new(meme_token, self.http_provider.clone());
+ erc20_contract
+ .balanceOf(self.env_config.bot_address)
+ .call()
+ .await
+ .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))
+ }
+
+ /// Helper function: sell all the tokens
+ async fn sell_all_tokens(
+ &self,
+ meme_token: Address,
+ bot_meme_balance: U256,
+ strategy: &str,
+ log_message: &str,
+ ) -> Result<(), CustomError> {
+ let mut trader = self.outer_pool_trader.lock().await;
+ trader
+ .sell_token(
+ strategy.to_string(),
+ &self.bloxroute_service,
+ self.env_config.tx_submit_type,
+ meme_token,
+ WBNB_CONTRACT,
+ bot_meme_balance,
+ self.env_config.slippage,
+ )
+ .await
+ .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
+
+ Logger.event(&format!("Auto sell token {}: {}", meme_token, log_message));
+ Ok(())
+ }
+
+ /// Helper function: sell partial tokens
+ async fn sell_partial_tokens(
+ &self,
+ meme_token: Address,
+ amount_to_sell: U256,
+ strategy: &str,
+ log_message: &str,
+ ) -> Result<(), CustomError> {
+ let mut trader = self.outer_pool_trader.lock().await;
+ trader
+ .sell_token(
+ strategy.to_string(),
+ &self.bloxroute_service,
+ self.env_config.tx_submit_type,
+ meme_token,
+ WBNB_CONTRACT,
+ amount_to_sell,
+ self.env_config.slippage,
+ )
+ .await
+ .map_err(|e| CustomError::TxExecutionFailed(e.to_string()))?;
+
+ Logger.event(&format!("Auto sell token {}: {}", meme_token, log_message));
+ Ok(())
+ }
}
diff --git a/src/pvp/start.rs b/src/pvp/start.rs
index 4ad2734..ff076f4 100644
--- a/src/pvp/start.rs
+++ b/src/pvp/start.rs
@@ -114,7 +114,14 @@ 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".to_string(), env_config.wss_rpc_url.clone()),
+ (
+ "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()),
// -------- database config
@@ -209,10 +216,15 @@ pub async fn run() -> eyre::Result<()> {
// 4. Setup providers
let wallet = alloy::network::EthereumWallet::from(signer.clone());
- let wss_provider = ProviderBuilder::new()
+ let wss_provider_fourmeme = ProviderBuilder::new()
.wallet(wallet.clone())
- .connect_ws(WsConnect::new(&env_config.wss_rpc_url))
+ .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()?);
@@ -228,8 +240,11 @@ pub async fn run() -> eyre::Result<()> {
));
// 6. Create contract instances
- let fourmeme_contract = Arc::new(FourmemeInstance::new(FOURMEME_CONTRACT, wss_provider.clone()));
- let pancake_router_contract = Arc::new(PancakeSwapRouterInstance::new(PANCAKESWAP_ROUTER, wss_provider.clone()));
+ 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(),
+ ));
// 7. Create nonce manager for Private transaction mode
let nonce_manager: Option>> = if env_config.tx_submit_type == TxSubmitType::Private {
@@ -303,11 +318,11 @@ pub async fn run() -> eyre::Result<()> {
.from_block(BlockNumberOrTag::Latest);
engine.add_collector(map_collector!(
- LogCollector::new(Arc::new(wss_provider.clone()), fourmeme_filter),
+ LogCollector::new(Arc::new(wss_provider_fourmeme.clone()), fourmeme_filter),
Event::FourmemeLog
));
engine.add_collector(map_collector!(
- LogCollector::new(Arc::new(wss_provider.clone()), pancake_filter),
+ LogCollector::new(Arc::new(wss_provider_pancake.clone()), pancake_filter),
Event::PancakeSwapLog
));
diff --git a/src/pvp/strategy/strategy_fourmeme.rs b/src/pvp/strategy/strategy_fourmeme.rs
index 40bba1b..9207c1a 100644
--- a/src/pvp/strategy/strategy_fourmeme.rs
+++ b/src/pvp/strategy/strategy_fourmeme.rs
@@ -40,7 +40,6 @@ pub async fn on_fourmeme_token_migrated_event<
) -> Result<(), CustomError> {
Logger.event(&format!("Token migrated event: {}", meme_address));
- // calculate the pair address using CREATE2, and add to tracking list
let pair_address = calculate_pair_address(
meme_address,
WBNB_CONTRACT,
@@ -48,59 +47,42 @@ pub async fn on_fourmeme_token_migrated_event<
PANCAKESWAP_INIT_CODE_HASH,
);
- // if the pair info is not in the database, then insert it
- let pair_info = db
- .get_migrated_token_info(BSC_CHAIN_ID, pair_address.to_string().as_str())
- .await
- .map_err(|e| CustomError::Database(format!("Get migrated token info failed: {}", e).into()))?;
- if pair_info.is_none() {
- let reserve0_address = meme_address.min(WBNB_CONTRACT);
- let reserve1_address = meme_address.max(WBNB_CONTRACT);
- db.insert_migrated_token_info(&MigratedTokenInfo {
- chain_id: BSC_CHAIN_ID,
- reserve0_address: reserve0_address.to_string(),
- reserve1_address: reserve1_address.to_string(),
- pair_address: pair_address.to_string(),
- updated_at: chrono::Utc::now(),
- })
- .await
- .map_err(|e| CustomError::Database(format!("Insert migrated token info failed: {}", e).into()))?;
- }
+ ensure_pair_info_in_db(&db, pair_address, meme_address).await?;
+ // [STRATEGY TOKEN MIGRATED]
+ // if:
+ // - 2 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();
let pancake_model_b_threshold = env_config.meme_migrated_model_b_threshold;
let meme_migrated_model_b_buy_amount = env_config.meme_migrated_model_b_buy_amount;
tokio::spawn(async move {
Logger.event(&format!(
- "Token migrated event: {}, sleep for 2 minutes to avoid RugPull",
+ "Token migrated event: {}, sleep for 2.5 minutes to avoid RugPull",
meme_address
));
- // sleep for 2.5 minutes to avoid RugPull
tokio::time::sleep(Duration::from_secs(150)).await;
- // [STRATEGY TOKEN MIGRATED]
- // if:
- // - 2 minutes after the token is migrated
- // - the pair's wbnb balance is greater than `meme_migrated_model_b_threshold`
- // then buy the token
- let pair_wbnb_balance = get_pair_wbnb_balance(&http_provider_clone, meme_address).await.unwrap();
- Logger.event(&format!(
- "Token migrated, pair wbnb balance: {}, meme address: {}",
- pair_wbnb_balance, meme_address
- ));
- if pair_wbnb_balance > pancake_model_b_threshold {
- Logger.event(&format!("Token migrated, buy token, meme address: {}", meme_address));
- let pending_action = PendingAction {
- strategy: StrategyType::TokenMigrated.to_string(),
- pool_type: PoolType::PancakeV2,
- pancake_wbnb_balance: pair_wbnb_balance,
- is_buy: true,
- meme_address,
- meme_amount: U256::from(0),
- bnb_amount: meme_migrated_model_b_buy_amount,
- more_info: format!("Migrated"),
- };
- submitter.submit(pending_action);
+ if let Ok(pair_wbnb_balance) = get_pair_wbnb_balance(&http_provider_clone, meme_address).await {
+ Logger.event(&format!(
+ "Token migrated, pair wbnb balance: {}, meme address: {}",
+ pair_wbnb_balance, meme_address
+ ));
+ if pair_wbnb_balance > pancake_model_b_threshold {
+ Logger.event(&format!("Token migrated, buy token, meme address: {}", meme_address));
+ let pending_action = PendingAction {
+ strategy: StrategyType::TokenMigrated.to_string(),
+ pool_type: PoolType::PancakeV2,
+ pancake_wbnb_balance: pair_wbnb_balance,
+ is_buy: true,
+ meme_address,
+ meme_amount: U256::from(0),
+ bnb_amount: meme_migrated_model_b_buy_amount,
+ more_info: "Migrated".to_string(),
+ };
+ submitter.submit(pending_action);
+ }
}
});
@@ -121,7 +103,7 @@ pub async fn consider_selling + Clone + Send + Sync
) -> Result {
let has_bought = check_token_has_bought(&*db, meme_address).await?;
- if has_bought.is_some() {
+ if let Some(_) = has_bought {
if bnb_amount < env_config.user_act_bnb_amount {
return Err(CustomError::StrategyDoNothing());
}
@@ -132,23 +114,20 @@ pub async fn consider_selling + Clone + Send + Sync
if bot_meme_balance == U256::from(0) {
return Err(CustomError::StrategyDoNothing());
}
- let pending_action = PendingAction {
- strategy: StrategyType::Bundle.to_string(),
- pool_type: PoolType::Fourmeme,
- pancake_wbnb_balance: U256::from(0),
- is_buy: false,
+
+ let more_info = format!("curve progress: {}", curve_progress);
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {}, bot meme balance: {}, curve progress: {} - sell all",
+ StrategyType::Bundle.to_string(),
meme_address,
- meme_amount: bot_meme_balance,
- bnb_amount: U256::from(0),
- more_info: format!("curve progress: {}", curve_progress),
- };
- Logger.process(&format!("Strategy {} considering selling: meme address: {}, bot meme balance: {}, curve progress: {} - sell all", StrategyType::Bundle.to_string(), meme_address, bot_meme_balance, curve_progress));
- return Ok(pending_action);
+ bot_meme_balance,
+ curve_progress
+ ));
+ return Ok(build_sell_pending_action(meme_address, bot_meme_balance, more_info));
}
}
if is_buy {
- // we apply none strategy, then add market trading record to the database
db.insert_market_trade_info(&MarketTradeInfo {
chain_id: BSC_CHAIN_ID,
block_number,
@@ -162,10 +141,7 @@ pub async fn consider_selling + Clone + Send + Sync
.map_err(|e| CustomError::Database(format!("Insert market trade info failed: {}", e).into()))?;
}
- Logger.item(&format!("No strategy applied, meme address: {}", meme_address));
- Logger.sub_item(&format!("Tx hash: {}", tx_hash));
- Logger.sub_item(&format!("Block number: {}", block_number));
- Logger.sub_item(&format!("Tx index: {}", tx_index));
+ log_no_strategy_applied(meme_address, block_number, tx_index, &tx_hash);
Err(CustomError::StrategyDoNothing())
}
@@ -177,56 +153,37 @@ pub async fn consider_buying + Clone + Send + Sync
meme_address: Address,
block_number: u64,
) -> Result {
- // if we have bought that token before, we should no longer buy it again
let has_bought = check_token_has_bought(&*db, meme_address).await?;
-
if has_bought.is_some() {
return Err(CustomError::StrategyDoNothing());
}
- // [STRATEGY CURVE PROGRESS]
- // if:
- // - the curve progress is greater than `fourmeme_progress_curve_threshold`
- // then buy the token
let curve_progress = cal_curve_progress(meme_address, &http_provider).await?;
if curve_progress > env_config.fourmeme_progress_buy_curve_threshold {
let pair_wbnb_balance = get_pair_wbnb_balance(&http_provider, meme_address).await?;
- let pending_action = PendingAction {
- strategy: StrategyType::Progress.to_string(),
- pool_type: PoolType::Fourmeme,
- pancake_wbnb_balance: pair_wbnb_balance,
- is_buy: true,
+ let more_info = format!("curve progress: {}", curve_progress);
+ return Ok(build_progress_buy_pending_action(
meme_address,
- meme_amount: U256::from(0),
- bnb_amount: env_config.fourmeme_progress_buy_amount,
- more_info: format!("curve progress: {}", curve_progress),
- };
- return Ok(pending_action);
+ pair_wbnb_balance,
+ env_config.fourmeme_progress_buy_amount,
+ more_info,
+ ));
}
- // [STRATEGY BUNDLE]
- // if:
- // - there is a bundle transaction
- // - the curve progress is greater than `fourmeme_bundle_threshold`
- // then buy the token
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 {
if curve_progress > env_config.fourmeme_bundle_threshold {
let pair_wbnb_balance = get_pair_wbnb_balance(&http_provider, meme_address).await?;
- let pending_action = PendingAction {
- strategy: StrategyType::Bundle.to_string(),
- pool_type: PoolType::Fourmeme,
- pancake_wbnb_balance: pair_wbnb_balance,
- is_buy: true,
+ let more_info = format!(
+ "bundle tx block: {}, bundle sender: {}",
+ bundle_block_number, bundle_sender
+ );
+ return Ok(build_bundle_buy_pending_action(
meme_address,
- meme_amount: U256::from(0),
- bnb_amount: env_config.fourmeme_bundle_buy_amount,
- more_info: format!(
- "bundle tx block: {}, bundle sender: {}",
- bundle_block_number, bundle_sender
- ),
- };
- return Ok(pending_action);
+ pair_wbnb_balance,
+ env_config.fourmeme_bundle_buy_amount,
+ more_info,
+ ));
}
}
@@ -235,3 +192,92 @@ pub async fn consider_buying + Clone + Send + Sync
Err(CustomError::StrategyDoNothing())
}
+
+/// Helper function: ensure the pair info is in the database.
+/// If the pair info is not in the database, insert it
+async fn ensure_pair_info_in_db(
+ db: &Arc,
+ pair_address: Address,
+ meme_address: Address,
+) -> Result<(), CustomError> {
+ let pair_info = db
+ .get_migrated_token_info(BSC_CHAIN_ID, pair_address.to_string().as_str())
+ .await
+ .map_err(|e| CustomError::Database(format!("Get migrated token info failed: {}", e).into()))?;
+
+ if pair_info.is_none() {
+ let reserve0_address = meme_address.min(WBNB_CONTRACT);
+ let reserve1_address = meme_address.max(WBNB_CONTRACT);
+ db.insert_migrated_token_info(&MigratedTokenInfo {
+ chain_id: BSC_CHAIN_ID,
+ reserve0_address: reserve0_address.to_string(),
+ reserve1_address: reserve1_address.to_string(),
+ pair_address: pair_address.to_string(),
+ updated_at: chrono::Utc::now(),
+ })
+ .await
+ .map_err(|e| CustomError::Database(format!("Insert migrated token info failed: {}", e).into()))?;
+ }
+
+ Ok(())
+}
+
+/// Helper function: build the sell pending action
+fn build_sell_pending_action(meme_address: Address, meme_amount: U256, more_info: String) -> PendingAction {
+ PendingAction {
+ strategy: StrategyType::Bundle.to_string(),
+ pool_type: PoolType::Fourmeme,
+ pancake_wbnb_balance: U256::from(0),
+ is_buy: false,
+ meme_address,
+ meme_amount,
+ bnb_amount: U256::from(0),
+ more_info,
+ }
+}
+
+/// Helper function: build the progress buy pending action
+fn build_progress_buy_pending_action(
+ meme_address: Address,
+ pancake_wbnb_balance: U256,
+ bnb_amount: U256,
+ more_info: String,
+) -> PendingAction {
+ PendingAction {
+ strategy: StrategyType::Progress.to_string(),
+ pool_type: PoolType::Fourmeme,
+ pancake_wbnb_balance,
+ is_buy: true,
+ meme_address,
+ meme_amount: U256::from(0),
+ bnb_amount,
+ more_info,
+ }
+}
+
+/// Helper function: build the bundle buy pending action
+fn build_bundle_buy_pending_action(
+ meme_address: Address,
+ pancake_wbnb_balance: U256,
+ bnb_amount: U256,
+ more_info: String,
+) -> PendingAction {
+ PendingAction {
+ strategy: StrategyType::Bundle.to_string(),
+ pool_type: PoolType::Fourmeme,
+ pancake_wbnb_balance,
+ is_buy: true,
+ meme_address,
+ meme_amount: U256::from(0),
+ bnb_amount,
+ more_info,
+ }
+}
+
+/// Helper function: log the no strategy applied
+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!("Tx hash: {}", tx_hash));
+ Logger.sub_item(&format!("Block number: {}", block_number));
+ Logger.sub_item(&format!("Tx index: {}", tx_index));
+}
diff --git a/src/pvp/strategy/strategy_pancake.rs b/src/pvp/strategy/strategy_pancake.rs
index 32c3012..82e5cbf 100644
--- a/src/pvp/strategy/strategy_pancake.rs
+++ b/src/pvp/strategy/strategy_pancake.rs
@@ -36,210 +36,234 @@ pub async fn consider_selling + Clone + Send + Sync
block_number: u64,
tx_index: u64,
) -> Result {
- // if we have bought that token before, we should no longer buy it again
let has_bought = check_token_has_bought(&*db, meme_address).await?;
- if has_bought.is_some() {
- // check if the bnb amount is greater than the user act bnb amount
+ if let Some(strategy) = has_bought {
if bnb_amount < env_config.user_act_bnb_amount {
return Err(CustomError::StrategyDoNothing());
}
- // check if the internal time interval has passed
let is_pass_internal = is_pass_internal(&*db, meme_address, env_config.internal_each_trade).await?;
if !is_pass_internal {
return Err(CustomError::StrategyDoNothing());
}
- // get the bot's meme balance
let bot_meme_balance = get_bot_meme_balance(&http_provider, meme_address, env_config.bot_address).await?;
if bot_meme_balance == U256::from(0) {
return Err(CustomError::StrategyDoNothing());
}
- // get the current pair's wbnb balance
let current_pair_wbnb_balance = get_pair_wbnb_balance(&http_provider, meme_address).await?;
- let strategy = has_bought.clone().unwrap_or_default();
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {}, bot meme balance: {}, current pair wbnb balance: {}",
+ strategy, meme_address, bot_meme_balance, current_pair_wbnb_balance
+ ));
match strategy.as_str() {
s if s == StrategyType::Bundle.to_string() || s == StrategyType::TokenMigrated.to_string() => {
- // [STRATEGY BUNDLE OR TOKEN MIGRATED]
- // - (-∞, X-3): sell all
- // - (X, +∞): sell 1% of initial bought amount each trade; do nothing if only left 5% of the initial bought
- // amount, sell all when the pair's wbnb balance is reached 100 WBNB
- Logger.process(&format!("Strategy {} considering selling: meme address: {}, bot meme balance: {}, current pair wbnb balance: {}", strategy, meme_address, bot_meme_balance, current_pair_wbnb_balance));
+ return handle_bundle_or_migrated_sell(
+ &db,
+ meme_address,
+ bot_meme_balance,
+ current_pair_wbnb_balance,
+ &strategy,
+ )
+ .await;
+ }
+ s if s == StrategyType::Progress.to_string() => {
+ return handle_progress_sell(
+ &db,
+ &env_config,
+ meme_address,
+ bot_meme_balance,
+ current_pair_wbnb_balance,
+ &strategy,
+ )
+ .await;
+ }
+ _ => {}
+ }
+ }
+
+ if is_buy {
+ (&*db)
+ .insert_market_trade_info(&MarketTradeInfo {
+ chain_id: BSC_CHAIN_ID,
+ block_number,
+ tx_index,
+ tx_hash: tx_hash.clone(),
+ token_address: meme_address.to_string(),
+ updated_at: chrono::Utc::now(),
+ tx_sender: tx_sender.to_string(),
+ })
+ .await
+ .map_err(|e| CustomError::Database(format!("Insert market trade info failed: {}", e).into()))?;
+ }
+
+ log_no_strategy_applied(meme_address, block_number, tx_index, &tx_hash);
- let wbnb_amount_when_bought = get_wbnb_amount_when_bought_token(&*db, meme_address).await?;
+ Err(CustomError::StrategyDoNothing())
+}
- if current_pair_wbnb_balance < wbnb_amount_when_bought - U256::from(3) * ONE_ETHER {
- let pending_action = PendingAction {
- strategy: strategy.clone(),
- pool_type: PoolType::PancakeV2,
- pancake_wbnb_balance: current_pair_wbnb_balance,
- is_buy: false,
- meme_address,
- meme_amount: bot_meme_balance,
- bnb_amount: U256::from(0),
- more_info: format!("{} (-∞, X-3): sell all", strategy),
- };
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} - (-∞, X-3): sell all",
- strategy, meme_address
- ));
- return Ok(pending_action);
- } else if current_pair_wbnb_balance > wbnb_amount_when_bought {
- let initial_fourmeme_buy_amount = get_initial_fourmeme_buy_amount(&*db, meme_address).await?;
- let five_percentage = initial_fourmeme_buy_amount / U256::from(20);
+async fn handle_bundle_or_migrated_sell(
+ db: &Arc,
+ meme_address: Address,
+ bot_meme_balance: U256,
+ current_pair_wbnb_balance: U256,
+ strategy: &str,
+) -> Result {
+ let wbnb_amount_when_bought = get_wbnb_amount_when_bought_token(&**db, meme_address).await?;
- if bot_meme_balance < five_percentage {
- if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} < 100 WBNB: do nothing",
- strategy, meme_address
- ));
- return Err(CustomError::StrategyDoNothing());
- } else {
- let pending_action = PendingAction {
- strategy: strategy.clone(),
- pool_type: PoolType::PancakeV2,
- pancake_wbnb_balance: current_pair_wbnb_balance,
- is_buy: false,
- meme_address,
- meme_amount: bot_meme_balance,
- bnb_amount: U256::from(0),
- more_info: format!("{}: pair reaches 100 WBNB, sell all", strategy),
- };
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} >= 100 WBNB: sell all",
- strategy, meme_address
- ));
- return Ok(pending_action);
- }
- }
+ if current_pair_wbnb_balance < wbnb_amount_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",
+ strategy, meme_address
+ ));
+ return Ok(build_sell_pending_action(
+ strategy,
+ current_pair_wbnb_balance,
+ meme_address,
+ bot_meme_balance,
+ more_info,
+ ));
+ } else if current_pair_wbnb_balance > wbnb_amount_when_bought {
+ let initial_fourmeme_buy_amount = get_initial_fourmeme_buy_amount(&**db, meme_address).await?;
+ let five_percentage = initial_fourmeme_buy_amount / U256::from(20);
- let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(100);
- let pending_action = PendingAction {
- strategy: strategy.clone(),
- pool_type: PoolType::PancakeV2,
- pancake_wbnb_balance: current_pair_wbnb_balance,
- is_buy: false,
- meme_address,
- meme_amount: meme_amount_to_sell,
- bnb_amount: U256::from(0),
- more_info: format!("{} (X, +∞): sell 1%", strategy),
- };
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} - (X, +∞): sell 1%",
- strategy, meme_address
- ));
- return Ok(pending_action);
- }
+ if bot_meme_balance < five_percentage {
+ if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} < 100 WBNB: do nothing",
+ strategy, meme_address
+ ));
+ return Err(CustomError::StrategyDoNothing());
+ } else {
+ let more_info = format!("{}: pair reaches 100 WBNB, sell all", strategy);
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} >= 100 WBNB: sell all",
+ strategy, meme_address
+ ));
+ return Ok(build_sell_pending_action(
+ strategy,
+ current_pair_wbnb_balance,
+ meme_address,
+ bot_meme_balance,
+ more_info,
+ ));
}
- s if s == StrategyType::Progress.to_string() => {
- // [STRATEGY PROGRESS]
- // - (-∞, pancake_sl_wbnb_amount): sell all
- // - (pancake_sl_wbnb_amount, +∞): sell 1% of initial bought amount each trade; do nothing if
- // only left 5% of the initial bought amount, sell all when the pair's wbnb balance is reached 100 WBNB
- Logger.process(&format!("Strategy {} considering selling: meme address: {}, bot meme balance: {}, current pair wbnb balance: {}", strategy, meme_address, bot_meme_balance, current_pair_wbnb_balance));
+ }
- // we don't trade within 2 minutes after the token migration
- if let Some(time_since_migration) = get_diff_time_since_migration(&*db, meme_address).await? {
- if time_since_migration < Duration::minutes(2) {
- Logger.process(&format!("Strategy {} considering selling: meme address: {} - within 2 minutes after the token migration: do nothing", strategy, meme_address));
- return Err(CustomError::StrategyDoNothing());
- }
- }
+ let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(100);
+ let more_info = format!("{} (X, +∞): sell 1%", strategy);
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} - (X, +∞): sell 1%",
+ strategy, meme_address
+ ));
+ return Ok(build_sell_pending_action(
+ strategy,
+ current_pair_wbnb_balance,
+ meme_address,
+ meme_amount_to_sell,
+ more_info,
+ ));
+ }
+
+ Err(CustomError::StrategyDoNothing())
+}
- if current_pair_wbnb_balance < U256::from(env_config.pancake_sl_wbnb_amount) {
- let pending_action = PendingAction {
- strategy: StrategyType::Progress.to_string(),
- pool_type: PoolType::PancakeV2,
- pancake_wbnb_balance: current_pair_wbnb_balance,
- is_buy: false,
- meme_address,
- meme_amount: bot_meme_balance,
- bnb_amount: U256::from(0),
- more_info: format!("{}: sell all", strategy),
- };
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} - (-∞, pancake_sl_wbnb_amount): sell all",
- strategy, meme_address
- ));
- return Ok(pending_action);
- } 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);
+async fn handle_progress_sell(
+ db: &Arc,
+ env_config: &Arc,
+ meme_address: Address,
+ bot_meme_balance: U256,
+ current_pair_wbnb_balance: U256,
+ strategy: &str,
+) -> Result {
+ if let Some(time_since_migration) = get_diff_time_since_migration(&**db, meme_address).await? {
+ if time_since_migration < Duration::minutes(2) {
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} - within 2 minutes after the token migration: do nothing",
+ strategy, meme_address
+ ));
+ return Err(CustomError::StrategyDoNothing());
+ }
+ }
- if bot_meme_balance < five_percentage {
- if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} - < 100 WBNB: do nothing",
- strategy, meme_address
- ));
- return Err(CustomError::StrategyDoNothing());
- } else {
- let pending_action = PendingAction {
- strategy: strategy.clone(),
- pool_type: PoolType::PancakeV2,
- pancake_wbnb_balance: current_pair_wbnb_balance,
- is_buy: false,
- meme_address,
- meme_amount: bot_meme_balance,
- bnb_amount: U256::from(0),
- more_info: format!("{}: pair reaches 100 WBNB, sell all", strategy),
- };
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} - >= 100 WBNB: sell all",
- strategy, meme_address
- ));
- return Ok(pending_action);
- }
- }
+ if current_pair_wbnb_balance < U256::from(env_config.pancake_sl_wbnb_amount) {
+ let more_info = format!("{}: sell all", strategy);
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} - (-∞, pancake_sl_wbnb_amount): sell all",
+ strategy, meme_address
+ ));
+ return Ok(build_sell_pending_action(
+ strategy,
+ current_pair_wbnb_balance,
+ meme_address,
+ bot_meme_balance,
+ more_info,
+ ));
+ } 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);
- let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(100);
- let pending_action = PendingAction {
- strategy: strategy.clone(),
- pool_type: PoolType::PancakeV2,
- pancake_wbnb_balance: current_pair_wbnb_balance,
- is_buy: false,
- meme_address,
- meme_amount: meme_amount_to_sell,
- bnb_amount: U256::from(0),
- more_info: format!("{} (X, +∞): sell 1%", strategy),
- };
- Logger.process(&format!(
- "Strategy {} considering selling: meme address: {} - (X, +∞): sell 1%",
- strategy, meme_address
- ));
- return Ok(pending_action);
- }
+ if bot_meme_balance < five_percentage {
+ if current_pair_wbnb_balance < ONE_ETHER * U256::from(100) {
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} - < 100 WBNB: do nothing",
+ strategy, meme_address
+ ));
+ return Err(CustomError::StrategyDoNothing());
+ } else {
+ let more_info = format!("{}: pair reaches 100 WBNB, sell all", strategy);
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} - >= 100 WBNB: sell all",
+ strategy, meme_address
+ ));
+ return Ok(build_sell_pending_action(
+ strategy,
+ current_pair_wbnb_balance,
+ meme_address,
+ bot_meme_balance,
+ more_info,
+ ));
}
- _ => {}
}
- }
- if is_buy {
- // we apply none strategy, then add market trading record to the database
- db.insert_market_trade_info(&MarketTradeInfo {
- chain_id: BSC_CHAIN_ID,
- block_number,
- tx_index,
- tx_hash: tx_hash.clone(),
- token_address: meme_address.to_string(),
- updated_at: chrono::Utc::now(),
- tx_sender: tx_sender.to_string(),
- })
- .await
- .map_err(|e| CustomError::Database(format!("Insert market trade info failed: {}", e).into()))?;
+ let meme_amount_to_sell = initial_fourmeme_buy_amount / U256::from(100);
+ let more_info = format!("{} (X, +∞): sell 1%", strategy);
+ Logger.process(&format!(
+ "Strategy {} considering selling: meme address: {} - (X, +∞): sell 1%",
+ strategy, meme_address
+ ));
+ return Ok(build_sell_pending_action(
+ strategy,
+ current_pair_wbnb_balance,
+ meme_address,
+ meme_amount_to_sell,
+ more_info,
+ ));
}
+}
- Logger.item(&format!("No strategy applied, meme address: {}", meme_address));
- Logger.sub_item(&format!("Block number: {}", block_number));
- Logger.sub_item(&format!("Tx index: {}", tx_index));
- Logger.sub_item(&format!("Tx hash: {}", tx_hash));
-
- Err(CustomError::StrategyDoNothing())
+fn build_sell_pending_action(
+ strategy: &str,
+ pancake_wbnb_balance: U256,
+ meme_address: Address,
+ meme_amount: U256,
+ more_info: String,
+) -> PendingAction {
+ PendingAction {
+ strategy: strategy.to_string(),
+ pool_type: PoolType::PancakeV2,
+ pancake_wbnb_balance,
+ is_buy: false,
+ meme_address,
+ meme_amount,
+ bnb_amount: U256::from(0),
+ more_info,
+ }
}
pub async fn consider_buying + Clone + Send + Sync + 'static, N: Network>(
@@ -249,18 +273,12 @@ pub async fn consider_buying + Clone + Send + Sync
meme_address: Address,
block_number: u64,
) -> Result {
- // if we have bought that token before, we should no longer buy it again
let has_bought = check_token_has_bought(&*db, meme_address).await?;
if has_bought.is_some() {
return Err(CustomError::StrategyDoNothing());
}
- // [STRATEGY BUNDLE]
- // if:
- // - there is a bundle transaction
- // - the pair's wbnb balance is greater than `pancake_bundle_threshold`
- // then buy the token
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?;
@@ -268,7 +286,11 @@ pub async fn consider_buying + Clone + Send + Sync
return Err(CustomError::StrategyDoNothing());
}
- let pending_action = PendingAction {
+ 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,
@@ -276,12 +298,8 @@ pub async fn consider_buying + Clone + Send + Sync
meme_address,
meme_amount: U256::from(0),
bnb_amount: env_config.pancake_bundle_buy_amount,
- more_info: format!(
- "bundle tx block: {}, bundle sender: {}",
- bundle_block_number, bundle_sender
- ),
- };
- return Ok(pending_action);
+ more_info,
+ });
}
Logger.item(&format!("No strategy applied, meme address: {}", meme_address));
@@ -289,3 +307,10 @@ pub async fn consider_buying + Clone + Send + Sync
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));
+ Logger.sub_item(&format!("Tx index: {}", tx_index));
+ Logger.sub_item(&format!("Tx hash: {}", tx_hash));
+}