using System.Numerics; using Managing.Common; using Managing.Infrastructure.Evm.Models.Gmx.v2; using Nethereum.Web3; namespace Managing.Infrastructure.Evm.Services.Gmx; public static class GmxV2Helpers { public static BigInteger GetFundingFactorPerPeriod(GmxMarketInfo marketInfo, bool isLong, int periodInSeconds) { var fundingFactorPerSecond = marketInfo.Market.FundingFactorPerSecond; var longsPayShorts = marketInfo.Market.LongsPayShorts; var longInterestUsd = marketInfo.Infos.LongInterestUsd; var shortInterestUsd = marketInfo.Infos.ShortInterestUsd; var isLargerSide = isLong ? longsPayShorts : !longsPayShorts; BigInteger factorPerSecond; if (isLargerSide) { factorPerSecond = fundingFactorPerSecond * -1; } else { var largerInterestUsd = longsPayShorts ? longInterestUsd : shortInterestUsd; var smallerInterestUsd = longsPayShorts ? shortInterestUsd : longInterestUsd; var ratio = smallerInterestUsd > 0 ? BigInteger.Multiply(largerInterestUsd, Precision) / smallerInterestUsd : 0; factorPerSecond = ApplyFactor(ratio, fundingFactorPerSecond); } return factorPerSecond * periodInSeconds; } private static BigInteger ApplyFactor(BigInteger ratio, BigInteger fundingFactorPerSecond) { // Implement the logic for applying the factor based on the ratio // This is a placeholder implementation return BigInteger.Multiply(ratio, fundingFactorPerSecond) / Precision; } private static readonly BigInteger Precision = BigInteger.Pow(10, 18); public static Dictionary Periods = new Dictionary() { { Enums.Timeframe.FiveMinutes, 300 }, { Enums.Timeframe.FifteenMinutes, 900 }, { Enums.Timeframe.OneHour, 3600 }, { Enums.Timeframe.FourHour, 14400 }, { Enums.Timeframe.OneDay, 86400 } }; public static string GetMarketAddress(Enums.Ticker ticker) { // Switch statement to return the market address based on the ticker switch (ticker) { case Enums.Ticker.BTC: return Constants.GMX.Markets.BTCUSDC; case Enums.Ticker.ETH: return Constants.GMX.Markets.ETHUSDC; case Enums.Ticker.LINK: return Constants.GMX.Markets.LINKUSD; case Enums.Ticker.PEPE: return Constants.GMX.Markets.PEPEUSD; case Enums.Ticker.SOL: return Constants.GMX.Markets.SOLUSD; case Enums.Ticker.UNI: return Constants.GMX.Markets.UNIUSD; case Enums.Ticker.SHIB: return Constants.GMX.Markets.SHIBUSD; case Enums.Ticker.AAVE: return Constants.GMX.Markets.AAVEUSD; case Enums.Ticker.OP: return Constants.GMX.Markets.OPUSD; case Enums.Ticker.APE: return Constants.GMX.Markets.APEUSD; case Enums.Ticker.GMX: return Constants.GMX.Markets.GMXUSD; case Enums.Ticker.ARB: return Constants.GMX.Markets.ARBUSD; case Enums.Ticker.NEAR: return Constants.GMX.Markets.NEARUSD; case Enums.Ticker.STX: return Constants.GMX.Markets.STXUSD; case Enums.Ticker.LTC: return Constants.GMX.Markets.LTCUSD; case Enums.Ticker.XRP: return Constants.GMX.Markets.XRPUSD; case Enums.Ticker.WIF: return Constants.GMX.Markets.WIFUSD; case Enums.Ticker.BNB: return Constants.GMX.Markets.BNBUSD; case Enums.Ticker.ORDI: return Constants.GMX.Markets.ORDIUSD; case Enums.Ticker.DOGE: return Constants.GMX.Markets.DOGEUSD; default: throw new ArgumentOutOfRangeException(nameof(ticker), "Invalid ticker value."); } } public static Enums.Ticker GetTicker(string marketAddress) { switch (marketAddress) { case Constants.GMX.Markets.BTCUSDC: return Enums.Ticker.BTC; case Constants.GMX.Markets.ETHUSDC: return Enums.Ticker.ETH; case Constants.GMX.Markets.LINKUSD: return Enums.Ticker.LINK; case Constants.GMX.Markets.PEPEUSD: return Enums.Ticker.PEPE; case Constants.GMX.Markets.SOLUSD: return Enums.Ticker.SOL; case Constants.GMX.Markets.UNIUSD: return Enums.Ticker.UNI; case Constants.GMX.Markets.SHIBUSD: return Enums.Ticker.SHIB; case Constants.GMX.Markets.AAVEUSD: return Enums.Ticker.AAVE; case Constants.GMX.Markets.OPUSD: return Enums.Ticker.OP; case Constants.GMX.Markets.APEUSD: return Enums.Ticker.APE; case Constants.GMX.Markets.GMXUSD: return Enums.Ticker.GMX; case Constants.GMX.Markets.ARBUSD: return Enums.Ticker.ARB; case Constants.GMX.Markets.NEARUSD: return Enums.Ticker.NEAR; case Constants.GMX.Markets.STXUSD: return Enums.Ticker.STX; case Constants.GMX.Markets.LTCUSD: return Enums.Ticker.LTC; case Constants.GMX.Markets.XRPUSD: return Enums.Ticker.XRP; case Constants.GMX.Markets.WIFUSD: return Enums.Ticker.WIF; case Constants.GMX.Markets.ORDIUSD: return Enums.Ticker.ORDI; default: throw new ArgumentOutOfRangeException(nameof(marketAddress), "Invalid market address."); } } public static Enums.TradeType GetTradeType(GmxV2OrderType orderType) { return orderType switch { GmxV2OrderType.LimitIncrease => Enums.TradeType.Limit, GmxV2OrderType.LimitDecrease => Enums.TradeType.Limit, GmxV2OrderType.MarketIncrease => Enums.TradeType.Market, GmxV2OrderType.MarketDecrease => Enums.TradeType.Market, GmxV2OrderType.Liquidation => Enums.TradeType.Market, _ => throw new ArgumentOutOfRangeException(nameof(orderType), "Invalid order type.") }; } /// /// Parses the contract price to a decimal value. /// /// /// /// Used to tranform the decimal since GMX is not using everytime the decimal from the MarketProps. Use true for prices and false for amounts /// public static decimal ParseContractPrice(BigInteger price, int tokenDecimals, bool useTransform = false) { if (useTransform) tokenDecimals = TransformValue(tokenDecimals); return (decimal)price / (decimal)ExpandDecimals(1, tokenDecimals); } public static BigInteger ConvertToContractPrice(decimal value, int tokenDecimals, bool useTransform = false) { if (useTransform) tokenDecimals = TransformValue(tokenDecimals); return new BigInteger(value * (decimal)ExpandDecimals(1, tokenDecimals)); } public static BigInteger ExpandDecimals(int value, int decimals) { return BigInteger.Multiply(value, BigInteger.Pow(10, decimals)); } public static int TransformValue(int input) { // Apply the derived linear equation y = -x + 30 return -input + 30; } public static BigInteger EstimateOrderOraclePriceCount(int swapsCount) { return 3 + new BigInteger(swapsCount); } public static bool SameAddress(string address, string address2) { return string.Equals(address, address2, StringComparison.CurrentCultureIgnoreCase); } public static BigInteger GetAcceptablePrice(decimal price, bool isLong) { var slippage = 0.003m; // 0.3% slippage var priceSlippage = price * slippage; var acceptablePrice = isLong ? price + priceSlippage : price - priceSlippage; return Web3.Convert.ToWei(acceptablePrice, 22); } public static BigInteger GetCollateralAmount(decimal amount, decimal leverage = 1) { return Web3.Convert.ToWei(amount * leverage, 30); } public static decimal ConvertToUsd(BigInteger amount, int decimals, BigInteger price) { var result = (amount * price) / ExpandDecimals(1, decimals); return (decimal)result; } public static BigInteger ParseValue(decimal value, int decimals) { return Web3.Convert.ToWei(value, decimals); } public static string GetPositionKey(string addressesAccount, string addressesMarket, string addressesCollateralToken, bool flagsIsLong) { return $"{addressesAccount}:{addressesMarket}:{addressesCollateralToken}:{flagsIsLong}"; } public static BigInteger GetEntryPrice(BigInteger positionSizeInUsd, BigInteger positionSizeInTokens, int decimals) { if (positionSizeInTokens <= 0) { return 0; } return BigInteger.Multiply(positionSizeInUsd, ExpandDecimals(1, decimals)) / positionSizeInTokens; } }