diff --git a/Handlers/RugHandler.cs b/Handlers/RugHandler.cs index 03a1c03..625454f 100644 --- a/Handlers/RugHandler.cs +++ b/Handlers/RugHandler.cs @@ -80,6 +80,11 @@ private async Task CheckMinLiquidity(PairCreatedEvent pairCreatedEvent, st { var tokenAmountInPool = otherPairIdx == 1 ? reserves.Reserve1 : reserves.Reserve0; var totalTokenAmount = await _bscWeb3.Eth.GetContract(_erc20Abi, pairCreatedEvent.Pair).GetFunction("totalSupply").CallAsync(); + if(totalTokenAmount == 0) + { + Serilog.Log.Logger.Error("Token {0} contract is giving a invalid supply", pairCreatedEvent.Pair); + return false; + } var percentageInPool = new Fraction(tokenAmountInPool).Divide(totalTokenAmount).Multiply(100); result = ((decimal)percentageInPool) > _sniperConfig.MinimumPercentageOfTokenInLiquidityPool; Serilog.Log.Logger.Information("Token {0} Token Amount in Pool: {1} Total Supply: {2} Total Percentage in pool: {3}% Min Percentage Liquidity Check Status: {4}", pairCreatedEvent.Pair, tokenAmountInPool, totalTokenAmount, percentageInPool.ToDouble(), result); diff --git a/Handlers/TradeHandler.cs b/Handlers/TradeHandler.cs index d594688..cd0fdcb 100644 --- a/Handlers/TradeHandler.cs +++ b/Handlers/TradeHandler.cs @@ -88,31 +88,38 @@ public TokensOwned GetOwnedTokens(string tokenAddress) return _ownedTokenList.FirstOrDefault(t => t.Address == tokenAddress); } - public async Task Sell(string tokenAddress, int tokenIdx, BigInteger amount, BigInteger outAmount) + public async Task Approve(string tokenAddress) { try { + var gas = new HexBigInteger(_sniperConfig.GasAmount); var pairContract = _bscWeb3.Eth.GetContract(_pairAbi, tokenAddress); - var approveFunction = pairContract.GetFunction(); + var approve = await approveFunction.SendTransactionAndWaitForReceiptAsync(new ApproveFunction + { + Spender = _sniperConfig.PancakeswapRouterAddress, + Value = Max + }, _sniperConfig.WalletAddress, gas, new HexBigInteger(BigInteger.Zero)); + } + catch (Exception e) + { + Serilog.Log.Logger.Warning("Could not approve sell for {0}", tokenAddress); + } + return true; + } + + public async Task Sell(string tokenAddress, BigInteger amount, BigInteger outAmount, double slippage) + { + try + { var sellFunction = _pancakeContract.GetFunction(); var gas = new HexBigInteger(_sniperConfig.GasAmount); var transactionAmount = new BigInteger((decimal)amount).ToHexBigInteger(); - try - { - var approve = await approveFunction.SendTransactionAndWaitForReceiptAsync(new ApproveFunction - { - Spender = _sniperConfig.PancakeswapRouterAddress, - Value = Max - }, _sniperConfig.WalletAddress, gas, new HexBigInteger(BigInteger.Zero)); - } catch(Exception e) - { - Serilog.Log.Logger.Warning("Could not approve sell for {0}", tokenAddress); - } + var txId = await sellFunction.SendTransactionAsync(new SwapExactTokensForETHSupportingFeeOnTransferTokensFunction { - AmountOutMin = outAmount, + AmountOutMin = slippage == 0 ? outAmount : (new Fraction(outAmount).Subtract(new Fraction(slippage/100.0).Multiply(outAmount)).ToBigInteger()), AmountIn = amount, Path = new List() { tokenAddress, _sniperConfig.LiquidityPairAddress }, To = _sniperConfig.WalletAddress, @@ -139,15 +146,16 @@ public async Task Sell(string tokenAddress, int tokenIdx, BigInteger amoun } } - public async Task GetMarketPrice(TokensOwned ownedToken) + public async Task GetMarketPrice(TokensOwned ownedToken, BigInteger amount) { var price = await _rugChecker.GetReserves(ownedToken.PairAddress); if(price.Reserve0 == 0 || price.Reserve1 == 0) { return BigInteger.Zero; } - var pricePerLiquidityToken = ownedToken.TokenIdx == 1 ? new Fraction(price.Reserve1).Divide(price.Reserve0).ToDouble() : new Fraction(price.Reserve0).Divide(price.Reserve1).ToDouble(); - return new Fraction(pricePerLiquidityToken).Multiply(ownedToken.Amount).ToBigInteger(); + var pricePerLiquidityToken = ownedToken.TokenIdx == 1 ? new Fraction(price.Reserve0).Divide(price.Reserve1) : new Fraction(price.Reserve1).Divide(price.Reserve0); + + return ((BigInteger)pricePerLiquidityToken.Multiply(amount)); } public void Start() @@ -167,8 +175,12 @@ private void MonitorPrices() continue; } var price = _rugChecker.GetReserves(ownedToken.PairAddress).Result; + if(price.Reserve0 == 0 || price.Reserve1 == 0) + { + continue; + } var pricePerLiquidityToken = ownedToken.TokenIdx == 1 ? new Fraction(price.Reserve1).Divide(price.Reserve0).ToDouble() : new Fraction(price.Reserve0).Divide(price.Reserve1).ToDouble(); - var profitPerc = 100.0 - ((100.0 / ownedToken.SinglePrice) * pricePerLiquidityToken); + var profitPerc = ((100.0 / ownedToken.SinglePrice) * pricePerLiquidityToken) - 100.0; Log.Logger.Information("Token: {0} Price bought: {1} Current Price: {2} Current Profit: {3}%", ownedToken.Address, ownedToken.SinglePrice, pricePerLiquidityToken, profitPerc); @@ -176,7 +188,7 @@ private void MonitorPrices() { try { - ownedToken.FailedSell = !Sell(ownedToken.Address, ownedToken.TokenIdx, ownedToken.Amount, new Fraction(pricePerLiquidityToken).Multiply(ownedToken.Amount).ToBigInteger()).Result; + ownedToken.FailedSell = !Sell(ownedToken.Address, ownedToken.Amount - 1, GetMarketPrice(ownedToken, ownedToken.Amount - 1).Result, _sniperConfig.SellSlippage).Result; } catch(Exception e) { Serilog.Log.Logger.Error(nameof(MonitorPrices), e); diff --git a/Models/SniperConfiguration.cs b/Models/SniperConfiguration.cs index 7c82088..6302fb7 100644 --- a/Models/SniperConfiguration.cs +++ b/Models/SniperConfiguration.cs @@ -24,5 +24,6 @@ public class SniperConfiguration public decimal MinimumPercentageOfTokenInLiquidityPool { get; set; } public bool HoneypotCheck { get; set; } public double HoneypotCheckAmount { get; set; } + public double SellSlippage { get; set; } } } diff --git a/Models/SwapTokensForExactEthFunction.cs b/Models/SwapTokensForExactEthFunction.cs index eca50d1..58f9f09 100644 --- a/Models/SwapTokensForExactEthFunction.cs +++ b/Models/SwapTokensForExactEthFunction.cs @@ -1,5 +1,6 @@ using Nethereum.ABI.FunctionEncoding.Attributes; using Nethereum.Contracts; +using Nethereum.Util; using System.Collections.Generic; using System.Numerics; diff --git a/SniperService.cs b/SniperService.cs index c52db0d..af6b231 100644 --- a/SniperService.cs +++ b/SniperService.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Numerics; using System.Threading; using System.Threading.Tasks; @@ -98,7 +99,7 @@ private async Task PairCreated(EventLog pairCreated) var otherTokenIdx = pair.Token0.Equals(_sniperConfig.LiquidityPairAddress, StringComparison.InvariantCultureIgnoreCase) ? 1 : 0; var honeypotCheck = _sniperConfig.HoneypotCheck; - Log.Logger.Information("Discovered Token Pair {0} Rug check Result: {1}", symbol, rugCheckPassed); + Log.Logger.Information("Discovered Token Pair {0} Rug check Result: {1} Contract address: {2}", symbol, rugCheckPassed, otherPairAddress); if (!rugCheckPassed) { Log.Logger.Warning("Rug Check failed for {0}", symbol); @@ -119,12 +120,13 @@ private async Task PairCreated(EventLog pairCreated) return; } var ownedToken = _tradeHandler.GetOwnedTokens(otherPairAddress); - var marketPrice = await _tradeHandler.GetMarketPrice(ownedToken); + await _tradeHandler.Approve(otherPairAddress); + var marketPrice = await _tradeHandler.GetMarketPrice(ownedToken, ownedToken.Amount - 1); var sellSuccess = false; try { - sellSuccess = await _tradeHandler.Sell(otherPairAddress, otherTokenIdx, ownedToken.Amount, marketPrice); + sellSuccess = await _tradeHandler.Sell(otherPairAddress, ownedToken.Amount - 1, marketPrice, _sniperConfig.SellSlippage); } catch(Exception e) { Serilog.Log.Error("Error Sell", e); @@ -135,7 +137,7 @@ private async Task PairCreated(EventLog pairCreated) return; } - Log.Logger.Fatal("Honeypot check PASSED buying token: {0}", pair.Symbol); + Log.Logger.Information("Honeypot check PASSED buying token: {0}", pair.Symbol); await _tradeHandler.Buy(otherPairAddress, otherTokenIdx, pair.Pair, _sniperConfig.AmountToSnipe); } catch (Exception e) diff --git a/appsettings.json b/appsettings.json index 886b5bc..6058c88 100644 --- a/appsettings.json +++ b/appsettings.json @@ -29,16 +29,17 @@ "PancakeswapRouterAddress": "0x10ED43C718714eb63d5aA57B78B54704E256024E", "ChainId": "56", "AmountToSnipe": 0.0001, - "ProfitPercentageMargin": 10, + "ProfitPercentageMargin": 20, "TransactionRevertTimeSeconds": 10000, "GasAmount": 300000, "BscHttpApi": "https://speedy-nodes-nyc.moralis.io/xxx/bsc/mainnet", "BscNode": "wss://speedy-nodes-nyc.moralis.io/xxx/bsc/mainnet/ws", "BscScanApiKey": "xxx", - "MinLiquidityAmount": 0.7, - "MinimumPercentageOfTokenInLiquidityPool": 95, + "MinLiquidityAmount": 0.5, + "MinimumPercentageOfTokenInLiquidityPool": 50, "RugCheckEnabled": true, "RenounceOwnershipCheck": true, + "SellSlippage": 0.5, "ContractRugCheckStrings": [ "function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool)", "function _approve(address owner, address spender, uint256 amount) internal",