docker files fixes from liaqat

This commit is contained in:
alirehmani
2024-05-03 16:39:25 +05:00
commit 464a8730e8
587 changed files with 44288 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
using Managing.Common;
using Managing.Domain.Evm;
namespace Managing.Infrastructure.Evm.Services;
public static class ChainService
{
//private const string RPC_ARBITRUM = "https://convincing-smart-arm.arbitrum-mainnet.discover.quiknode.pro/561ad3fa1db431a2c728c2fdb1a62e8f94acf703/";
private const string RPC_ARBITRUM = "https://arb1.arbitrum.io/rpc";
private const string RPC_ARBITRUM_GOERLI = "https://arb-goerli.g.alchemy.com/v2/ZMkIiKtNvgY03UtWOjho0oqkQrNt_pyc";
private const string RPC_ETHEREUM = "https://mainnet.infura.io/v3/58f44d906ab345beadd03dd2b76348af";
private const string RPC_ETHEREUM_GOERLI = "https://eth-goerli.g.alchemy.com/v2/xbc-eM-vxBmM9Uf1-RjjGjLp8Ng-FIc6";
public static Chain GetChain(string chainName)
{
if (string.IsNullOrEmpty(chainName))
throw new Exception("Chain name is null or empty");
return GetChains().FirstOrDefault(c => c.Name == chainName);
}
public static List<Chain> GetChains()
{
var chains = new List<Chain>()
{
GetArbitrum(),
GetEthereum(),
//GetArbitrumGoerli(),
//GetGoerli()
};
return chains;
}
public static Chain GetArbitrum()
{
return new Chain()
{
Name = Constants.Chains.Arbitrum,
RpcUrl = RPC_ARBITRUM
};
}
public static Chain GetEthereum()
{
return new Chain()
{
Name = Constants.Chains.Ethereum,
RpcUrl = RPC_ETHEREUM
};
}
public static Chain GetArbitrumGoerli()
{
return new Chain()
{
Name = Constants.Chains.ArbitrumGoerli,
RpcUrl = RPC_ARBITRUM_GOERLI
};
}
public static Chain GetGoerli()
{
return new Chain()
{
Name = Constants.Chains.Goerli,
RpcUrl = RPC_ETHEREUM_GOERLI
};
}
}

View File

@@ -0,0 +1,139 @@
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Models.Gmx;
using Managing.Infrastructure.Evm.Referentials;
using Nethereum.Web3;
using System.Numerics;
using static Managing.Common.Enums;
namespace Managing.Infrastructure.Evm.Services.Gmx;
public static class GmxHelpers
{
public static decimal GetQuantityForLeverage(decimal quantity, decimal? leverage)
{
return leverage.HasValue ? leverage.Value * quantity : quantity;
}
public static (List<string> CollateralTokens, List<string> IndexTokens, List<bool> IsLong) GetPositionQueryData(List<string> contractAddress)
{
var collateralToken = new List<string>();
var indexTokens = new List<string>();
var isLongs = new List<bool>();
foreach (var token in contractAddress)
{
collateralToken.Add(token);
indexTokens.Add(token);
isLongs.Add(true);
}
foreach (var token in contractAddress)
{
collateralToken.Add(Arbitrum.Address.USDC);
indexTokens.Add(token);
isLongs.Add(false);
}
return (collateralToken, indexTokens, isLongs);
}
public static List<BigInteger> GetIndexesRange(int lastIndex)
{
var indexes = new List<BigInteger>();
var limit = 15;
var from = (lastIndex - limit) < 0 ? 0 : lastIndex - limit;
for (int i = from; i <= lastIndex; i++)
{
indexes.Add(new BigInteger(i));
}
return indexes;
}
public static BigInteger GetAcceptablePrice(decimal price, bool isLong)
{
decimal priceBasisPoints;
var basisPointDivisor = 10000m;
var allowedSlippage = 34m;
var toDecimal = 30;
if (isLong)
{
priceBasisPoints = basisPointDivisor - allowedSlippage;
}
else
{
priceBasisPoints = basisPointDivisor + allowedSlippage;
}
var test = Web3.Convert.ToWei(price, toDecimal) * new BigInteger(priceBasisPoints);
var priceLimit = test / Web3.Convert.ToWei(basisPointDivisor, 0);
return priceLimit;
}
internal static BigInteger? GetGasLimit()
{
throw new NotImplementedException();
}
internal static List<Trade> Map(List<GmxOrder> orders, Ticker ticker)
{
return orders.ConvertAll(order => Map(order, ticker));
}
private static Trade Map(GmxOrder order, Ticker ticker)
{
var trade = new Trade(DateTime.UtcNow,
order.IsLong ? TradeDirection.Short : TradeDirection.Long,
TradeStatus.Requested,
GetTradeType(order.IsLong, order.TriggerAboveThreshold),
ticker,
Convert.ToDecimal(order.SizeDelta),
Convert.ToDecimal(order.TriggerPrice),
null,
"", ""
);
return trade;
}
public static bool GetTriggerAboveThreshold(bool isLong, TradeType tradeType)
{
if ((isLong && tradeType == TradeType.TakeProfit) ||
(!isLong && tradeType == TradeType.StopLoss))
{
return true;
}
return false;
}
public static TradeType GetTradeType(bool isLong, bool isAboveThreshold)
{
if ((isLong && isAboveThreshold) ||
(!isLong && !isAboveThreshold))
{
return TradeType.TakeProfit;
}
return TradeType.StopLoss;
}
internal static string GeTimeframe(Timeframe timeframe)
{
return timeframe switch
{
Timeframe.FiveMinutes => "5m",
Timeframe.FifteenMinutes => "15m",
Timeframe.ThirtyMinutes => "30m",
Timeframe.OneHour => "1h",
Timeframe.FourHour => "4h",
Timeframe.OneDay => "1d",
_ => throw new NotImplementedException(),
};
}
}

View File

@@ -0,0 +1,177 @@
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Models.Gmx;
using Managing.Infrastructure.Evm.Referentials;
using Nethereum.Web3;
using System.Numerics;
using static Managing.Common.Enums;
namespace Managing.Infrastructure.Evm.Services.Gmx;
public static class GmxMappers
{
internal static Trade Map(GmxPosition? position, Ticker ticker)
{
if (position == null)
{
return null;
}
var leverage = position.SizeDelta / position.Collateral;
var trade = new Trade(
DateHelpers.GetFromUnixTimestamp((int)position.LastIncreasedTime),
position.IsLong ? TradeDirection.Long : TradeDirection.Short,
TradeStatus.Filled,
TradeType.Limit,
ticker,
Web3.Convert.FromWei(position.SizeDelta, 30),
Web3.Convert.FromWei(position.AveragePrice, 30),
(decimal)leverage,
"",
"");
return trade;
}
public static List<GmxPosition> MapPositions(List<BigInteger> positionData,
(List<string> CollateralTokens, List<string> IndexTokens, List<bool> IsLong) queryData)
{
var gmxPositions = new List<GmxPosition>();
var propLength = 9;
for (int i = 0; i < queryData.CollateralTokens.Count; i++)
{
var gmxPosition = new GmxPosition
{
CollateralToken = queryData.CollateralTokens[i],
IndexToken = queryData.IndexTokens[i],
IsLong = queryData.IsLong[i],
SizeDelta = positionData[i * propLength],
Collateral = positionData[i * propLength + 1],
AveragePrice = positionData[i * propLength + 2],
EntryFundingRate = positionData[i * propLength + 3],
//gmxPosition.CumulativeFundingRate = collateralToken.cumulativeFundingRate;
HasRealisedProfit = positionData[i * propLength + 4].Equals(1),
RealisedPnl = positionData[i * propLength + 5],
LastIncreasedTime = positionData[i * propLength + 6],
HasProfit = positionData[i * propLength + 7],
Delta = positionData[i * propLength + 8]
};
if (!gmxPosition.SizeDelta.IsZero)
gmxPositions.Add(gmxPosition);
}
return gmxPositions;
}
public static List<GmxOrder> MapIncrease(
List<BigInteger> orderData,
List<string> addressData,
List<BigInteger> indexes)
{
var extractor = (List<BigInteger> orderProperty, List<string> address) =>
{
var order = new GmxOrder
{
PurchaseToken = address[0].ToString(),
CollateralToken = address[1].ToString(),
IndexToken = address[2].ToString(),
PurchaseTokenAmount = Web3.Convert.FromWeiToBigDecimal(orderProperty[0], 18).ToString(),
SizeDelta = Web3.Convert.FromWeiToBigDecimal(orderProperty[1], 30).ToString(),
IsLong = orderProperty[2].ToString() == "1",
TriggerPrice = Web3.Convert.FromWeiToBigDecimal(orderProperty[3], 30).ToString(),
TriggerAboveThreshold = orderProperty[4].ToString() == "1",
Type = GmxOrderType.Increase
};
return order;
};
return ParseOrdersData(orderData, addressData, extractor, indexes, 5, 3);
}
public static List<GmxOrder> MapDecrease(
List<BigInteger> orderData,
List<string> addressData,
List<BigInteger> indexes)
{
var extractor = (List<BigInteger> orderProperty, List<string> address) =>
{
var order = new GmxOrder
{
CollateralToken = address[0],
IndexToken = address[1],
CollateralDelta = orderProperty[0].ToString(),
SizeDelta = Web3.Convert.FromWeiToBigDecimal(orderProperty[1], 30).ToString(),
IsLong = orderProperty[2].ToString() == "1",
TriggerPrice = Web3.Convert.FromWeiToBigDecimal(orderProperty[3], 30).ToString(),
TriggerAboveThreshold = orderProperty[4].ToString() == "1",
Type = GmxOrderType.Decrease
};
return order;
};
return ParseOrdersData(orderData, addressData, extractor, indexes, 5, 2);
}
public static List<GmxOrder> ParseOrdersData(
List<BigInteger> orderData,
List<string> addressData,
Func<List<BigInteger>, List<string>, GmxOrder> extractor,
List<BigInteger> indexes,
int uintPropsLength,
int addressPropsLength)
{
if (orderData.Count == 0 || addressData.Count == 0)
{
return new List<GmxOrder>();
}
var count = orderData.Count / uintPropsLength;
var orders = new List<GmxOrder>();
for (int i = 0; i < count; i++)
{
var slicedAddress = addressData
.Skip(addressPropsLength * i)
.Take(addressPropsLength)
.ToList();
if (slicedAddress[0] == Arbitrum.Address.Zero && slicedAddress[1] == Arbitrum.Address.Zero)
{
continue;
}
var slicedProperty = orderData
.Skip(uintPropsLength * i)
.Take(uintPropsLength * i)
.ToList();
var order = extractor(slicedProperty, slicedAddress);
order.Index = indexes[i];
orders.Add(order);
}
return orders;
}
internal static Candle Map(GmxOhlc price, Ticker ticker, Timeframe timeframe)
{
return new Candle()
{
Date = DateHelpers.GetFromUnixTimestamp(price.t),
OpenTime = DateHelpers.GetFromUnixTimestamp(price.t).AddSeconds(-1),
Open = Convert.ToDecimal(price.o),
High = Convert.ToDecimal(price.h),
Low = Convert.ToDecimal(price.l),
Close = Convert.ToDecimal(price.c),
Exchange = TradingExchanges.Evm,
Ticker = ticker.ToString(),
Timeframe = timeframe
};
}
}

View File

@@ -0,0 +1,458 @@
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Models.Gmx;
using Managing.Infrastructure.Evm.Referentials;
using Managing.Tools.OrderBook;
using Managing.Tools.OrderBook.ContractDefinition;
using Managing.Tools.OrderBookReader;
using Managing.Tools.OrderBookReader.ContractDefinition;
using Managing.Tools.PositionRouter;
using Managing.Tools.PositionRouter.ContractDefinition;
using Managing.Tools.Reader;
using Managing.Tools.Reader.ContractDefinition;
using Managing.Tools.Router;
using Managing.Tools.Router.ContractDefinition;
using Nethereum.Contracts.Standards.ERC20;
using Nethereum.Contracts.Standards.ERC20.ContractDefinition;
using Nethereum.Util;
using Nethereum.Web3;
using System.Numerics;
using static Managing.Common.Enums;
namespace Managing.Infrastructure.Evm.Services.Gmx;
public static class GmxService
{
private const decimal _orderFeesExecution = 0.0003m;
private const decimal _positionUpdateFees = 0.0001m;
public async static Task InitAccountForTrading(Web3 web3, string publicAddress, List<Ticker> tickers)
{
var router = new RouterService(web3, Arbitrum.Address.Router);
if (!await IsPluginAdded(web3, publicAddress, Arbitrum.Address.PositionRouter))
{
var routerApproval = await router
.ApprovePluginRequestAndWaitForReceiptAsync(Arbitrum.Address.PositionRouter);
}
if (!await IsPluginAdded(web3, publicAddress, Arbitrum.Address.OrderBook))
{
var routerApproval = await router
.ApprovePluginRequestAsync(Arbitrum.Address.OrderBook);
}
foreach (var ticker in tickers)
{
var conntractAddress = TokenService.GetContractAddress(ticker);
await ApproveToken(web3, publicAddress, conntractAddress);
}
}
public async static Task<bool> IsPluginAdded(Web3 web3, string publicAddress, string pluginAddress)
{
var router = new RouterService(web3, Arbitrum.Address.Router);
var function = new ApprovedPluginsFunction
{
ReturnValue1 = publicAddress,
ReturnValue2 = pluginAddress
};
var isAdded = await router.ApprovedPluginsQueryAsync(function);
return isAdded;
}
public async static Task ApproveToken(Web3 web3, string publicAddress, string contractAddress)
{
var input = new Nethereum.Contracts.Standards.ERC20.ContractDefinition.ApproveFunction
{
Spender = publicAddress
};
var contract = new ERC20ContractService(web3.Eth, contractAddress);
var approval = await contract.ApproveRequestAsync(input);
}
public static async Task<bool> ApproveOrder(Web3 web3, Ticker ticker, string publicAddress, decimal amount)
{
var contractAddress = TokenService.GetContractAddress(ticker);
var contract = new ERC20ContractService(web3.Eth, contractAddress);
var allowanceQuery = new AllowanceFunction
{
Owner = publicAddress,
Spender = Arbitrum.Address.Router
};
var allowance = await contract.AllowanceQueryAsync(allowanceQuery);
if (allowance.IsZero) return false;
var approveQuery = new Nethereum.Contracts.Standards.ERC20.ContractDefinition.ApproveFunction
{
FromAddress = publicAddress,
Spender = Arbitrum.Address.Router,
Value = Web3.Convert.ToWei(amount)
};
var approval = await contract.ApproveRequestAsync(approveQuery);
return true;
}
public async static Task<Trade> IncreasePosition(Web3 web3, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
{
var quantityLeveraged = GmxHelpers.GetQuantityForLeverage(quantity, leverage);
var orderBook = new OrderBookService(web3, Arbitrum.Address.OrderBook);
var contractAddress = TokenService.GetContractAddress(ticker);
var isLong = direction == TradeDirection.Long;
var function = new CreateIncreaseOrderFunction();
// Forcing path to use USDC to pay the trade
function.Path = new List<string> { Arbitrum.Address.USDC };
function.AmountIn = Web3.Convert.ToWei(quantity * price, 6); // Price in $ to pay the long/short. Ex 11.42$
function.IndexToken = contractAddress; // Token to long/short
function.MinOut = new BigInteger(0);
// Size of the position with the leveraged quantity
// Ex : Long 11$ x3. SizeDelta = 33$
function.SizeDelta = Web3.Convert.ToWei(quantityLeveraged * price, UnitConversion.EthUnit.Tether);
function.CollateralToken = Arbitrum.Address.USDC; // USDC
function.IsLong = isLong;
function.TriggerPrice = Web3.Convert.ToWei(price, 30); // Price of the order execution
function.TriggerAboveThreshold = false;
function.ExecutionFee = Web3.Convert.ToWei(_orderFeesExecution); // Fee required to execute tx
function.ShouldWrap = false;
// Specify the tx opts
function.AmountToSend = Web3.Convert.ToWei(_orderFeesExecution);
function.FromAddress = publicAddress;
//function.MaxFeePerGas = await orderBook.ContractHandler.EstimateGasAsync(function);
function.GasPrice = GetGasPrice();
// Approving Router to transfer ERC20 token
var approval = await ApproveOrder(web3, Ticker.USDC, publicAddress, _orderFeesExecution);
if (!approval) return null;
var receipt = await orderBook
.CreateIncreaseOrderRequestAndWaitForReceiptAsync(function);
var trade = new Trade(DateTime.UtcNow,
direction,
TradeStatus.Requested,
TradeType.Limit,
ticker,
quantity,
price,
leverage,
receipt.TransactionHash,
"");
return trade;
}
public async static Task<Trade> DecreasePosition(Web3 web3, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
{
var trade = new Trade(DateTime.UtcNow,
direction,
TradeStatus.Cancelled,
TradeType.Market,
ticker,
quantity,
price,
leverage,
"",
"");
// Check if there is quantity in position
if (await QuantityInPosition(web3, publicAddress, ticker) == 0) return trade;
var quantityLeveraged = GmxHelpers.GetQuantityForLeverage(quantity, leverage);
var positionRouter = new PositionRouterService(web3, Arbitrum.Address.PositionRouter);
var contractAddress = TokenService.GetContractAddress(ticker);
var isLong = direction == TradeDirection.Long;
var function = new CreateDecreasePositionFunction();
// Forcing path to use contract address to widthdraw funds
// The address for closing a short, should be USDC
//function.Path = new List<string> { contractAddress };
function.Path = new List<string> { Arbitrum.Address.USDC };
// the index token of the position
function.IndexToken = contractAddress; // Token to long/short
// the amount of collateral in USD value to withdraw
function.CollateralDelta = new BigInteger(0); // Price in $ to pay the long/short. Ex 11.42$
//function.CollateralDelta = Web3.Convert.ToWei(quantity * price, 6); // Price in $ to pay the long/short. Ex 11.42$
// the USD value of the change in position size
function.SizeDelta = Web3.Convert.ToWei(quantity, UnitConversion.EthUnit.Tether);
function.IsLong = isLong;
// the address to receive the withdrawn tokens
function.Receiver = publicAddress;
// the USD value of the min (for longs) or max (for shorts) index price acceptable when executing the request
function.AcceptablePrice = GmxHelpers.GetAcceptablePrice(price, isLong);
// the min output token amount
function.MinOut = new BigInteger(0);
function.ExecutionFee = Web3.Convert.ToWei(_positionUpdateFees); // Fee required to execute tx
function.WithdrawETH = false;
function.CallbackTarget = Arbitrum.Address.Zero;
// Specify the tx opts
function.AmountToSend = Web3.Convert.ToWei(_positionUpdateFees);
function.FromAddress = publicAddress;
function.MaxFeePerGas = await positionRouter.ContractHandler.EstimateGasAsync(function);
function.GasPrice = GetGasPrice();
var approval = await ApproveOrder(web3, ticker, publicAddress, _positionUpdateFees);
if (!approval) return null;
var receipt = await positionRouter
.CreateDecreasePositionRequestAndWaitForReceiptAsync(function);
trade.SetExchangeOrderId(receipt.TransactionHash);
trade.SetStatus(receipt.Status.Value.IsOne ? TradeStatus.Requested : TradeStatus.Cancelled);
return trade;
}
public static async Task<Trade> DecreaseOrder(Web3 web3, TradeType tradeType, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
{
var trade = new Trade(DateTime.UtcNow,
direction,
TradeStatus.Cancelled,
tradeType,
ticker,
quantity,
price,
leverage,
"",
"");
// Check if there is quantity in position
var currentPosition = await GetGmxPosition(web3, publicAddress, ticker);
if (currentPosition == null || currentPosition?.SizeDelta == 0) return trade;
var quantityLeveraged = GmxHelpers.GetQuantityForLeverage(quantity, leverage);
var orderbook = new OrderBookService(web3, Arbitrum.Address.OrderBook);
var contractAddress = TokenService.GetContractAddress(ticker);
var isLong = direction != TradeDirection.Long;
var function = new CreateDecreaseOrderFunction();
// the index token of the position
function.IndexToken = contractAddress; // Token to long/short
// the USD value of the change in position size
function.SizeDelta = currentPosition.SizeDelta;
function.CollateralToken = Arbitrum.Address.USDC;
// the amount of collateral in USD value to withdraw
function.CollateralDelta = new BigInteger(0); // Price in $ to pay the long/short. Ex 11.42$
//function.CollateralDelta = Web3.Convert.ToWei(quantity * price, 6); // Price in $ to pay the long/short. Ex 11.42$
function.IsLong = isLong;
// the USD value of the min (for longs) or max (for shorts) index price acceptable when executing the request
function.TriggerPrice = GmxHelpers.GetAcceptablePrice(price, isLong);
function.TriggerAboveThreshold = GmxHelpers.GetTriggerAboveThreshold(isLong, tradeType);
// Specify the tx opts
function.AmountToSend = Web3.Convert.ToWei(_orderFeesExecution);
function.FromAddress = publicAddress;
function.MaxFeePerGas = await orderbook.ContractHandler.EstimateGasAsync(function);
function.GasPrice = GetGasPrice();
var approval = await ApproveOrder(web3, ticker, publicAddress, _positionUpdateFees);
if (!approval) return null;
var receipt = await orderbook
.CreateDecreaseOrderRequestAndWaitForReceiptAsync(function);
trade.SetExchangeOrderId(receipt.TransactionHash);
trade.SetStatus(TradeStatus.Requested);
return trade;
}
public async static Task<bool> CancelOrders(Web3 web3, string publicAddress, Ticker ticker)
{
var orderBook = new OrderBookService(web3, Arbitrum.Address.OrderBook);
var orders = await GetOrders(web3, publicAddress, ticker);
if (!orders.Any()) return true;
var function = new CancelMultipleFunction();
var increaseOrderIndexes = orders.Where(i => i.Type == GmxOrderType.Increase);
function.IncreaseOrderIndexes = increaseOrderIndexes.Select(o => o.Index).ToList();
var decreaseOrderIndexes = orders.Where(i => i.Type == GmxOrderType.Decrease);
function.DecreaseOrderIndexes = decreaseOrderIndexes.Select(o => o.Index).ToList();
function.SwapOrderIndexes = new List<BigInteger>();
try
{
if (function.DecreaseOrderIndexes.Any() || function.IncreaseOrderIndexes.Any())
{
function.MaxFeePerGas = await orderBook.ContractHandler.EstimateGasAsync(function);
function.GasPrice = GetGasPrice();
var cancellation = await orderBook.CancelMultipleRequestAndWaitForReceiptAsync(function);
}
}
catch (Exception ex)
{
return false;
}
return true;
}
private static BigInteger GetGasPrice()
{
return Web3.Convert.ToWei(0.1, UnitConversion.EthUnit.Gwei);
}
public static async Task<List<GmxOrder>> GetOrders(Web3 web3, string publicAddress, Ticker ticker)
{
var lastIndexes = await GetLastIndex(web3, publicAddress);
var orders = new List<GmxOrder>();
var orderBookReader = new OrderBookReaderService(web3, Arbitrum.Address.OrderBookReader);
var increaseOrders = await GetIncreaseOrders(orderBookReader, publicAddress, lastIndexes.IncreaseIndex);
var decreaseOrders = await GetDecreaseOrders(orderBookReader, publicAddress, lastIndexes.DecreaseIndex);
orders.AddRange(increaseOrders);
orders.AddRange(decreaseOrders);
var contractAddress = TokenService.GetContractAddress(ticker);
var ordersFiltered = orders.Where(o => string.Equals(o.IndexToken, contractAddress, StringComparison.CurrentCultureIgnoreCase));
return ordersFiltered.ToList();
}
private static async Task<List<GmxOrder>> GetIncreaseOrders(OrderBookReaderService orderBookReader, string publicAddress, int lastIndex)
{
var increaseIndex = GmxHelpers.GetIndexesRange(lastIndex);
var increaseOrdersFunction = new GetIncreaseOrdersFunction
{
OrderBookAddress = Arbitrum.Address.OrderBook,
Account = publicAddress,
Indices = increaseIndex,
};
var increaseOrders = await orderBookReader.GetIncreaseOrdersQueryAsync(increaseOrdersFunction);
return GmxMappers.MapIncrease(increaseOrders.ReturnValue1, increaseOrders.ReturnValue2, increaseIndex);
}
private static async Task<List<GmxOrder>> GetDecreaseOrders(OrderBookReaderService orderBookReader, string publicAddress, int lastIndex)
{
var increaseIndex = GmxHelpers.GetIndexesRange(lastIndex);
var increaseOrdersFunction = new GetDecreaseOrdersFunction
{
OrderBookAddress = Arbitrum.Address.OrderBook,
Account = publicAddress,
Indices = increaseIndex,
};
var increaseOrders = await orderBookReader.GetDecreaseOrdersQueryAsync(increaseOrdersFunction);
return GmxMappers.MapDecrease(increaseOrders.ReturnValue1, increaseOrders.ReturnValue2, increaseIndex);
}
public static async Task<GmxOrderIndexes> GetLastIndex(Web3 web3, string publicAddress)
{
var orderBook = new OrderBookService(web3, Arbitrum.Address.OrderBook);
var increaseFunction = new IncreaseOrdersIndexFunction
{
ReturnValue1 = publicAddress
};
var decreaseFunction = new DecreaseOrdersIndexFunction
{
ReturnValue1 = publicAddress
};
var swapFunction = new SwapOrdersIndexFunction
{
ReturnValue1 = publicAddress
};
var increaseIndex = await orderBook.IncreaseOrdersIndexQueryAsync(increaseFunction);
var decreaseIndex = await orderBook.DecreaseOrdersIndexQueryAsync(decreaseFunction);
var swapIndex = await orderBook.SwapOrdersIndexQueryAsync(swapFunction);
var indexes = new GmxOrderIndexes
{
SwapIndex = (int)swapIndex > 0 ? (int)swapIndex - 1 : (int)swapIndex,
IncreaseIndex = (int)increaseIndex > 0 ? (int)increaseIndex - 1 : (int)increaseIndex,
DecreaseIndex = (int)decreaseIndex > 0 ? (int)decreaseIndex - 1 : (int)decreaseIndex
};
return indexes;
}
public static async Task<Trade> GetTrade(Web3 web3, string publicAddress, Ticker ticker)
{
var position = await GetGmxPosition(web3, publicAddress, ticker);
return GmxMappers.Map(position, ticker);
}
public static async Task<GmxPosition> GetGmxPosition(Web3 web3, string publicAddress, Ticker ticker)
{
var reader = new ReaderService(web3, Arbitrum.Address.Reader);
var contractAddress = TokenService.GetContractAddress(ticker);
var queryData = GmxHelpers.GetPositionQueryData(new List<string> { contractAddress });
var function = new GetPositionsFunction
{
Vault = Arbitrum.Address.Vault,
Account = publicAddress,
CollateralTokens = queryData.CollateralTokens,
IndexTokens = queryData.IndexTokens,
IsLong = queryData.IsLong
};
var result = await reader.GetPositionsQueryAsync(function);
var positions = GmxMappers.MapPositions(result, queryData);
var position = positions.FirstOrDefault(p => p.IndexToken == contractAddress);
return position;
}
public static async Task<decimal> QuantityInPosition(Web3 web3, string key, Ticker ticker)
{
var position = await GetTrade(web3, key, ticker);
return position?.Quantity ?? 0m;
}
public static async Task<decimal> GetFee(Web3 web3, decimal ethPrice)
{
var positionRouter = new PositionRouterService(web3, Arbitrum.Address.PositionRouter);
var contractAddress = TokenService.GetContractAddress(Ticker.BTC);
var function = new CreateDecreasePositionFunction();
function.Path = new List<string> { contractAddress };
function.IndexToken = contractAddress; // Token to long/short
function.CollateralDelta = new BigInteger(0); // Price in $ to pay the long/short. Ex 11.42$
function.SizeDelta = Web3.Convert.ToWei(100, UnitConversion.EthUnit.Tether);
function.IsLong = true;
function.Receiver = Arbitrum.Address.Zero;
function.AcceptablePrice = GmxHelpers.GetAcceptablePrice(100, true);
function.MinOut = new BigInteger(0);
function.ExecutionFee = Web3.Convert.ToWei(_positionUpdateFees); // Fee required to execute tx
function.WithdrawETH = false;
function.CallbackTarget = Arbitrum.Address.Zero;
function.AmountToSend = Web3.Convert.ToWei(_positionUpdateFees);
function.FromAddress = Arbitrum.Address.Zero;
var totalCost = 0m;
try
{
var gasCost = await positionRouter.ContractHandler.EstimateGasAsync(function);
var gasPrice = GetGasPrice();
var gas = gasPrice * gasCost;
totalCost = ethPrice * Web3.Convert.FromWei(gas, 18);
return totalCost;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return totalCost;
}
}

View File

@@ -0,0 +1,45 @@
using Nethereum.Contracts;
using Nethereum.Contracts.Standards.ERC721.ContractDefinition;
using Nethereum.Web3;
namespace Managing.Infrastructure.Evm.Services;
public static class NftService
{
public static async Task<List<EventLog<TransferEventDTO>>> GetNftEvent(Web3 web3, string owner, string contract)
{
try
{
// Retrieve transfer event of the contract
var transferEvent = web3.Eth.GetEvent<TransferEventDTO>(contract);
// Create IN & OUT filter to filter the return transfers changes
var transferFilterIn = transferEvent.CreateFilterInput<string, string>(null, owner);
var transferFilterOut = transferEvent.CreateFilterInput(owner);
// Retrieve changes based on filter
var transferInLogs = await transferEvent.GetAllChangesAsync(transferFilterIn);
var transferOutLogs = await transferEvent.GetAllChangesAsync(transferFilterOut);
var list = new List<EventLog<TransferEventDTO>>();
// For each transfer IN, we add the event into the list
foreach (var ins in transferInLogs)
{
list.Add(ins);
}
// Remove all transfer OUT of the list because the use might already send the token
foreach (var ins in transferOutLogs)
{
list.Remove(ins);
}
return list;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return null;
}
}
}

View File

@@ -0,0 +1,45 @@
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.SystemTextJson;
using Managing.Domain.Evm;
using static Managing.Common.Enums;
namespace Managing.Infrastructure.Evm.Services;
public static class SubgraphService
{
private const string SUBGRAPH_UNISWAP_V2 = "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2";
private const string SUBGRAPH_CHAINLINK = "https://api.thegraph.com/subgraphs/name/openpredict/chainlink-prices-subgraph";
private const string SUBGRAPH_CHAINLINK_GMX = "https://api.thegraph.com/subgraphs/name/deividask/chainlink";
private const string SUBGRAPH_GBC = "https://api.thegraph.com/subgraphs/name/nissoh/gmx-arbitrum";
public static GraphQLHttpClient GetSubgraphClient(SubgraphProvider subgraphProvider)
{
var url = GetSubgraph(subgraphProvider).Url;
var graphQLOptions = new GraphQLHttpClientOptions
{
EndPoint = new Uri(url)
};
return new GraphQLHttpClient(graphQLOptions, new SystemTextJsonSerializer());
}
private static Subgraph GetSubgraph(SubgraphProvider subgraphProvider)
{
return new Subgraph()
{
SubgraphProvider = subgraphProvider,
Url = GetSubgraphUrl(subgraphProvider)
};
}
private static string GetSubgraphUrl(SubgraphProvider subgraphProvider)
{
return subgraphProvider switch
{
SubgraphProvider.UniswapV2 => SUBGRAPH_UNISWAP_V2,
SubgraphProvider.ChainlinkPrice => SUBGRAPH_CHAINLINK,
SubgraphProvider.ChainlinkGmx => SUBGRAPH_CHAINLINK_GMX,
SubgraphProvider.Gbc => SUBGRAPH_GBC,
_ => throw new Exception("No url for subgraphprovider")
};
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,98 @@
using Managing.Application.Abstractions.Services;
using Managing.Domain.Statistics;
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Models;
using System.Net.Http.Json;
namespace Managing.Infrastructure.Evm.Services;
public class TradaoService : ITradaoService
{
private readonly HttpClient _httpClient;
public TradaoService()
{
_httpClient = new HttpClient(); ;
}
public async Task<List<Trader>> GetBadTrader()
{
var bestTraders = await _httpClient.GetFromJsonAsync<TradaoList>($"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/asc/2592000/0/?current=1&limit=500&order=asc&window=2592000&chain=42161&exchange=gmx&openPosition=0");
if (bestTraders == null || bestTraders.row.Count == 0)
{
return new List<Trader>();
}
return await GetTraderDetails(bestTraders);
}
public async Task<List<Trader>> GetBestTrader()
{
var bestTraders = await _httpClient.GetFromJsonAsync<TradaoList>($"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/desc/2592000/0/?current=1&limit=500&order=desc&window=2592000&chain=42161&exchange=gmx&openPosition=0");
if (bestTraders == null || bestTraders.row.Count == 0)
{
return new List<Trader>();
}
return await GetTraderDetails(bestTraders);
}
public async Task<List<Trade>> GetTrades(string address)
{
var response = await _httpClient.GetFromJsonAsync<TradaoUserDetails>($"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{address}");
var trades = new List<Trade>();
if (response == null) return trades;
foreach (var position in response.openPositions)
{
var trade = new Trade(
DateTime.UtcNow,
position.isLong ? Common.Enums.TradeDirection.Long : Common.Enums.TradeDirection.Short,
Common.Enums.TradeStatus.Filled,
Common.Enums.TradeType.Market,
TokenService.GetTicker(position.indexTokenAddress),
Convert.ToDecimal(position.collateral),
Convert.ToDecimal(position.averagePrice),
Convert.ToDecimal(position.position) / Convert.ToDecimal(position.collateral),
address, position.liqPrice
);
trades.Add(trade);
}
return trades;
}
private async Task<List<Trader>> GetTraderDetails(TradaoList traders)
{
var result = new List<Trader>();
foreach (var trader in traders.row)
{
var response = await _httpClient.GetFromJsonAsync<TradaoUserDetails>($"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{trader.user}");
if (response != null)
result.Add(Map(response, trader.user));
}
return result;
}
private Trader Map(TradaoUserDetails response, string address)
{
return new Trader
{
Address = address,
Winrate = (int)(response.summary.winRate * 100),
Pnl = Convert.ToDecimal(response.summary.pnl),
TradeCount = response.summary.trades,
AverageWin = Convert.ToDecimal(response.summary.averageWin),
AverageLoss = Convert.ToDecimal(response.summary.averageLoss),
Roi = Convert.ToDecimal(response.summary.roi)
};
}
}