public BigInteger?EstimatePriorityFee(FeeHistoryResult feeHistory)
        {
            var rewards = feeHistory.Reward
                          ?.Select((r) => r[0].Value)
                          .Where((r) => r != 0)
                          .ToList();

            rewards.Sort();

            if (rewards == null || rewards.Count == 0)
            {
                return(null);
            }

            var percentageIncreases = new List <BigInteger>();

            for (var i = 0; i < rewards.Count - 1; i++)
            {
                var next = rewards[i + 1];
                var p    = ((next - rewards[i]) / rewards[i]) * 100;
                percentageIncreases.Add(p);
            }


            var highestIncrease      = percentageIncreases.Max();
            var highestIncreaseIndex = percentageIncreases.IndexOf(highestIncrease);

            //// If we have big increase in value, we could be considering "outliers" in our estimate
            //// Skip the low elements and take a new median
            var values =
                (highestIncrease > PRIORITY_FEE_INCREASE_BOUNDARY &&
                 highestIncreaseIndex >= Math.Floor((double)(rewards.Count / 2))
                ? rewards.Skip(highestIncreaseIndex)
                : rewards).ToArray();

            var valuesIndex = (int)Math.Floor((double)(values.Length / 2));

            return(values[valuesIndex]);
        }
        public Fee1559[] SuggestFees(FeeHistoryResult feeHistory, BigInteger tip)
        {
            var baseFee      = feeHistory.BaseFeePerGas.Select(x => x == null? 0 : x.Value).ToArray();
            var gasUsedRatio = feeHistory.GasUsedRatio;


            // If a block is full then the baseFee of the next block is copied. The reason is that in full blocks the minimal tip might not be enough to get included.
            // The last (pending) block is also assumed to end up being full in order to give some upwards bias for urgent suggestions.
            baseFee[baseFee.Length - 1] *= 9 / 8;
            for (var i = gasUsedRatio.Length - 1; i >= 0; i--)
            {
                if (gasUsedRatio[i] > (decimal)0.9)
                {
                    baseFee[i] = baseFee[i + 1];
                }
            }

            var order = new int[baseFee.Length];

            for (var i = 0; i < baseFee.Length; i++)
            {
                order[i] = i;
            }

#if DOTNET35
            var comparer = Comparer.Create <int>
#else
            var comparer = Comparer <int> .Create
#endif
                               ((int x, int y) =>
            {
                var aa = baseFee[x];
                var bb = baseFee[y];
                if (aa < bb)
                {
                    return(-1);
                }

                if (aa > bb)
                {
                    return(1);
                }

                return(0);
            });

            Array.Sort(order, comparer);


            var        result     = new List <Fee1559>();
            BigDecimal maxBaseFee = 0;
            for (var timeFactor = MaxTimeFactor; timeFactor >= 0; timeFactor--)
            {
                var bf = SuggestBaseFee(baseFee, order, timeFactor);
                var t  = new BigDecimal(tip, 0);
                if (bf > maxBaseFee)
                {
                    maxBaseFee = bf;
                }
                else
                {
                    // If a narrower time window yields a lower base fee suggestion than a wider window then we are probably in a price dip.
                    // In this case getting included with a low tip is not guaranteed; instead we use the higher base fee suggestion
                    // and also offer extra tip to increase the chance of getting included in the base fee dip.
                    t += (maxBaseFee - bf) * ExtraTipRatio;
                    bf = maxBaseFee;
                }
                result.Add(new Fee1559()
                {
                    BaseFee              = bf.Floor().Mantissa,
                    MaxFeePerGas         = (bf + t).Floor().Mantissa,
                    MaxPriorityFeePerGas = t.Floor().Mantissa
                });
            }

            result.Reverse();
            return(result.ToArray());
        }