Gmx v2 - Funding rates (#6)
* Setup GMX v2 * Add get markets * Map token with service * Add get market info data * Add get markets * Add get market token prices * Get markets infos multicall * Try call datastore * Add some tests to figure out why datastore call dont work * Update funding rates * clean
This commit is contained in:
@@ -29,7 +29,7 @@ public class EvmManager : IEvmManager
|
||||
private readonly Web3 _web3;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly string _password = "!StrongPassword94";
|
||||
private readonly IEnumerable<ISubgraphPrices> _subgraphs;
|
||||
private readonly IEnumerable<ISubgraphPrices> _subgraphs;
|
||||
private Dictionary<string, Dictionary<string, decimal>> _geckoPrices;
|
||||
|
||||
public EvmManager(IEnumerable<ISubgraphPrices> subgraphs)
|
||||
@@ -38,7 +38,9 @@ public class EvmManager : IEvmManager
|
||||
_web3 = new Web3(defaultChain.RpcUrl);
|
||||
_httpClient = new HttpClient();
|
||||
_subgraphs = subgraphs;
|
||||
_geckoPrices = _geckoPrices != null && _geckoPrices.Any() ? _geckoPrices : new Dictionary<string, Dictionary<string, decimal>>();
|
||||
_geckoPrices = _geckoPrices != null && _geckoPrices.Any()
|
||||
? _geckoPrices
|
||||
: new Dictionary<string, Dictionary<string, decimal>>();
|
||||
SetupPrices();
|
||||
}
|
||||
|
||||
@@ -131,7 +133,8 @@ public class EvmManager : IEvmManager
|
||||
return holders;
|
||||
}
|
||||
|
||||
public async Task<List<EventLog<Nethereum.Contracts.Standards.ERC721.ContractDefinition.TransferEventDTO>>> GetNftEvent(string contractAddress, string tokenOwner)
|
||||
public async Task<List<EventLog<Nethereum.Contracts.Standards.ERC721.ContractDefinition.TransferEventDTO>>>
|
||||
GetNftEvent(string contractAddress, string tokenOwner)
|
||||
{
|
||||
return await NftService.GetNftEvent(_web3, tokenOwner, contractAddress);
|
||||
}
|
||||
@@ -186,15 +189,16 @@ public class EvmManager : IEvmManager
|
||||
{
|
||||
var web3 = new Web3(chain.RpcUrl);
|
||||
var etherBalance = Web3.Convert.FromWei(await web3.Eth.GetBalance.SendRequestAsync(account));
|
||||
var etherPrice = (await GetPrices(new List<string> { "ethereum"}))["ethereum"]["usd"];
|
||||
var etherPrice = (await GetPrices(new List<string> { "ethereum" }))["ethereum"]["usd"];
|
||||
|
||||
return new EvmBalance() { Balance = etherBalance, Price = etherPrice, TokenName = "ETH", Value = etherBalance * etherPrice };
|
||||
return new EvmBalance()
|
||||
{ Balance = etherBalance, Price = etherPrice, TokenName = "ETH", Value = etherBalance * etherPrice };
|
||||
}
|
||||
|
||||
public async Task<List<EvmBalance>> GetAllBalances(Domain.Evm.Chain chain, string publicAddress)
|
||||
{
|
||||
var balances = new List<EvmBalance>();
|
||||
|
||||
|
||||
|
||||
var web3 = new Web3(chain.RpcUrl);
|
||||
SetupPrices();
|
||||
@@ -213,6 +217,7 @@ public class EvmManager : IEvmManager
|
||||
// TODO : handle exception
|
||||
}
|
||||
}
|
||||
|
||||
var etherBalance = await GetEtherBalance(chain, publicAddress);
|
||||
etherBalance.Chain = chain;
|
||||
balances.Add(etherBalance);
|
||||
@@ -267,7 +272,8 @@ public class EvmManager : IEvmManager
|
||||
return evmBalance;
|
||||
}
|
||||
|
||||
public async Task<List<EvmBalance>> GetBalances(Domain.Evm.Chain chain, int page, int pageSize, string publicAddress)
|
||||
public async Task<List<EvmBalance>> GetBalances(Domain.Evm.Chain chain, int page, int pageSize,
|
||||
string publicAddress)
|
||||
{
|
||||
var callList = new List<IMulticallInputOutput>();
|
||||
var startItem = (page * pageSize);
|
||||
@@ -281,6 +287,7 @@ public class EvmManager : IEvmManager
|
||||
tokens[i].Address);
|
||||
callList.Add(call);
|
||||
}
|
||||
|
||||
var evmTokens = new List<(EvmBalance Balance, GeckoToken GeckoToken)>();
|
||||
|
||||
try
|
||||
@@ -292,7 +299,8 @@ public class EvmManager : IEvmManager
|
||||
|
||||
for (int i = startItem; i < totaItemsToFetch; i++)
|
||||
{
|
||||
var balance = ((MulticallInputOutput<BalanceOfFunction, BalanceOfOutputDTO>)callList[i - startItem]).Output.Balance;
|
||||
var balance = ((MulticallInputOutput<BalanceOfFunction, BalanceOfOutputDTO>)callList[i - startItem])
|
||||
.Output.Balance;
|
||||
if (balance > 0)
|
||||
{
|
||||
var tokenBalance = new EvmBalance()
|
||||
@@ -303,7 +311,8 @@ public class EvmManager : IEvmManager
|
||||
Chain = chain
|
||||
};
|
||||
|
||||
var geckoToken = geckoTokens.FirstOrDefault(x => string.Equals(x.Symbol, tokens[i].Symbol, StringComparison.InvariantCultureIgnoreCase));
|
||||
var geckoToken = geckoTokens.FirstOrDefault(x =>
|
||||
string.Equals(x.Symbol, tokens[i].Symbol, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
evmTokens.Add((tokenBalance, geckoToken));
|
||||
}
|
||||
@@ -324,7 +333,6 @@ public class EvmManager : IEvmManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -338,7 +346,8 @@ public class EvmManager : IEvmManager
|
||||
public async Task<Dictionary<string, Dictionary<string, decimal>>> GetPrices(List<string> geckoIds)
|
||||
{
|
||||
var idsCombined = string.Join(",", geckoIds);
|
||||
return await _httpClient.GetFromJsonAsync<Dictionary<string, Dictionary<string, decimal>>>("https://api.coingecko.com/api/v3/simple/price?ids=" + idsCombined + "&vs_currencies=usd");
|
||||
return await _httpClient.GetFromJsonAsync<Dictionary<string, Dictionary<string, decimal>>>(
|
||||
"https://api.coingecko.com/api/v3/simple/price?ids=" + idsCombined + "&vs_currencies=usd");
|
||||
}
|
||||
|
||||
public async Task<List<EvmBalance>> GetAllBalancesOnAllChain(string publicAddress)
|
||||
@@ -354,11 +363,12 @@ public class EvmManager : IEvmManager
|
||||
return chainBalances;
|
||||
}
|
||||
|
||||
public async Task<List<Candle>> GetCandles(SubgraphProvider subgraphProvider, Ticker ticker, DateTime startDate, Timeframe timeframe)
|
||||
public async Task<List<Candle>> GetCandles(SubgraphProvider subgraphProvider, Ticker ticker, DateTime startDate,
|
||||
Timeframe timeframe)
|
||||
{
|
||||
|
||||
string gmxTimeframe = GmxHelpers.GeTimeframe(timeframe);
|
||||
var gmxPrices = await _httpClient.GetFromJsonAsync<GmxPrices>($"https://stats.gmx.io/api/candles/{ticker}?preferableChainId=42161&period={gmxTimeframe}&from={startDate.ToUnixTimestamp()}&preferableSource=fast");
|
||||
var gmxPrices = await _httpClient.GetFromJsonAsync<GmxPrices>(
|
||||
$"https://stats.gmx.io/api/candles/{ticker}?preferableChainId=42161&period={gmxTimeframe}&from={startDate.ToUnixTimestamp()}&preferableSource=fast");
|
||||
//var subgraph = _subgraphs.First(s => s.GetProvider() == subgraphProvider);
|
||||
//var prices = await subgraph.GetPrices(ticker, startDate, timeframe);
|
||||
|
||||
@@ -401,7 +411,8 @@ public class EvmManager : IEvmManager
|
||||
|
||||
public async Task<Candle> GetCandle(SubgraphProvider subgraphProvider, Ticker ticker)
|
||||
{
|
||||
var lastPrices = await GetCandles(subgraphProvider, ticker, DateTime.UtcNow.AddMinutes(-15), Timeframe.FiveMinutes);
|
||||
var lastPrices = await GetCandles(subgraphProvider, ticker, DateTime.UtcNow.AddMinutes(-15),
|
||||
Timeframe.FiveMinutes);
|
||||
return lastPrices.Last();
|
||||
}
|
||||
|
||||
@@ -482,7 +493,6 @@ public class EvmManager : IEvmManager
|
||||
string receiverAddress,
|
||||
Web3 web3)
|
||||
{
|
||||
|
||||
var contractAddress = TokenService.GetContractAddress(ticker);
|
||||
var transactionMessage = new TransferFunction
|
||||
{
|
||||
@@ -495,7 +505,8 @@ public class EvmManager : IEvmManager
|
||||
var transferReceipt =
|
||||
await transferHandler.SendRequestAndWaitForReceiptAsync(contractAddress, transactionMessage);
|
||||
|
||||
var transaction = await web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(transferReceipt.TransactionHash);
|
||||
var transaction =
|
||||
await web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(transferReceipt.TransactionHash);
|
||||
return transaction != null;
|
||||
}
|
||||
|
||||
@@ -523,7 +534,6 @@ public class EvmManager : IEvmManager
|
||||
try
|
||||
{
|
||||
trade = await GmxService.IncreasePosition(web3, account.Key, ticker, direction, price, quantity, leverage);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -558,9 +568,9 @@ public class EvmManager : IEvmManager
|
||||
return trade;
|
||||
}
|
||||
|
||||
public async Task<Trade> DecreaseOrder(Account account, TradeType tradeType, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
|
||||
public async Task<Trade> DecreaseOrder(Account account, TradeType tradeType, Ticker ticker,
|
||||
TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
|
||||
{
|
||||
|
||||
var wallet = new Wallet(account.Secret, _password).GetAccount(account.Key);
|
||||
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
|
||||
var web3 = new Web3(wallet, chain.RpcUrl);
|
||||
@@ -568,7 +578,8 @@ public class EvmManager : IEvmManager
|
||||
Trade trade = null;
|
||||
try
|
||||
{
|
||||
trade = await GmxService.DecreaseOrder(web3, tradeType, account.Key, ticker, direction, price, quantity, leverage);
|
||||
trade = await GmxService.DecreaseOrder(web3, tradeType, account.Key, ticker, direction, price, quantity,
|
||||
leverage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -591,13 +602,14 @@ public class EvmManager : IEvmManager
|
||||
return await GmxService.GetTrade(web3, reference, ticker);
|
||||
}
|
||||
|
||||
public Task<List<FundingRate>> GetFundingRates()
|
||||
public async Task<List<FundingRate>> GetFundingRates()
|
||||
{
|
||||
// Call GMX v2
|
||||
// Call hyperliquid
|
||||
|
||||
// Map the results
|
||||
return Task.FromResult(new List<FundingRate>());
|
||||
var chain = ChainService.GetChain(Constants.Chains.Arbitrum);
|
||||
var web3 = new Web3(chain.RpcUrl);
|
||||
var service = new GmxV2Service();
|
||||
var fundingRates = await service.GetFundingRates(web3);
|
||||
|
||||
return fundingRates;
|
||||
}
|
||||
|
||||
public async Task<decimal> QuantityInPosition(string chainName, string publicAddress, Ticker ticker)
|
||||
@@ -626,5 +638,4 @@ public class EvmManager : IEvmManager
|
||||
|
||||
return GmxHelpers.Map(orders, ticker);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GraphQL.Client" Version="6.0.5" />
|
||||
<PackageReference Include="GraphQL.Client.Abstractions" Version="6.0.5" />
|
||||
<PackageReference Include="GraphQL.Client.Serializer.SystemTextJson" Version="6.0.5" />
|
||||
<PackageReference Include="GraphQL.Query.Builder" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.36" />
|
||||
<PackageReference Include="Nethereum.HdWallet" Version="4.20.0" />
|
||||
<PackageReference Include="Nethereum.StandardTokenEIP20" Version="4.20.0" />
|
||||
<PackageReference Include="Nethereum.Web3" Version="4.20.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GraphQL.Client" Version="6.0.5"/>
|
||||
<PackageReference Include="GraphQL.Client.Abstractions" Version="6.0.5"/>
|
||||
<PackageReference Include="GraphQL.Client.Serializer.SystemTextJson" Version="6.0.5"/>
|
||||
<PackageReference Include="GraphQL.Query.Builder" Version="2.0.2"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1"/>
|
||||
<PackageReference Include="NBitcoin" Version="7.0.36"/>
|
||||
<PackageReference Include="Nethereum.HdWallet" Version="4.20.0"/>
|
||||
<PackageReference Include="Nethereum.Hex" Version="4.21.3"/>
|
||||
<PackageReference Include="Nethereum.StandardTokenEIP20" Version="4.20.0"/>
|
||||
<PackageReference Include="Nethereum.Web3" Version="4.21.2"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Managing.Application.Abstractions\Managing.Application.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\Managing.Tools.ABI\Managing.Tools.ABI.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Managing.ABI.GmxV2\Managing.ABI.GmxV2.csproj"/>
|
||||
<ProjectReference Include="..\Managing.Application.Abstractions\Managing.Application.Abstractions.csproj"/>
|
||||
<ProjectReference Include="..\Managing.Tools.ABI\Managing.Tools.ABI.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
17
src/Managing.Infrastructure.Web3/Models/Gmx/v2/GmxMarket.cs
Normal file
17
src/Managing.Infrastructure.Web3/Models/Gmx/v2/GmxMarket.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxMarket
|
||||
{
|
||||
public string MarketToken { get; set; }
|
||||
public string IndexToken { get; set; }
|
||||
public string LongToken { get; set; }
|
||||
public string ShortToken { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Symbol { get; set; }
|
||||
public bool IsSpotOnly { get; set; }
|
||||
public bool IsSameCollaterals { get; set; }
|
||||
public BigInteger FundingFactorPerSecond { get; set; }
|
||||
public bool LongsPayShorts { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxMarketInfo
|
||||
{
|
||||
public GmxMarket Market { get; set; }
|
||||
public GmxMarketTokenPrice MarketTokenPriceMax { get; set; }
|
||||
public GmxMarketTokenPrice MarketTokenPriceMin { get; set; }
|
||||
public GmxMarketInfos Infos { get; set; }
|
||||
}
|
||||
110
src/Managing.Infrastructure.Web3/Models/Gmx/v2/GmxMarketInfos.cs
Normal file
110
src/Managing.Infrastructure.Web3/Models/Gmx/v2/GmxMarketInfos.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxMarketInfos
|
||||
{
|
||||
public bool IsDisabled { get; set; }
|
||||
|
||||
public GmxTokenData LongToken { get; set; }
|
||||
public GmxTokenData ShortToken { get; set; }
|
||||
public GmxTokenData IndexToken { get; set; }
|
||||
|
||||
public BigInteger LongPoolAmount { get; set; }
|
||||
public BigInteger ShortPoolAmount { get; set; }
|
||||
|
||||
public BigInteger MaxLongPoolAmount { get; set; }
|
||||
public BigInteger MaxShortPoolAmount { get; set; }
|
||||
public BigInteger MaxLongPoolUsdForDeposit { get; set; }
|
||||
public BigInteger MaxShortPoolUsdForDeposit { get; set; }
|
||||
|
||||
public BigInteger LongPoolAmountAdjustment { get; set; }
|
||||
public BigInteger ShortPoolAmountAdjustment { get; set; }
|
||||
|
||||
public BigInteger PoolValueMax { get; set; }
|
||||
public BigInteger PoolValueMin { get; set; }
|
||||
|
||||
public BigInteger ReserveFactorLong { get; set; }
|
||||
public BigInteger ReserveFactorShort { get; set; }
|
||||
|
||||
public BigInteger OpenInterestReserveFactorLong { get; set; }
|
||||
public BigInteger OpenInterestReserveFactorShort { get; set; }
|
||||
|
||||
public BigInteger MaxOpenInterestLong { get; set; }
|
||||
public BigInteger MaxOpenInterestShort { get; set; }
|
||||
|
||||
public BigInteger BorrowingFactorLong { get; set; }
|
||||
public BigInteger BorrowingFactorShort { get; set; }
|
||||
public BigInteger BorrowingExponentFactorLong { get; set; }
|
||||
public BigInteger BorrowingExponentFactorShort { get; set; }
|
||||
|
||||
public BigInteger FundingFactor { get; set; }
|
||||
public BigInteger FundingExponentFactor { get; set; }
|
||||
public BigInteger FundingIncreaseFactorPerSecond { get; set; }
|
||||
public BigInteger FundingDecreaseFactorPerSecond { get; set; }
|
||||
public BigInteger ThresholdForStableFunding { get; set; }
|
||||
public BigInteger ThresholdForDecreaseFunding { get; set; }
|
||||
public BigInteger MinFundingFactorPerSecond { get; set; }
|
||||
public BigInteger MaxFundingFactorPerSecond { get; set; }
|
||||
|
||||
public BigInteger TotalBorrowingFees { get; set; }
|
||||
|
||||
public BigInteger PositionImpactPoolAmount { get; set; }
|
||||
public BigInteger MinPositionImpactPoolAmount { get; set; }
|
||||
public BigInteger PositionImpactPoolDistributionRate { get; set; }
|
||||
|
||||
public BigInteger MinCollateralFactor { get; set; }
|
||||
public BigInteger MinCollateralFactorForOpenInterestLong { get; set; }
|
||||
public BigInteger MinCollateralFactorForOpenInterestShort { get; set; }
|
||||
|
||||
public BigInteger SwapImpactPoolAmountLong { get; set; }
|
||||
public BigInteger SwapImpactPoolAmountShort { get; set; }
|
||||
|
||||
public BigInteger MaxPnlFactorForTradersLong { get; set; }
|
||||
public BigInteger MaxPnlFactorForTradersShort { get; set; }
|
||||
|
||||
public BigInteger PnlLongMin { get; set; }
|
||||
public BigInteger PnlLongMax { get; set; }
|
||||
public BigInteger PnlShortMin { get; set; }
|
||||
public BigInteger PnlShortMax { get; set; }
|
||||
|
||||
public BigInteger NetPnlMin { get; set; }
|
||||
public BigInteger NetPnlMax { get; set; }
|
||||
|
||||
public BigInteger? ClaimableFundingAmountLong { get; set; }
|
||||
public BigInteger? ClaimableFundingAmountShort { get; set; }
|
||||
|
||||
public BigInteger LongInterestUsd { get; set; }
|
||||
public BigInteger ShortInterestUsd { get; set; }
|
||||
public BigInteger LongInterestInTokens { get; set; }
|
||||
public BigInteger ShortInterestInTokens { get; set; }
|
||||
|
||||
public BigInteger PositionFeeFactorForPositiveImpact { get; set; }
|
||||
public BigInteger PositionFeeFactorForNegativeImpact { get; set; }
|
||||
public BigInteger PositionImpactFactorPositive { get; set; }
|
||||
public BigInteger PositionImpactFactorNegative { get; set; }
|
||||
public BigInteger MaxPositionImpactFactorPositive { get; set; }
|
||||
public BigInteger MaxPositionImpactFactorNegative { get; set; }
|
||||
public BigInteger MaxPositionImpactFactorForLiquidations { get; set; }
|
||||
public BigInteger PositionImpactExponentFactor { get; set; }
|
||||
|
||||
public BigInteger SwapFeeFactorForPositiveImpact { get; set; }
|
||||
public BigInteger SwapFeeFactorForNegativeImpact { get; set; }
|
||||
public BigInteger SwapImpactFactorPositive { get; set; }
|
||||
public BigInteger SwapImpactFactorNegative { get; set; }
|
||||
public BigInteger SwapImpactExponentFactor { get; set; }
|
||||
|
||||
public BigInteger BorrowingFactorPerSecondForLongs { get; set; }
|
||||
public BigInteger BorrowingFactorPerSecondForShorts { get; set; }
|
||||
|
||||
public BigInteger FundingFactorPerSecond { get; set; }
|
||||
public bool LongsPayShorts { get; set; }
|
||||
|
||||
public BigInteger VirtualPoolAmountForLongToken { get; set; }
|
||||
public BigInteger VirtualPoolAmountForShortToken { get; set; }
|
||||
public BigInteger VirtualInventoryForPositions { get; set; }
|
||||
|
||||
public string VirtualMarketId { get; set; }
|
||||
public string VirtualLongTokenId { get; set; }
|
||||
public string VirtualShortTokenId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxMarketTokenPrice
|
||||
{
|
||||
public BigInteger PoolValue { get; set; }
|
||||
public BigInteger LongPnl { get; set; }
|
||||
public BigInteger ShortPnl { get; set; }
|
||||
public BigInteger NetPnl { get; set; }
|
||||
public BigInteger LongTokenAmount { get; set; }
|
||||
public BigInteger ShortTokenAmount { get; set; }
|
||||
public BigInteger LongTokenUsd { get; set; }
|
||||
public BigInteger ShortTokenUsd { get; set; }
|
||||
public BigInteger TotalBorrowingFees { get; set; }
|
||||
public BigInteger BorrowingFeePoolFactor { get; set; }
|
||||
public BigInteger ImpactPoolAmount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxStoreKeys
|
||||
{
|
||||
[JsonProperty("longPoolAmount", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string LongPoolAmount { get; set; }
|
||||
|
||||
[JsonProperty("shortPoolAmount", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ShortPoolAmount { get; set; }
|
||||
|
||||
[JsonProperty("positionImpactPoolAmount", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string PositionImpactPoolAmount { get; set; }
|
||||
|
||||
[JsonProperty("swapImpactPoolAmountLong", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string SwapImpactPoolAmountLong { get; set; }
|
||||
|
||||
[JsonProperty("swapImpactPoolAmountShort", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string SwapImpactPoolAmountShort { get; set; }
|
||||
|
||||
[JsonProperty("longInterestUsingLongToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string LongInterestUsingLongToken { get; set; }
|
||||
|
||||
[JsonProperty("longInterestUsingShortToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string LongInterestUsingShortToken { get; set; }
|
||||
|
||||
[JsonProperty("shortInterestUsingLongToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ShortInterestUsingLongToken { get; set; }
|
||||
|
||||
[JsonProperty("shortInterestUsingShortToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ShortInterestUsingShortToken { get; set; }
|
||||
|
||||
[JsonProperty("longInterestInTokensUsingLongToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string LongInterestInTokensUsingLongToken { get; set; }
|
||||
|
||||
[JsonProperty("longInterestInTokensUsingShortToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string LongInterestInTokensUsingShortToken { get; set; }
|
||||
|
||||
[JsonProperty("shortInterestInTokensUsingLongToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ShortInterestInTokensUsingLongToken { get; set; }
|
||||
|
||||
[JsonProperty("shortInterestInTokensUsingShortToken", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ShortInterestInTokensUsingShortToken { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxTokenBalances
|
||||
{
|
||||
public Dictionary<string, BigInteger> Balances { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Numerics;
|
||||
using Managing.ABI.GmxV2.Reader.ContractDefinition;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxTokenData : GmxToken
|
||||
{
|
||||
public MarketPrice Price { get; set; }
|
||||
public BigInteger? Balance { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Numerics;
|
||||
using Managing.ABI.GmxV2.Reader.ContractDefinition;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxTokenPriceData
|
||||
{
|
||||
public MarketPrice Price { get; set; }
|
||||
public BigInteger? Balance { get; set; }
|
||||
public BigInteger? TotalSupply { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class MarketStats
|
||||
{
|
||||
public string Market { get; set; }
|
||||
public decimal PoolValue { get; set; }
|
||||
public decimal PoolBalance { get; set; }
|
||||
public decimal PoolCapLong { get; set; }
|
||||
public decimal PoolCapShort { get; set; }
|
||||
public decimal PnL { get; set; }
|
||||
public decimal BorrowingFees { get; set; }
|
||||
public decimal FundingApr { get; set; }
|
||||
public decimal OpenInterest { get; set; }
|
||||
public decimal LiquidityLong { get; set; }
|
||||
public decimal LiquidityShort { get; set; }
|
||||
public decimal Vipositions { get; set; }
|
||||
public decimal Viswaps { get; set; }
|
||||
public decimal PositionImpactPool { get; set; }
|
||||
public string Config { get; set; }
|
||||
}
|
||||
@@ -4,54 +4,68 @@ public class Arbitrum
|
||||
{
|
||||
public class Address
|
||||
{
|
||||
public const string ETH = "0x82af49447d8a07e3bd95bd0d56f35241523fbab1";
|
||||
public const string WBTC = "0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f";
|
||||
public const string LINK = "0xf97f4df75117a78c1a5a0dbb814af92458539fb4";
|
||||
public const string UNI = "0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0";
|
||||
public const string ETH = "0x82af49447d8a07e3bd95bd0d56f35241523fbab1";
|
||||
public const string WBTC = "0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f";
|
||||
public const string LINK = "0xf97f4df75117a78c1a5a0dbb814af92458539fb4";
|
||||
public const string UNI = "0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0";
|
||||
|
||||
public const string USDC = "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8";
|
||||
public const string USDT = "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9";
|
||||
public const string DAI = "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1";
|
||||
public const string MIM = "0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A";
|
||||
public const string FRAX = "0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F";
|
||||
public const string USDC = "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8";
|
||||
public const string USDT = "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9";
|
||||
public const string DAI = "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1";
|
||||
public const string MIM = "0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A";
|
||||
public const string FRAX = "0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F";
|
||||
|
||||
public const string Vault = "0x489ee077994B6658eAfA855C308275EAd8097C4A";
|
||||
public const string VaultPriceFeed = "0x2d68011bcA022ed0E474264145F46CC4de96a002";
|
||||
public const string Router = "0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064";
|
||||
public const string VaultReader = "0xfebB9f4CAC4cD523598fE1C5771181440143F24A";
|
||||
public const string Reader = "0xF09eD52638c22cc3f1D7F5583e3699A075e601B2";
|
||||
public const string GlpManager = "0x321F653eED006AD1C29D174e17d96351BDe22649";
|
||||
public const string RewardRouter = "0xc73d553473dC65CE56db96c58e6a091c20980fbA";
|
||||
public const string RewardReader = "0xe725Ad0ce3eCf68A7B93d8D8091E83043Ff12e9A";
|
||||
public const string Vault = "0x489ee077994B6658eAfA855C308275EAd8097C4A";
|
||||
public const string VaultPriceFeed = "0x2d68011bcA022ed0E474264145F46CC4de96a002";
|
||||
public const string Router = "0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064";
|
||||
public const string VaultReader = "0xfebB9f4CAC4cD523598fE1C5771181440143F24A";
|
||||
public const string Reader = "0xF09eD52638c22cc3f1D7F5583e3699A075e601B2";
|
||||
public const string GlpManager = "0x321F653eED006AD1C29D174e17d96351BDe22649";
|
||||
public const string RewardRouter = "0xc73d553473dC65CE56db96c58e6a091c20980fbA";
|
||||
public const string RewardReader = "0xe725Ad0ce3eCf68A7B93d8D8091E83043Ff12e9A";
|
||||
|
||||
public const string GLP = "0x4277f8f2c384827b5273592ff7cebd9f2c1ac258";
|
||||
public const string GMX = "0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a";
|
||||
public const string ES_GMX = "0xf42ae1d54fd613c9bb14810b0588faaa09a426ca";
|
||||
public const string BN_GMX = "0x35247165119B69A40edD5304969560D0ef486921";
|
||||
public const string USDG = "0x45096e7aA921f27590f8F19e457794EB09678141";
|
||||
public const string GLP = "0x4277f8f2c384827b5273592ff7cebd9f2c1ac258";
|
||||
public const string GMX = "0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a";
|
||||
public const string ES_GMX = "0xf42ae1d54fd613c9bb14810b0588faaa09a426ca";
|
||||
public const string BN_GMX = "0x35247165119B69A40edD5304969560D0ef486921";
|
||||
public const string USDG = "0x45096e7aA921f27590f8F19e457794EB09678141";
|
||||
|
||||
public const string StakedGmxTracker = "0x908C4D94D34924765f1eDc22A1DD098397c59dD4";
|
||||
public const string BonusGmxTracker = "0x4d268a7d4C16ceB5a606c173Bd974984343fea13";
|
||||
public const string FeeGmxTracker = "0xd2D1162512F927a7e282Ef43a362659E4F2a728F";
|
||||
public const string FeeGlpTracker = "0x4e971a87900b931fF39d1Aad67697F49835400b6";
|
||||
public const string StakedGlpTracker = "0x1aDDD80E6039594eE970E5872D247bf0414C8903";
|
||||
public const string StakedGmxTracker = "0x908C4D94D34924765f1eDc22A1DD098397c59dD4";
|
||||
public const string BonusGmxTracker = "0x4d268a7d4C16ceB5a606c173Bd974984343fea13";
|
||||
public const string FeeGmxTracker = "0xd2D1162512F927a7e282Ef43a362659E4F2a728F";
|
||||
public const string FeeGlpTracker = "0x4e971a87900b931fF39d1Aad67697F49835400b6";
|
||||
public const string StakedGlpTracker = "0x1aDDD80E6039594eE970E5872D247bf0414C8903";
|
||||
|
||||
public const string StakedGmxDistributor = "0x23208B91A98c7C1CD9FE63085BFf68311494F193";
|
||||
public const string StakedGlpDistributor = "0x60519b48ec4183a61ca2B8e37869E675FD203b34";
|
||||
public const string StakedGmxDistributor = "0x23208B91A98c7C1CD9FE63085BFf68311494F193";
|
||||
public const string StakedGlpDistributor = "0x60519b48ec4183a61ca2B8e37869E675FD203b34";
|
||||
|
||||
public const string GmxVester = "0x199070DDfd1CFb69173aa2F7e20906F26B363004";
|
||||
public const string GlpVester = "0xA75287d2f8b217273E7FCD7E86eF07D33972042E";
|
||||
public const string GmxVester = "0x199070DDfd1CFb69173aa2F7e20906F26B363004";
|
||||
public const string GlpVester = "0xA75287d2f8b217273E7FCD7E86eF07D33972042E";
|
||||
|
||||
public const string OrderBook = "0x09f77E8A13De9a35a7231028187e9fD5DB8a2ACB";
|
||||
public const string OrderExecutor = "0x7257ac5D0a0aaC04AA7bA2AC0A6Eb742E332c3fB";
|
||||
public const string OrderBookReader = "0xa27C20A7CF0e1C68C0460706bB674f98F362Bc21";
|
||||
public const string OrderBook = "0x09f77E8A13De9a35a7231028187e9fD5DB8a2ACB";
|
||||
public const string OrderExecutor = "0x7257ac5D0a0aaC04AA7bA2AC0A6Eb742E332c3fB";
|
||||
public const string OrderBookReader = "0xa27C20A7CF0e1C68C0460706bB674f98F362Bc21";
|
||||
|
||||
public const string FastPriceFeed = "0x1a0ad27350cccd6f7f168e052100b4960efdb774";
|
||||
public const string PositionRouter = "0xb87a436B93fFE9D75c5cFA7bAcFff96430b09868";
|
||||
public const string PositionManager = "0x87a4088Bd721F83b6c2E5102e2FA47022Cb1c831";
|
||||
public const string FastPriceFeed = "0x1a0ad27350cccd6f7f168e052100b4960efdb774";
|
||||
public const string PositionRouter = "0xb87a436B93fFE9D75c5cFA7bAcFff96430b09868";
|
||||
public const string PositionManager = "0x87a4088Bd721F83b6c2E5102e2FA47022Cb1c831";
|
||||
|
||||
public const string UniswapGmxEthPool = "0x80A9ae39310abf666A87C743d6ebBD0E8C42158E";
|
||||
public const string UniswapGmxEthPool = "0x80A9ae39310abf666A87C743d6ebBD0E8C42158E";
|
||||
|
||||
public static string Zero = "0x0000000000000000000000000000000000000000";
|
||||
public static string Zero = "0x0000000000000000000000000000000000000000";
|
||||
}
|
||||
}
|
||||
|
||||
public class AddressV2
|
||||
{
|
||||
public const string DataStore = "0xFD70de6b91282D8017aA4E741e9Ae325CAb992d8";
|
||||
public const string EventEmitter = "0xC8ee91A54287DB53897056e12D9819156D3822Fb";
|
||||
public const string SubaccountRouter = "0x9F48160eDc3Ad78F4cA0E3FDF54A75D8FB228452";
|
||||
public const string ExchangeRouter = "0x69C527fC77291722b52649E45c838e41be8Bf5d5";
|
||||
public const string DepositVault = "0xF89e77e8Dc11691C9e8757e84aaFbCD8A67d7A55";
|
||||
public const string WithdrawalVault = "0x0628D46b5D145f183AdB6Ef1f2c97eD1C4701C55";
|
||||
public const string OrderVault = "0x31eF83a530Fde1B38EE9A18093A333D8Bbbc40D5";
|
||||
public const string Reader = "0x5Ca84c34a381434786738735265b9f3FD814b824";
|
||||
public const string SyntheticsRouter = "0x7452c558d45f8afC8c83dAe62C3f8A5BE19c71f6";
|
||||
public const string Multicall = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,8 @@ public static class ChainService
|
||||
return new Chain()
|
||||
{
|
||||
Name = Constants.Chains.Arbitrum,
|
||||
RpcUrl = RPC_ARBITRUM
|
||||
RpcUrl = RPC_ARBITRUM,
|
||||
ChainId = 42161
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +47,8 @@ public static class ChainService
|
||||
return new Chain()
|
||||
{
|
||||
Name = Constants.Chains.Ethereum,
|
||||
RpcUrl = RPC_ETHEREUM
|
||||
RpcUrl = RPC_ETHEREUM,
|
||||
ChainId = 1
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,4 +69,4 @@ public static class ChainService
|
||||
RpcUrl = RPC_ETHEREUM_GOERLI
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,26 @@
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Infrastructure.Evm.Models.Gmx;
|
||||
using Managing.Infrastructure.Evm.Referentials;
|
||||
using Nethereum.Web3;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using Managing.ABI.GmxV2.Reader.ContractDefinition;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Infrastructure.Evm.Models.Gmx;
|
||||
using Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
using Managing.Infrastructure.Evm.Referentials;
|
||||
using Nethereum.Hex.HexConvertors.Extensions;
|
||||
using Nethereum.Web3;
|
||||
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)
|
||||
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>();
|
||||
@@ -68,6 +73,7 @@ public static class GmxHelpers
|
||||
{
|
||||
priceBasisPoints = basisPointDivisor + allowedSlippage;
|
||||
}
|
||||
|
||||
var test = Web3.Convert.ToWei(price, toDecimal) * new BigInteger(priceBasisPoints);
|
||||
|
||||
var priceLimit = test / Web3.Convert.ToWei(basisPointDivisor, 0);
|
||||
@@ -83,20 +89,20 @@ public static class GmxHelpers
|
||||
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),
|
||||
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),
|
||||
Convert.ToDecimal(order.TriggerPrice),
|
||||
null,
|
||||
"", ""
|
||||
);
|
||||
);
|
||||
|
||||
return trade;
|
||||
}
|
||||
@@ -136,4 +142,119 @@ public static class GmxHelpers
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetRandomAddress()
|
||||
{
|
||||
var ecKey = Nethereum.Signer.EthECKey.GenerateKey();
|
||||
var privateKey = ecKey.GetPrivateKeyAsBytes().ToHex();
|
||||
var account = new Nethereum.Signer.EthECKey(privateKey).GetPublicAddress();
|
||||
return account;
|
||||
}
|
||||
|
||||
internal static MarketPrices GetContractMarketPrices(List<GmxTokenData> tokens, GmxMarket market)
|
||||
{
|
||||
var indexToken = tokens.First(t => GmxV2Helpers.SameAddress(t.Address, market.IndexToken));
|
||||
var longToken = tokens.First(t => GmxV2Helpers.SameAddress(t.Address, market.LongToken));
|
||||
var shortToken = tokens.First(t => GmxV2Helpers.SameAddress(t.Address, market.ShortToken));
|
||||
|
||||
return new MarketPrices()
|
||||
{
|
||||
IndexTokenPrice = ConvertToContractTokenPrice(indexToken.Price, indexToken.Decimals),
|
||||
LongTokenPrice = ConvertToContractTokenPrice(longToken.Price, longToken.Decimals),
|
||||
ShortTokenPrice = ConvertToContractTokenPrice(shortToken.Price, shortToken.Decimals)
|
||||
};
|
||||
}
|
||||
|
||||
private static MarketPrice ConvertToContractTokenPrice(MarketPrice marketPrice, int tokenDecimals)
|
||||
{
|
||||
var price = new MarketPrice();
|
||||
price.Min = ConvertToContractPrice(tokenDecimals, marketPrice.Min);
|
||||
price.Max = ConvertToContractPrice(tokenDecimals, marketPrice.Max);
|
||||
return price;
|
||||
}
|
||||
|
||||
private static BigInteger ConvertToContractPrice(decimal price, BigInteger tokenDecimals)
|
||||
{
|
||||
// Convert the decimal price to a BigInteger, scaling it up to maintain precision.
|
||||
BigInteger scaledPrice = new BigInteger(price * (decimal)Math.Pow(10, (double)tokenDecimals));
|
||||
|
||||
// Return the scaled price directly since it's already adjusted for token decimals.
|
||||
return scaledPrice;
|
||||
}
|
||||
|
||||
public static Ticker GetTicker(string marketName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var indexToken = marketName.Split(' ')[0];
|
||||
var ticker = MiscExtensions.ParseEnum<Ticker>(indexToken);
|
||||
return ticker;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return Ticker.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public static decimal FormatAmount(BigInteger? amount, int tokenDecimals, int? displayDecimals = null,
|
||||
bool useCommas = false, string defaultValue = "...")
|
||||
{
|
||||
if (amount == null || amount == BigInteger.Zero)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (displayDecimals == null)
|
||||
{
|
||||
displayDecimals = 4;
|
||||
}
|
||||
|
||||
var amountStr = Web3.Convert.FromWei(amount.Value, tokenDecimals).ToString(CultureInfo.InvariantCulture);
|
||||
amountStr = LimitDecimals(amountStr, displayDecimals.Value);
|
||||
|
||||
if (displayDecimals != 0)
|
||||
{
|
||||
amountStr = PadDecimals(amountStr, displayDecimals.Value);
|
||||
}
|
||||
|
||||
if (useCommas)
|
||||
{
|
||||
amountStr = NumberWithCommas(amountStr);
|
||||
}
|
||||
|
||||
return decimal.Parse(amountStr, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private static string LimitDecimals(string amountStr, int displayDecimals)
|
||||
{
|
||||
var parts = amountStr.Split('.');
|
||||
if (parts.Length == 2 && parts[1].Length > displayDecimals)
|
||||
{
|
||||
parts[1] = parts[1].Substring(0, displayDecimals);
|
||||
}
|
||||
|
||||
return string.Join(".", parts);
|
||||
}
|
||||
|
||||
private static string PadDecimals(string amountStr, int displayDecimals)
|
||||
{
|
||||
var parts = amountStr.Split('.');
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
return amountStr + "." + new string('0', displayDecimals);
|
||||
}
|
||||
else if (parts.Length == 2)
|
||||
{
|
||||
return parts[0] + "." + parts[1].PadRight(displayDecimals, '0');
|
||||
}
|
||||
|
||||
return amountStr;
|
||||
}
|
||||
|
||||
private static string NumberWithCommas(string amountStr)
|
||||
{
|
||||
var parts = amountStr.Split('.');
|
||||
parts[0] = int.Parse(parts[0]).ToString("N0", CultureInfo.InvariantCulture);
|
||||
return string.Join(".", parts);
|
||||
}
|
||||
}
|
||||
112
src/Managing.Infrastructure.Web3/Services/Gmx/GmxKeysService.cs
Normal file
112
src/Managing.Infrastructure.Web3/Services/Gmx/GmxKeysService.cs
Normal file
File diff suppressed because one or more lines are too long
@@ -1,10 +1,12 @@
|
||||
using Managing.Core;
|
||||
using System.Numerics;
|
||||
using Managing.ABI.GmxV2.Reader.ContractDefinition;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Infrastructure.Evm.Models.Gmx;
|
||||
using Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
using Managing.Infrastructure.Evm.Referentials;
|
||||
using Nethereum.Web3;
|
||||
using System.Numerics;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Services.Gmx;
|
||||
@@ -174,4 +176,73 @@ public static class GmxMappers
|
||||
Timeframe = timeframe
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal static GmxMarket Map(MarketInfo infos)
|
||||
{
|
||||
var indexToken =
|
||||
TokenV2Service.GetTokenByAddress(TokenV2Service.ConvertTokenAddress(infos.Market.IndexToken, "native"));
|
||||
var longToken = TokenV2Service.GetTokenByAddress(infos.Market.LongToken);
|
||||
var shortToken = TokenV2Service.GetTokenByAddress(infos.Market.ShortToken);
|
||||
var isSameCollaterals = infos.Market.LongToken == infos.Market.ShortToken;
|
||||
var isSpotOnly = infos.Market.IndexToken == Nethereum.Util.AddressUtil.ZERO_ADDRESS;
|
||||
var name = TokenV2Service.GetMarketFullName(indexToken.Address, longToken.Address, shortToken.Address,
|
||||
isSpotOnly);
|
||||
var symbol = indexToken.Symbol;
|
||||
|
||||
return new GmxMarket
|
||||
{
|
||||
MarketToken = infos.Market.MarketToken,
|
||||
IndexToken = infos.Market.IndexToken,
|
||||
LongToken = infos.Market.LongToken,
|
||||
ShortToken = infos.Market.ShortToken,
|
||||
Name = name,
|
||||
Symbol = symbol,
|
||||
IsSpotOnly = isSpotOnly,
|
||||
IsSameCollaterals = isSameCollaterals,
|
||||
FundingFactorPerSecond = infos.NextFunding.FundingFactorPerSecond,
|
||||
LongsPayShorts = infos.NextFunding.LongsPayShorts
|
||||
};
|
||||
}
|
||||
|
||||
public static GmxMarket MapInfos(MarketsProps infos)
|
||||
{
|
||||
var indexToken =
|
||||
TokenV2Service.GetTokenByAddress(TokenV2Service.ConvertTokenAddress(infos.IndexToken, "native"));
|
||||
var longToken = TokenV2Service.GetTokenByAddress(infos.LongToken);
|
||||
var shortToken = TokenV2Service.GetTokenByAddress(infos.ShortToken);
|
||||
var isSameCollaterals = infos.LongToken == infos.ShortToken;
|
||||
var isSpotOnly = infos.IndexToken == Nethereum.Util.AddressUtil.ZERO_ADDRESS;
|
||||
var name = TokenV2Service.GetMarketFullName(indexToken.Address, longToken.Address, shortToken.Address,
|
||||
isSpotOnly);
|
||||
|
||||
return new GmxMarket
|
||||
{
|
||||
MarketToken = infos.MarketToken,
|
||||
IndexToken = infos.IndexToken,
|
||||
LongToken = infos.LongToken,
|
||||
ShortToken = infos.ShortToken,
|
||||
Name = name,
|
||||
Symbol = indexToken.Symbol,
|
||||
IsSpotOnly = isSpotOnly,
|
||||
IsSameCollaterals = isSameCollaterals,
|
||||
};
|
||||
}
|
||||
|
||||
public static GmxMarketTokenPrice Map(GetMarketTokenPriceOutputDTO response)
|
||||
{
|
||||
return new GmxMarketTokenPrice
|
||||
{
|
||||
LongTokenAmount = response.ReturnValue2.LongTokenAmount,
|
||||
ShortTokenAmount = response.ReturnValue2.ShortTokenAmount,
|
||||
LongTokenUsd = response.ReturnValue2.LongTokenUsd,
|
||||
ShortTokenUsd = response.ReturnValue2.ShortTokenUsd,
|
||||
PoolValue = response.ReturnValue2.PoolValue,
|
||||
LongPnl = response.ReturnValue2.LongPnl,
|
||||
ShortPnl = response.ReturnValue2.ShortPnl,
|
||||
NetPnl = response.ReturnValue2.NetPnl,
|
||||
TotalBorrowingFees = response.ReturnValue2.TotalBorrowingFees,
|
||||
BorrowingFeePoolFactor = response.ReturnValue2.BorrowingFeePoolFactor,
|
||||
ImpactPoolAmount = response.ReturnValue2.ImpactPoolAmount
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
using Managing.Domain.Trades;
|
||||
using System.Numerics;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Infrastructure.Evm.Models.Gmx;
|
||||
using Managing.Infrastructure.Evm.Referentials;
|
||||
using Managing.Tools.ABI.Reader;
|
||||
using Managing.Tools.ABI.Reader.ContractDefinition;
|
||||
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;
|
||||
@@ -98,7 +98,8 @@ public static class GmxService
|
||||
return true;
|
||||
}
|
||||
|
||||
public async static Task<Trade> IncreasePosition(Web3 web3, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
|
||||
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);
|
||||
@@ -114,9 +115,9 @@ public static class GmxService
|
||||
// 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.CollateralToken = Arbitrum.Address.USDC; // USDC
|
||||
function.IsLong = isLong;
|
||||
function.TriggerPrice = Web3.Convert.ToWei(price, 30); // Price of the order execution
|
||||
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;
|
||||
@@ -149,18 +150,19 @@ public static class GmxService
|
||||
return trade;
|
||||
}
|
||||
|
||||
public async static Task<Trade> DecreasePosition(Web3 web3, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
|
||||
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,
|
||||
"",
|
||||
"");
|
||||
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;
|
||||
@@ -212,18 +214,19 @@ public static class GmxService
|
||||
return trade;
|
||||
}
|
||||
|
||||
public static async Task<Trade> DecreaseOrder(Web3 web3, TradeType tradeType, string publicAddress, Ticker ticker, TradeDirection direction, decimal price, decimal quantity, decimal? leverage)
|
||||
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,
|
||||
"",
|
||||
"");
|
||||
direction,
|
||||
TradeStatus.Cancelled,
|
||||
tradeType,
|
||||
ticker,
|
||||
quantity,
|
||||
price,
|
||||
leverage,
|
||||
"",
|
||||
"");
|
||||
|
||||
// Check if there is quantity in position
|
||||
var currentPosition = await GetGmxPosition(web3, publicAddress, ticker);
|
||||
@@ -318,12 +321,14 @@ public static class GmxService
|
||||
orders.AddRange(increaseOrders);
|
||||
orders.AddRange(decreaseOrders);
|
||||
var contractAddress = TokenService.GetContractAddress(ticker);
|
||||
var ordersFiltered = orders.Where(o => string.Equals(o.IndexToken, contractAddress, StringComparison.CurrentCultureIgnoreCase));
|
||||
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)
|
||||
private static async Task<List<GmxOrder>> GetIncreaseOrders(OrderBookReaderService orderBookReader,
|
||||
string publicAddress, int lastIndex)
|
||||
{
|
||||
var increaseIndex = GmxHelpers.GetIndexesRange(lastIndex);
|
||||
var increaseOrdersFunction = new GetIncreaseOrdersFunction
|
||||
@@ -338,7 +343,8 @@ public static class GmxService
|
||||
return GmxMappers.MapIncrease(increaseOrders.ReturnValue1, increaseOrders.ReturnValue2, increaseIndex);
|
||||
}
|
||||
|
||||
private static async Task<List<GmxOrder>> GetDecreaseOrders(OrderBookReaderService orderBookReader, string publicAddress, int lastIndex)
|
||||
private static async Task<List<GmxOrder>> GetDecreaseOrders(OrderBookReaderService orderBookReader,
|
||||
string publicAddress, int lastIndex)
|
||||
{
|
||||
var increaseIndex = GmxHelpers.GetIndexesRange(lastIndex);
|
||||
var increaseOrdersFunction = new GetDecreaseOrdersFunction
|
||||
@@ -452,7 +458,7 @@ public static class GmxService
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Numerics;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Nethereum.ABI;
|
||||
using Nethereum.Util;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public class GmxV2Helpers
|
||||
{
|
||||
private readonly MemoryCache _dataCache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 10000 });
|
||||
private readonly MemoryCache _stringCache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 10000 });
|
||||
private readonly ABIEncode _abiEncode = new ABIEncode();
|
||||
private readonly MemoryCacheEntryOptions _absoluteExpiration = new MemoryCacheEntryOptions { Size = 1 };
|
||||
|
||||
|
||||
public byte[] HashData(string[] dataTypes, object[] dataValues)
|
||||
{
|
||||
var key = GenerateKey(dataTypes, dataValues);
|
||||
|
||||
if (_dataCache.TryGetValue(key, out byte[] cachedHash))
|
||||
{
|
||||
return cachedHash;
|
||||
}
|
||||
|
||||
var abiValues = new List<ABIValue>();
|
||||
foreach (var dataType in dataTypes)
|
||||
{
|
||||
var abiValue = new ABIValue(dataType, dataValues[dataTypes.ToList().IndexOf(dataType)]);
|
||||
abiValues.Add(abiValue);
|
||||
}
|
||||
|
||||
var abiEncode = new ABIEncode();
|
||||
var encoded = abiEncode.GetABIEncoded(abiValues.ToArray());
|
||||
var hash = new Sha3Keccack().CalculateHash(encoded);
|
||||
_dataCache.Set(key, hash, _absoluteExpiration);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
public byte[] HashString(string input)
|
||||
{
|
||||
if (_stringCache.TryGetValue(input, out byte[] cachedHash))
|
||||
{
|
||||
return cachedHash;
|
||||
}
|
||||
|
||||
// Adjusted to directly work with byte arrays
|
||||
var hash = HashData(new[] { "string" }, new string[] { input });
|
||||
_stringCache.Set(input, hash, _absoluteExpiration);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
private string GenerateKey(string[] dataTypes, object[] dataValues)
|
||||
{
|
||||
var combined = new List<string>();
|
||||
for (int i = 0; i < dataTypes.Length; i++)
|
||||
{
|
||||
combined.Add($"{dataTypes[i]}:{(dataValues[i] is BigInteger bigInt ? bigInt.ToString() : dataValues[i])}");
|
||||
}
|
||||
|
||||
return string.Join(",", combined);
|
||||
}
|
||||
|
||||
public static bool SameAddress(string address, string address2)
|
||||
{
|
||||
return address.ToLower() == address2.ToLower();
|
||||
}
|
||||
}
|
||||
531
src/Managing.Infrastructure.Web3/Services/Gmx/GmxV2Service.cs
Normal file
531
src/Managing.Infrastructure.Web3/Services/Gmx/GmxV2Service.cs
Normal file
@@ -0,0 +1,531 @@
|
||||
using System.Numerics;
|
||||
using Managing.ABI.GmxV2.DataStore;
|
||||
using Managing.ABI.GmxV2.DataStore.ContractDefinition;
|
||||
using Managing.ABI.GmxV2.Multicall3.ContractDefinition;
|
||||
using Managing.ABI.GmxV2.Reader;
|
||||
using Managing.ABI.GmxV2.Reader.ContractDefinition;
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Statistics;
|
||||
using Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
using Managing.Infrastructure.Evm.Referentials;
|
||||
using Managing.Infrastructure.Evm.Services.Gmx;
|
||||
using Nethereum.Contracts;
|
||||
using Nethereum.Contracts.Standards.ERC20.ContractDefinition;
|
||||
using Nethereum.Hex.HexConvertors.Extensions;
|
||||
using Nethereum.Web3;
|
||||
|
||||
public class GmxV2Service
|
||||
{
|
||||
private readonly GmxV2Helpers _helpers = new GmxV2Helpers();
|
||||
|
||||
public GmxV2Service()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<object> GetMarkets(Web3 web3)
|
||||
{
|
||||
var reader = new ReaderService(web3, Arbitrum.AddressV2.Reader);
|
||||
var result = await reader.GetMarketsQueryAsync(Arbitrum.AddressV2.DataStore, 0, 100);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<List<GmxMarket>> GetMarketsAsync(Web3 web3)
|
||||
{
|
||||
var getMarketCallData = new GetMarketsFunction
|
||||
{
|
||||
DataStore = Arbitrum.AddressV2.DataStore,
|
||||
Start = 0,
|
||||
End = 100
|
||||
}.GetCallData();
|
||||
|
||||
var call1 = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.Reader,
|
||||
CallData = getMarketCallData
|
||||
};
|
||||
|
||||
var aggregateFunction = new AggregateFunction();
|
||||
aggregateFunction.Calls = new List<Call>();
|
||||
aggregateFunction.Calls.Add(call1);
|
||||
|
||||
var queryHandler = web3.Eth.GetContractQueryHandler<AggregateFunction>();
|
||||
var returnCalls = await queryHandler
|
||||
.QueryDeserializingToObjectAsync<AggregateOutputDTO>(aggregateFunction, Arbitrum.AddressV2.Multicall)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var markets = new GetMarketsOutputDTO().DecodeOutput(returnCalls.ReturnData[0].ToHex());
|
||||
var result = new List<GmxMarket>();
|
||||
foreach (var market in markets.ReturnValue1)
|
||||
{
|
||||
try
|
||||
{
|
||||
result.Add(GmxMappers.MapInfos(market));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GmxMarketInfo> GetMarketInfo(Web3 web3)
|
||||
{
|
||||
var reader = new ReaderService(web3, Arbitrum.AddressV2.Reader);
|
||||
var market = (await GetMarketsAsync(web3)).First();
|
||||
var tokensData = await GetTokensData(web3);
|
||||
var marketPrices = GmxHelpers.GetContractMarketPrices(tokensData, market);
|
||||
var response =
|
||||
await reader.GetMarketInfoQueryAsync(Arbitrum.AddressV2.DataStore, marketPrices, market.MarketToken);
|
||||
|
||||
// Get hashed key to call datastore
|
||||
var marketKeys = GmxKeysService.GetMarketKeys(market.MarketToken);
|
||||
var longInterestUsingLongTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.LongInterestUsingLongToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var longInterestUsingLongTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = longInterestUsingLongTokenCallData
|
||||
};
|
||||
var longInterestUsingShortTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.LongInterestUsingShortToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var longInterestUsingShortTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = longInterestUsingShortTokenCallData
|
||||
};
|
||||
var shortInterestUsingLongTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.ShortInterestUsingLongToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var shortInterestUsingLongTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = shortInterestUsingLongTokenCallData
|
||||
};
|
||||
var shortInterestUsingShortTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.ShortInterestUsingShortToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var shortInterestUsingShortTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = shortInterestUsingShortTokenCallData
|
||||
};
|
||||
|
||||
var dataStoreMulticall = new AggregateFunction();
|
||||
dataStoreMulticall.Calls = new List<Call>();
|
||||
dataStoreMulticall.Calls.Add(longInterestUsingLongTokenCall);
|
||||
dataStoreMulticall.Calls.Add(longInterestUsingShortTokenCall);
|
||||
dataStoreMulticall.Calls.Add(shortInterestUsingLongTokenCall);
|
||||
dataStoreMulticall.Calls.Add(shortInterestUsingShortTokenCall);
|
||||
var queryHandler = web3.Eth.GetContractQueryHandler<AggregateFunction>();
|
||||
var returnCalls = await queryHandler
|
||||
.QueryDeserializingToObjectAsync<AggregateOutputDTO>(dataStoreMulticall, Arbitrum.AddressV2.Multicall)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var marketInfos = GmxMappers.Map(response.ReturnValue1);
|
||||
var result = new GmxMarketInfo()
|
||||
{
|
||||
Market = marketInfos,
|
||||
Infos = DecodeFundingRateOutput(returnCalls, marketInfos.IsSameCollaterals)
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GmxMarketTokenPrice> GetMarketTokenPrice(Web3 web3, bool maximize = true)
|
||||
{
|
||||
var reader = new ReaderService(web3, Arbitrum.AddressV2.Reader);
|
||||
var market = (await GetMarketsAsync(web3)).First();
|
||||
var tokensData = await GetTokensData(web3);
|
||||
var marketPrices = GmxHelpers.GetContractMarketPrices(tokensData, market);
|
||||
var marketProps = new MarketsProps()
|
||||
{
|
||||
MarketToken = market.MarketToken,
|
||||
IndexToken = market.IndexToken,
|
||||
LongToken = market.LongToken,
|
||||
ShortToken = market.ShortToken
|
||||
};
|
||||
|
||||
var response = await reader.GetMarketTokenPriceQueryAsync(Arbitrum.AddressV2.DataStore, marketProps,
|
||||
marketPrices.IndexTokenPrice, marketPrices.LongTokenPrice, marketPrices.ShortTokenPrice,
|
||||
_helpers.HashString(Constants.GMX.MAX_PNL_FACTOR_FOR_TRADERS), maximize);
|
||||
|
||||
return GmxMappers.Map(response);
|
||||
}
|
||||
|
||||
public async Task<bool> GetIsMarketDisabled(Web3 web3)
|
||||
{
|
||||
var dataStoreService = new DataStoreService(web3, Arbitrum.AddressV2.DataStore);
|
||||
var market = (await GetMarketsAsync(web3)).First();
|
||||
var hashString = _helpers.HashString(Constants.GMX.MARKET_DISABLED_KEY);
|
||||
var response = await dataStoreService.GetBoolQueryAsync(_helpers.HashData(
|
||||
new[] { "bytes32", "address" },
|
||||
new object[] { hashString, market.MarketToken }));
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<BigInteger> GetLongInterestAmount(Web3 web3)
|
||||
{
|
||||
var dataStoreService = new DataStoreService(web3, Arbitrum.AddressV2.DataStore);
|
||||
var market = (await GetMarketsAsync(web3)).First();
|
||||
var keys = GmxKeysService.GetMarketKeys(market.MarketToken);
|
||||
var response = await dataStoreService.GetUintQueryAsync(keys.LongInterestUsingLongToken.HexToByteArray());
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<List<GmxMarketInfo>> GetMarketInfosAsync(Web3 web3)
|
||||
{
|
||||
var markets = await GetMarketsAsync(web3);
|
||||
var readerResult = new List<GmxMarketInfo>();
|
||||
foreach (var market in markets)
|
||||
{
|
||||
var tokensData = await GetTokensData(web3);
|
||||
var marketPrices = GmxHelpers.GetContractMarketPrices(tokensData, market);
|
||||
var marketProps = new MarketsProps()
|
||||
{
|
||||
MarketToken = market.MarketToken,
|
||||
IndexToken = market.IndexToken,
|
||||
LongToken = market.LongToken,
|
||||
ShortToken = market.ShortToken
|
||||
};
|
||||
var getMarketInfoCallData = new GetMarketInfoFunction
|
||||
{
|
||||
DataStore = Arbitrum.AddressV2.DataStore,
|
||||
Prices = marketPrices,
|
||||
MarketKey = market.MarketToken
|
||||
}.GetCallData();
|
||||
var getMarketInfoCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.Reader,
|
||||
CallData = getMarketInfoCallData
|
||||
};
|
||||
var getMarketTokenPriceCallData = new GetMarketTokenPriceFunction
|
||||
{
|
||||
DataStore = Arbitrum.AddressV2.DataStore,
|
||||
Market = marketProps,
|
||||
IndexTokenPrice = marketPrices.IndexTokenPrice,
|
||||
LongTokenPrice = marketPrices.LongTokenPrice,
|
||||
ShortTokenPrice = marketPrices.ShortTokenPrice,
|
||||
PnlFactorType = _helpers.HashString(Constants.GMX.MAX_PNL_FACTOR_FOR_TRADERS),
|
||||
Maximize = true
|
||||
}.GetCallData();
|
||||
var getMarketTokenPriceCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.Reader,
|
||||
CallData = getMarketTokenPriceCallData
|
||||
};
|
||||
var getMarketTokenPriceMinCallData = new GetMarketTokenPriceFunction
|
||||
{
|
||||
DataStore = Arbitrum.AddressV2.DataStore,
|
||||
Market = marketProps,
|
||||
IndexTokenPrice = marketPrices.IndexTokenPrice,
|
||||
LongTokenPrice = marketPrices.LongTokenPrice,
|
||||
ShortTokenPrice = marketPrices.ShortTokenPrice,
|
||||
PnlFactorType = _helpers.HashString(Constants.GMX.MAX_PNL_FACTOR_FOR_TRADERS),
|
||||
Maximize = false
|
||||
}.GetCallData();
|
||||
var getMarketTokenPriceMinCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.Reader,
|
||||
CallData = getMarketTokenPriceMinCallData
|
||||
};
|
||||
|
||||
var readerMulticall = new AggregateFunction();
|
||||
readerMulticall.Calls = new List<Call>();
|
||||
readerMulticall.Calls.Add(getMarketInfoCall);
|
||||
readerMulticall.Calls.Add(getMarketTokenPriceCall);
|
||||
readerMulticall.Calls.Add(getMarketTokenPriceMinCall);
|
||||
|
||||
var queryHandler = web3.Eth.GetContractQueryHandler<AggregateFunction>();
|
||||
var readerCallResults = await queryHandler
|
||||
.QueryDeserializingToObjectAsync<AggregateOutputDTO>(readerMulticall, Arbitrum.AddressV2.Multicall)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var marketInfo = new GetMarketInfoOutputDTO().DecodeOutput(readerCallResults.ReturnData[0].ToHex());
|
||||
var marketTokenPriceMax =
|
||||
new GetMarketTokenPriceOutputDTO().DecodeOutput(readerCallResults.ReturnData[1].ToHex());
|
||||
var marketTokenPriceMin =
|
||||
new GetMarketTokenPriceOutputDTO().DecodeOutput(readerCallResults.ReturnData[2].ToHex());
|
||||
|
||||
// Get hashed key to call datastore
|
||||
var marketKeys = GmxKeysService.GetMarketKeys(market.MarketToken);
|
||||
if (marketKeys == null)
|
||||
continue;
|
||||
|
||||
var longInterestUsingLongTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.LongInterestUsingLongToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var longInterestUsingLongTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = longInterestUsingLongTokenCallData
|
||||
};
|
||||
var longInterestUsingShortTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.LongInterestUsingShortToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var longInterestUsingShortTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = longInterestUsingShortTokenCallData
|
||||
};
|
||||
var shortInterestUsingLongTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.ShortInterestUsingLongToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var shortInterestUsingLongTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = shortInterestUsingLongTokenCallData
|
||||
};
|
||||
var shortInterestUsingShortTokenCallData = new GetUintFunction()
|
||||
{
|
||||
Key = marketKeys.ShortInterestUsingShortToken.HexToByteArray()
|
||||
}.GetCallData();
|
||||
var shortInterestUsingShortTokenCall = new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.DataStore,
|
||||
CallData = shortInterestUsingShortTokenCallData
|
||||
};
|
||||
|
||||
var dataStoreMulticall = new AggregateFunction();
|
||||
dataStoreMulticall.Calls = new List<Call>();
|
||||
dataStoreMulticall.Calls.Add(longInterestUsingLongTokenCall);
|
||||
dataStoreMulticall.Calls.Add(longInterestUsingShortTokenCall);
|
||||
dataStoreMulticall.Calls.Add(shortInterestUsingLongTokenCall);
|
||||
dataStoreMulticall.Calls.Add(shortInterestUsingShortTokenCall);
|
||||
|
||||
// Call datastore to get market info
|
||||
var dataStoreQueryHandler = web3.Eth.GetContractQueryHandler<AggregateFunction>();
|
||||
var dataStoreCallResults = await dataStoreQueryHandler
|
||||
.QueryDeserializingToObjectAsync<AggregateOutputDTO>(dataStoreMulticall, Arbitrum.AddressV2.Multicall)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var marketInfos = GmxMappers.Map(marketInfo.ReturnValue1);
|
||||
readerResult.Add(new GmxMarketInfo()
|
||||
{
|
||||
Market = marketInfos,
|
||||
Infos = DecodeFundingRateOutput(dataStoreCallResults, marketInfos.IsSameCollaterals),
|
||||
MarketTokenPriceMax = GmxMappers.Map(marketTokenPriceMax),
|
||||
MarketTokenPriceMin = GmxMappers.Map(marketTokenPriceMin)
|
||||
});
|
||||
}
|
||||
|
||||
return readerResult;
|
||||
}
|
||||
|
||||
private GmxMarketInfos DecodeFundingRateOutput(AggregateOutputDTO results, bool isSameCollaterals)
|
||||
{
|
||||
var marketDivisor = new BigInteger(isSameCollaterals ? 2 : 1);
|
||||
var longInterestUsingLongToken =
|
||||
new GetUintOutputDTO().DecodeOutput(results.ReturnData[0].ToHex()).ReturnValue1 / marketDivisor;
|
||||
var longInterestUsingShortToken =
|
||||
new GetUintOutputDTO().DecodeOutput(results.ReturnData[1].ToHex()).ReturnValue1 / marketDivisor;
|
||||
var shortInterestUsingLongToken =
|
||||
new GetUintOutputDTO().DecodeOutput(results.ReturnData[2].ToHex()).ReturnValue1 / marketDivisor;
|
||||
var shortInterestUsingShortToken =
|
||||
new GetUintOutputDTO().DecodeOutput(results.ReturnData[3].ToHex()).ReturnValue1 / marketDivisor;
|
||||
|
||||
var longInterestUsd = longInterestUsingLongToken + longInterestUsingShortToken;
|
||||
var shortInterestUsd = shortInterestUsingLongToken + shortInterestUsingShortToken;
|
||||
|
||||
var marketInfos = new GmxMarketInfos()
|
||||
{
|
||||
LongInterestUsd = longInterestUsd,
|
||||
ShortInterestUsd = shortInterestUsd
|
||||
};
|
||||
return marketInfos;
|
||||
}
|
||||
|
||||
public async Task<List<GmxTokenData>> GetTokensData(Web3 web3)
|
||||
{
|
||||
var tokens = TokenV2Service.GetTokens().ToList();
|
||||
var balances = await GetTokenBalances(web3, tokens);
|
||||
var prices = GetTokenPrices(web3, tokens);
|
||||
var result = new List<GmxTokenData>();
|
||||
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
BigInteger? balance = null;
|
||||
if (balances.Balances != null && balances.Balances.ContainsKey(token.Address))
|
||||
{
|
||||
balance = balances.Balances[token.Address];
|
||||
}
|
||||
|
||||
var price = prices[token.Address].Price;
|
||||
|
||||
var data = new GmxTokenData()
|
||||
{
|
||||
Address = token.Address,
|
||||
Name = token.Name,
|
||||
Symbol = token.Symbol,
|
||||
Decimals = token.Decimals,
|
||||
Price = price,
|
||||
Balance = balance
|
||||
};
|
||||
result.Add(data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Dictionary<string, GmxTokenPriceData> GetTokenPrices(Web3 web3, List<GmxToken> tokens)
|
||||
{
|
||||
var result = new Dictionary<string, GmxTokenPriceData>();
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
// TODO fetch token price from oracle
|
||||
result.Add(token.Address, new GmxTokenPriceData()
|
||||
{
|
||||
Price = new MarketPrice()
|
||||
{
|
||||
Min = 0,
|
||||
Max = 0
|
||||
},
|
||||
Balance = 0,
|
||||
TotalSupply = 0
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<GmxTokenBalances> GetTokenBalances(Web3 web3, List<GmxToken> tokens, string? account = null)
|
||||
{
|
||||
var result = new GmxTokenBalances();
|
||||
result.Balances = new Dictionary<string, BigInteger>();
|
||||
var aggregateFunction = new AggregateFunction();
|
||||
aggregateFunction.Calls = new List<Call>();
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
if (token.IsNative)
|
||||
{
|
||||
var nativeBalanceCallData = new GetEthBalanceFunction()
|
||||
{
|
||||
Addr = account ?? Nethereum.Util.AddressUtil.ZERO_ADDRESS
|
||||
}.GetCallData();
|
||||
aggregateFunction.Calls.Add(new Call
|
||||
{
|
||||
Target = Arbitrum.AddressV2.Multicall,
|
||||
CallData = nativeBalanceCallData
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var balanceCallData = new BalanceOfFunction()
|
||||
{
|
||||
Owner = account ?? GmxHelpers.GetRandomAddress()
|
||||
}.GetCallData();
|
||||
aggregateFunction.Calls.Add(new Call
|
||||
{
|
||||
Target = token.Address,
|
||||
CallData = balanceCallData
|
||||
});
|
||||
}
|
||||
|
||||
var queryHandler = web3.Eth.GetContractQueryHandler<AggregateFunction>();
|
||||
var returnCalls = await queryHandler
|
||||
.QueryDeserializingToObjectAsync<AggregateOutputDTO>(aggregateFunction, Arbitrum.AddressV2.Multicall)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var balances = new BalanceOfOutputDTOBase().DecodeOutput(returnCalls.ReturnData[0].ToHex());
|
||||
result.Balances.Add(token.Address, balances.Balance);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<List<FundingRate>> GetFundingRate(Web3 web3)
|
||||
{
|
||||
var market = await GetMarketInfo(web3);
|
||||
return Map(market);
|
||||
}
|
||||
|
||||
private List<FundingRate> Map(GmxMarketInfo market)
|
||||
{
|
||||
var longApr = GetFundingFactorPerPeriod(market, true, Periods[Enums.Timeframe.OneHour]) * 100;
|
||||
var shortApr = GetFundingFactorPerPeriod(market, false, Periods[Enums.Timeframe.OneHour]) * 100;
|
||||
var longFundingRate = new FundingRate()
|
||||
{
|
||||
Ticker = GmxHelpers.GetTicker(market.Market.Symbol),
|
||||
Rate = GmxHelpers.FormatAmount(longApr, 30, 4),
|
||||
Direction = Enums.TradeDirection.Long
|
||||
};
|
||||
var shortFundingRate = new FundingRate()
|
||||
{
|
||||
Ticker = GmxHelpers.GetTicker(market.Market.Symbol),
|
||||
Rate = GmxHelpers.FormatAmount(shortApr, 30, 4),
|
||||
Direction = Enums.TradeDirection.Short
|
||||
};
|
||||
return new List<FundingRate>() { longFundingRate, shortFundingRate };
|
||||
}
|
||||
|
||||
public async Task<List<FundingRate>> GetFundingRates(Web3 web3)
|
||||
{
|
||||
var fundingRates = new List<FundingRate>();
|
||||
var marketDatas = await GetMarketInfosAsync(web3);
|
||||
foreach (var gmxMarketInfo in marketDatas)
|
||||
{
|
||||
var rates = Map(gmxMarketInfo);
|
||||
fundingRates.AddRange(rates);
|
||||
}
|
||||
|
||||
return fundingRates;
|
||||
}
|
||||
|
||||
private 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 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);
|
||||
|
||||
private Dictionary<Enums.Timeframe, int> Periods = new Dictionary<Enums.Timeframe, int>()
|
||||
{
|
||||
{ Enums.Timeframe.FiveMinutes, 300 },
|
||||
{ Enums.Timeframe.FifteenMinutes, 900 },
|
||||
{ Enums.Timeframe.OneHour, 3600 },
|
||||
{ Enums.Timeframe.FourHour, 14400 },
|
||||
{ Enums.Timeframe.OneDay, 86400 }
|
||||
};
|
||||
}
|
||||
447
src/Managing.Infrastructure.Web3/Services/Gmx/TokenV2Service.cs
Normal file
447
src/Managing.Infrastructure.Web3/Services/Gmx/TokenV2Service.cs
Normal file
@@ -0,0 +1,447 @@
|
||||
using Managing.Common;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Gmx.v2;
|
||||
|
||||
public static class TokenV2Service
|
||||
{
|
||||
public static List<GmxToken> TOKENS { get; set; } = new List<GmxToken>
|
||||
{
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Ethereum",
|
||||
Symbol = "ETH",
|
||||
Decimals = 18,
|
||||
Address = Nethereum.Util.AddressUtil.ZERO_ADDRESS,
|
||||
IsNative = true,
|
||||
IsShortable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/ethereum",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Wrapped Ethereum",
|
||||
Symbol = "WETH",
|
||||
Decimals = 18,
|
||||
Address = Constants.GMX.TokenAddress.WETH,
|
||||
IsWrapped = true,
|
||||
BaseSymbol = "ETH",
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/2518/thumb/weth.png?1628852295",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/ethereum",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Wrapped Stacked Ethereum",
|
||||
Symbol = "WSTETH",
|
||||
Decimals = 18,
|
||||
Address = Constants.GMX.TokenAddress.WSTETH,
|
||||
IsWrapped = true,
|
||||
BaseSymbol = "wstETH",
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/2518/thumb/wsteth.png?1628852295",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/ethereum",
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Bitcoin (WBTC)",
|
||||
Symbol = "BTC",
|
||||
AssetSymbol = "WBTC",
|
||||
Decimals = 8,
|
||||
Address = Constants.GMX.TokenAddress.WBTC,
|
||||
IsShortable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/26115/thumb/btcb.png?1655921693",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/wrapped-bitcoin",
|
||||
ExplorerUrl = "https://arbiscan.io/address/0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Arbitrum",
|
||||
Symbol = "ARB",
|
||||
Decimals = 18,
|
||||
PriceDecimals = 3,
|
||||
Address = Constants.GMX.TokenAddress.ARB,
|
||||
ImageUrl =
|
||||
"https://assets.coingecko.com/coins/images/16547/small/photo_2023-03-29_21.47.00.jpeg?1680097630",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/arbitrum",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0x912ce59144191c1204e64559fe8253a0e49e6548"
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Wrapped SOL (Wormhole)",
|
||||
Symbol = "SOL",
|
||||
AssetSymbol = "WSOL (Wormhole)",
|
||||
Decimals = 9,
|
||||
Address = Constants.GMX.TokenAddress.SOL,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/4128/small/solana.png?1640133422",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/solana",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0x2bCc6D6CdBbDC0a4071e48bb3B969b06B3330c07",
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Chainlink",
|
||||
Symbol = "LINK",
|
||||
Decimals = 18,
|
||||
PriceDecimals = 3,
|
||||
Address = Constants.GMX.TokenAddress.LINK,
|
||||
IsStable = false,
|
||||
IsShortable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/877/thumb/chainlink-new-logo.png?1547034700",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/chainlink",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0xf97f4df75117a78c1a5a0dbb814af92458539fb4",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Uniswap",
|
||||
Symbol = "UNI",
|
||||
Decimals = 18,
|
||||
PriceDecimals = 3,
|
||||
Address = Constants.GMX.TokenAddress.UNI,
|
||||
IsStable = false,
|
||||
IsShortable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/12504/thumb/uniswap-uni.png?1600306604",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/uniswap",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Bridged USDC (USDC.e)",
|
||||
Symbol = "USDC.e",
|
||||
Decimals = 6,
|
||||
Address = Constants.GMX.TokenAddress.USDCE,
|
||||
IsStable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/bridged-usdc-arbitrum",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "USD Coin",
|
||||
Symbol = "USDC",
|
||||
Decimals = 6,
|
||||
Address = Constants.GMX.TokenAddress.USDC,
|
||||
IsStable = true,
|
||||
IsV1Available = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/usd-coin",
|
||||
ExplorerUrl = "https://arbiscan.io/address/0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Tether",
|
||||
Symbol = "USDT",
|
||||
Decimals = 6,
|
||||
Address = Constants.GMX.TokenAddress.USDT,
|
||||
IsStable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/325/thumb/Tether-logo.png?1598003707",
|
||||
ExplorerUrl = "https://arbiscan.io/address/0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/tether",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Dai",
|
||||
Symbol = "DAI",
|
||||
Decimals = 18,
|
||||
Address = Constants.GMX.TokenAddress.DAI,
|
||||
IsStable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/dai",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "USDe",
|
||||
Symbol = "USDe",
|
||||
Decimals = 18,
|
||||
Address = Constants.GMX.TokenAddress.USDE,
|
||||
IsStable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/usde",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Frax",
|
||||
Symbol = "FRAX",
|
||||
Decimals = 18,
|
||||
Address = Constants.GMX.TokenAddress.FRAX,
|
||||
IsStable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/13422/small/frax_logo.png?1608476506",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/frax",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0x17fc002b466eec40dae837fc4be5c67993ddbd6f",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Magic Internet Money",
|
||||
Symbol = "MIM",
|
||||
Decimals = 18,
|
||||
Address = Constants.GMX.TokenAddress.MIM,
|
||||
IsStable = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/16786/thumb/mimlogopng.png?1624979612",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/magic-internet-money",
|
||||
ExplorerUrl = "https://arbiscan.io/token/0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A",
|
||||
IsV1Available = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Bitcoin",
|
||||
Symbol = "BTC",
|
||||
Decimals = 8,
|
||||
Address = Constants.GMX.TokenAddress.BTC,
|
||||
IsSynthetic = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/bitcoin",
|
||||
},
|
||||
new GmxToken()
|
||||
{
|
||||
Name = "DogeCoin",
|
||||
Symbol = "DOGE",
|
||||
Decimals = 8,
|
||||
PriceDecimals = 4,
|
||||
Address = Constants.GMX.TokenAddress.DOGE,
|
||||
IsSynthetic = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/5/small/dogecoin.png?1547792256",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/dogecoin",
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Litecoin",
|
||||
Symbol = "LTC",
|
||||
Decimals = 8,
|
||||
Address = Constants.GMX.TokenAddress.LTC,
|
||||
IsSynthetic = true,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/2/small/litecoin.png?1547033580",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/litecoin",
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "XRP",
|
||||
Symbol = "XRP",
|
||||
Decimals = 6,
|
||||
PriceDecimals = 4,
|
||||
Address = Constants.GMX.TokenAddress.XRP,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/44/small/xrp-symbol-white-128.png?1605778731",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/xrp",
|
||||
IsSynthetic = true,
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "GMX",
|
||||
Symbol = "GMX",
|
||||
Address =
|
||||
Constants.GMX.TokenAddress.GMX,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/18323/small/arbit.png?1631532468",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/gmx",
|
||||
ExplorerUrl = "https://arbiscan.io/address/0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a",
|
||||
IsPlatform = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Wrapped BNB (LayerZero)",
|
||||
Symbol = "BNB",
|
||||
AssetSymbol = "WBNB (LayerZero)",
|
||||
Address = Constants.GMX.TokenAddress.WBNB,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/825/standard/bnb-icon2_2x.png?1696501970",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/bnb",
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Cosmos",
|
||||
Symbol = "ATOM",
|
||||
AssetSymbol = "ATOM",
|
||||
Address = Constants.GMX.TokenAddress.ATOM,
|
||||
Decimals = 6,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/1481/standard/cosmos_hub.png?1696502525",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/cosmos-hub",
|
||||
IsSynthetic = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Near",
|
||||
Symbol = "NEAR",
|
||||
AssetSymbol = "NEAR",
|
||||
Address = Constants.GMX.TokenAddress.NEAR,
|
||||
Decimals = 24,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/10365/standard/near.jpg?1696510367",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/near",
|
||||
IsSynthetic = true
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Aave",
|
||||
Symbol = "AAVE",
|
||||
AssetSymbol = "AAVE",
|
||||
Address = Constants.GMX.TokenAddress.AAVE,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/12645/standard/AAVE.png?1696512452",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/aave"
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Wrapped AVAX (Wormhole)",
|
||||
Symbol = "AVAX",
|
||||
AssetSymbol = "WAVAX (Wormhole)",
|
||||
Address = Constants.GMX.TokenAddress.AVAX,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/12559/small/coin-round-red.png?1604021818",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/avalanche"
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Optimism",
|
||||
Symbol = "OP",
|
||||
Address = Constants.GMX.TokenAddress.OP,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/25244/standard/Optimism.png?1696524385",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/optimism"
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Pepe",
|
||||
Symbol = "PEPE",
|
||||
Address = Constants.GMX.TokenAddress.PEPE,
|
||||
Decimals = 18,
|
||||
PriceDecimals = 8,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/29850/standard/pepe-token.jpeg?1696528776",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/pepe"
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "dogwifhat",
|
||||
Symbol = "WIF",
|
||||
Address = Constants.GMX.TokenAddress.WIF,
|
||||
Decimals = 6,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/33566/standard/dogwifhat.jpg?1702499428",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/dogwifhat"
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Shiba Inu",
|
||||
Symbol = "SHIB",
|
||||
Address = Constants.GMX.TokenAddress.SHIB,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/11939/standard/shiba.png?1696511800",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/shiba-inu",
|
||||
IsSynthetic = true,
|
||||
PriceDecimals = 8
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "APE Coin",
|
||||
Symbol = "APE",
|
||||
Address = Constants.GMX.TokenAddress.APE,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/11939/standard/shiba.png?1696511800",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/shiba-inu",
|
||||
IsSynthetic = true,
|
||||
PriceDecimals = 8
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "Stacks",
|
||||
Symbol = "STX",
|
||||
Address = Constants.GMX.TokenAddress.STX,
|
||||
Decimals = 6,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/2069/standard/Stacks_Logo_png.png?1709979332",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/stacks",
|
||||
IsSynthetic = true,
|
||||
},
|
||||
new GmxToken
|
||||
{
|
||||
Name = "ORDI",
|
||||
Symbol = "ORDI",
|
||||
Address = Constants.GMX.TokenAddress.ORDI,
|
||||
Decimals = 18,
|
||||
ImageUrl = "https://assets.coingecko.com/coins/images/30162/standard/ordi.png?1696529082",
|
||||
CoingeckoUrl = "https://www.coingecko.com/en/coins/ordi",
|
||||
IsSynthetic = true,
|
||||
},
|
||||
};
|
||||
|
||||
public static GmxToken NATIVE_TOKEN = TOKENS.First(t => t.IsNative);
|
||||
public static GmxToken WRAPPRED_NATIVE_TOKEN = TOKENS.First(t => t.IsWrapped);
|
||||
|
||||
public static List<GmxToken> GetTokens()
|
||||
{
|
||||
return TOKENS;
|
||||
}
|
||||
|
||||
public static GmxToken GetTokenByAddress(string address)
|
||||
{
|
||||
var token = TOKENS.FirstOrDefault(t => GmxV2Helpers.SameAddress(t.Address, address));
|
||||
if (token == null)
|
||||
throw new Exception($"Token with address '{address}' not found");
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
public static string ConvertTokenAddress(string address, string convertTo = null)
|
||||
{
|
||||
if (convertTo == "wrapped" && address == NATIVE_TOKEN.Address)
|
||||
{
|
||||
return WRAPPRED_NATIVE_TOKEN.Address;
|
||||
}
|
||||
|
||||
if (convertTo == "native" && address == WRAPPRED_NATIVE_TOKEN.Address)
|
||||
{
|
||||
return NATIVE_TOKEN.Address;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
public static string GetMarketFullName(string indexToken, string longToken, string shortToken, bool isSpotOnly)
|
||||
{
|
||||
return $"{GetMarketIndexName(indexToken, isSpotOnly)} [{GetMarketPoolName(longToken, shortToken)}]";
|
||||
}
|
||||
|
||||
|
||||
public static string GetMarketIndexName(string indexToken, bool isSpotOnly)
|
||||
{
|
||||
if (isSpotOnly)
|
||||
{
|
||||
return "SPOT-ONLY";
|
||||
}
|
||||
|
||||
return indexToken;
|
||||
}
|
||||
|
||||
public static string GetMarketPoolName(string longToken, string shortToken)
|
||||
{
|
||||
if (longToken == shortToken)
|
||||
{
|
||||
return longToken;
|
||||
}
|
||||
|
||||
return $"{longToken}-{shortToken}";
|
||||
}
|
||||
}
|
||||
|
||||
public class GmxToken
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Symbol { get; set; }
|
||||
public int Decimals { get; set; }
|
||||
public string Address { get; set; }
|
||||
public bool IsNative { get; set; }
|
||||
public bool IsShortable { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public string CoingeckoUrl { get; set; }
|
||||
public bool IsV1Available { get; set; }
|
||||
public bool IsStable { get; set; }
|
||||
public string ExplorerUrl { get; set; }
|
||||
public string BaseSymbol { get; set; }
|
||||
public string AssetSymbol { get; set; }
|
||||
public bool IsWrapped { get; set; }
|
||||
public int PriceDecimals { get; set; }
|
||||
public bool IsSynthetic { get; set; }
|
||||
public bool IsPlatform { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user