diff --git a/Handlers/TradeHandler.cs b/Handlers/TradeHandler.cs index b965694..d594688 100644 --- a/Handlers/TradeHandler.cs +++ b/Handlers/TradeHandler.cs @@ -25,6 +25,8 @@ public class TradeHandler : IDisposable private List _ownedTokenList = new(); private bool _stopped; private readonly string _erc20Abi; + private readonly string _pairAbi; + private static BigInteger Max { get; } = BigInteger.Parse("115792089237316195423570985008687907853269984665640564039457584007913129639935"); public TradeHandler(IOptions options, RugHandler rugChecker) { @@ -32,12 +34,13 @@ public TradeHandler(IOptions options, RugHandler rugChecker _bscWeb3 = new Web3(url: _sniperConfig.BscHttpApi, account: new Account(_sniperConfig.WalletPrivateKey, new BigInteger(_sniperConfig.ChainId))); _bscWeb3.TransactionManager.UseLegacyAsDefault = true; _erc20Abi = File.ReadAllText("./Abis/Erc20.json"); + _pairAbi = File.ReadAllText("./Abis/Pair.json"); _pancakeContract = _bscWeb3.Eth.GetContract(File.ReadAllText("./Abis/Pancake.json"), _sniperConfig.PancakeswapRouterAddress); _rugChecker = rugChecker; Start(); } - public async Task Buy(string tokenAddress, int tokenIdx, string pairAddress, double amt) + public async Task Buy(string tokenAddress, int tokenIdx, string pairAddress, double amt, bool honeypotCheck = false) { try { @@ -66,7 +69,8 @@ public async Task Buy(string tokenAddress, int tokenIdx, string pairAddres SinglePrice = new Fraction(balance).Divide(new Fraction(amount.Value)).ToDouble(), TokenIdx = tokenIdx, PairAddress = pairAddress, - Decimals = decimals + Decimals = decimals, + HoneypotCheck = honeypotCheck }); return true; } @@ -88,10 +92,24 @@ public async Task Sell(string tokenAddress, int tokenIdx, BigInteger amoun { try { + var pairContract = _bscWeb3.Eth.GetContract(_pairAbi, tokenAddress); + + var approveFunction = pairContract.GetFunction(); 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, @@ -144,6 +162,10 @@ private void MonitorPrices() for (int i = _ownedTokenList.Count - 1; i >= 0; i--) { var ownedToken = _ownedTokenList[i]; + if(ownedToken.FailedSell || ownedToken.HoneypotCheck) + { + continue; + } var price = _rugChecker.GetReserves(ownedToken.PairAddress).Result; 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); @@ -152,7 +174,14 @@ private void MonitorPrices() if (profitPerc > _sniperConfig.ProfitPercentageMargin) { - Sell(ownedToken.Address, ownedToken.TokenIdx, ownedToken.Amount, new Fraction(pricePerLiquidityToken).Multiply(ownedToken.Amount).ToBigInteger()).Wait(); + try + { + ownedToken.FailedSell = !Sell(ownedToken.Address, ownedToken.TokenIdx, ownedToken.Amount, new Fraction(pricePerLiquidityToken).Multiply(ownedToken.Amount).ToBigInteger()).Result; + } catch(Exception e) + { + Serilog.Log.Logger.Error(nameof(MonitorPrices), e); + ownedToken.FailedSell = true; + } _ownedTokenList.Remove(ownedToken); } } diff --git a/Models/ApproveFunction.cs b/Models/ApproveFunction.cs new file mode 100644 index 0000000..6ea4e3e --- /dev/null +++ b/Models/ApproveFunction.cs @@ -0,0 +1,17 @@ +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; +using System.Numerics; + +namespace BscTokenSniper.Models +{ + public partial class ApproveFunction : ApproveFunctionBase { } + + [Function("approve", "bool")] + public class ApproveFunctionBase : FunctionMessage + { + [Parameter("address", "spender", 1)] + public virtual string Spender { get; set; } + [Parameter("uint256", "value", 2)] + public virtual BigInteger Value { get; set; } + } +} diff --git a/Models/TokensOwned.cs b/Models/TokensOwned.cs index 2153a17..5922ca1 100644 --- a/Models/TokensOwned.cs +++ b/Models/TokensOwned.cs @@ -11,5 +11,7 @@ public class TokensOwned public int TokenIdx { get; set; } public string PairAddress { get; set; } public int Decimals { get; set; } + public bool HoneypotCheck { get; set; } + public bool FailedSell { get; set; } } } diff --git a/SniperService.cs b/SniperService.cs index db972ee..92fa899 100644 --- a/SniperService.cs +++ b/SniperService.cs @@ -120,7 +120,15 @@ private async Task PairCreated(EventLog pairCreated) } var ownedToken = _tradeHandler.GetOwnedTokens(otherPairAddress); var marketPrice = await _tradeHandler.GetMarketPrice(ownedToken); - var sellSuccess = await _tradeHandler.Sell(otherPairAddress, otherTokenIdx, ownedToken.Amount, marketPrice); + var sellSuccess = false; + + try + { + sellSuccess = await _tradeHandler.Sell(otherPairAddress, otherTokenIdx, ownedToken.Amount, marketPrice); + } catch(Exception e) + { + Serilog.Log.Error("Error Sell", e); + } if (!sellSuccess) { Log.Logger.Fatal("Honeypot check DETECTED HONEYPOT could not sell token: {0}", pair.Symbol);