Update SDK (#35)
* Update SDK for swap * Fix web3proxy build * Update types * Fix swap * Send token test and BASE transfer * fix cache and hook * Fix send * Update health check with uiFeereceiver * Fix sdk * Fix get positions * Fix timeoutloop * Fix open position * Fix closes positions * Review
This commit is contained in:
@@ -15,6 +15,11 @@ public interface ICandleStoreGrain : IGrainWithStringKey
|
||||
/// </summary>
|
||||
/// <returns>List of candles ordered by date</returns>
|
||||
Task<HashSet<Candle>> GetCandlesAsync();
|
||||
Task<Candle> GetLastCandle();
|
||||
/// <summary>
|
||||
/// Gets the X latest candles from the store
|
||||
/// </summary>
|
||||
/// <param name="count">Number of latest candles to retrieve (default: 1)</param>
|
||||
/// <returns>List of the X latest candles ordered by date</returns>
|
||||
Task<List<Candle>> GetLastCandle(int count = 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public interface IMessengerService
|
||||
Timeframe timeframe);
|
||||
|
||||
Task SendPosition(Position position);
|
||||
Task SendClosingPosition(Position position);
|
||||
void SendClosingPosition(Position position);
|
||||
Task SendMessage(string message);
|
||||
Task SendMessage(string message, string channelId);
|
||||
|
||||
|
||||
@@ -133,7 +133,8 @@ public class TradingBotBase : ITradingBot
|
||||
{
|
||||
// Add a small delay to ensure grain is fully activated
|
||||
await Task.Delay(100);
|
||||
LastCandle = await grain.GetLastCandle();
|
||||
var lastCandles = await grain.GetLastCandle(1);
|
||||
LastCandle = lastCandles.FirstOrDefault();
|
||||
}
|
||||
catch (InvalidOperationException ex) when (ex.Message.Contains("invalid activation"))
|
||||
{
|
||||
@@ -143,7 +144,8 @@ public class TradingBotBase : ITradingBot
|
||||
await Task.Delay(1000);
|
||||
try
|
||||
{
|
||||
LastCandle = await grain.GetLastCandle();
|
||||
var lastCandles = await grain.GetLastCandle(1);
|
||||
LastCandle = lastCandles.FirstOrDefault();
|
||||
}
|
||||
catch (Exception retryEx)
|
||||
{
|
||||
@@ -1059,13 +1061,30 @@ public class TradingBotBase : ITradingBot
|
||||
if (currentCandle != null)
|
||||
{
|
||||
List<Candle> recentCandles = null;
|
||||
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
|
||||
|
||||
if (Config.IsForBacktest)
|
||||
{
|
||||
recentCandles = Config.IsForBacktest
|
||||
? (LastCandle != null ? new List<Candle>() { LastCandle } : new List<Candle>())
|
||||
: (await exchangeService.GetCandlesInflux(TradingExchanges.Evm, Config.Ticker,
|
||||
DateTime.UtcNow.AddHours(-4), Config.Timeframe)).ToList();
|
||||
});
|
||||
recentCandles = LastCandle != null ? new List<Candle>() { LastCandle } : new List<Candle>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use CandleStoreGrain to get recent candles instead of calling exchange service directly
|
||||
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
|
||||
{
|
||||
var grainKey = CandleHelpers.GetCandleStoreGrainKey(Account.Exchange, Config.Ticker, Config.Timeframe);
|
||||
var grain = grainFactory.GetGrain<ICandleStoreGrain>(grainKey);
|
||||
|
||||
try
|
||||
{
|
||||
recentCandles = await grain.GetLastCandle(5);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error retrieving recent candles from CandleStoreGrain for {GrainKey}", grainKey);
|
||||
recentCandles = new List<Candle>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check if we have any candles before proceeding
|
||||
if (recentCandles == null || !recentCandles.Any())
|
||||
@@ -1216,7 +1235,8 @@ public class TradingBotBase : ITradingBot
|
||||
if (!Config.IsForBacktest)
|
||||
{
|
||||
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
|
||||
async messengerService => { await messengerService.SendClosingPosition(position); });
|
||||
messengerService => { messengerService.SendClosingPosition(position);
|
||||
return Task.CompletedTask; });
|
||||
}
|
||||
|
||||
await CancelAllOrders();
|
||||
|
||||
@@ -232,7 +232,7 @@ public class CandleStoreGrain : Grain, ICandleStoreGrain, IAsyncObserver<Candle>
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Candle> GetLastCandle()
|
||||
public Task<List<Candle>> GetLastCandle(int count = 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -240,15 +240,33 @@ public class CandleStoreGrain : Grain, ICandleStoreGrain, IAsyncObserver<Candle>
|
||||
if (_state.State.Candles == null || _state.State.Candles.Count == 0)
|
||||
{
|
||||
_logger.LogDebug("No candles available for grain {GrainKey}", this.GetPrimaryKeyString());
|
||||
return Task.FromResult<Candle>(null);
|
||||
return Task.FromResult(new List<Candle>());
|
||||
}
|
||||
|
||||
return Task.FromResult(_state.State.Candles.LastOrDefault());
|
||||
// Validate count parameter
|
||||
if (count <= 0)
|
||||
{
|
||||
_logger.LogWarning("Invalid count parameter {Count} for grain {GrainKey}, using default value 1",
|
||||
count, this.GetPrimaryKeyString());
|
||||
count = 1;
|
||||
}
|
||||
|
||||
// Get the last X candles, ordered by date
|
||||
var lastCandles = _state.State.Candles
|
||||
.OrderBy(c => c.Date)
|
||||
.TakeLast(count)
|
||||
.ToList();
|
||||
|
||||
_logger.LogDebug("Retrieved {Count} latest candles for grain {GrainKey}",
|
||||
lastCandles.Count, this.GetPrimaryKeyString());
|
||||
|
||||
return Task.FromResult(lastCandles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error retrieving last candle for grain {GrainKey}", this.GetPrimaryKeyString());
|
||||
return Task.FromResult<Candle>(null);
|
||||
_logger.LogError(ex, "Error retrieving last {Count} candles for grain {GrainKey}",
|
||||
count, this.GetPrimaryKeyString());
|
||||
return Task.FromResult(new List<Candle>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,21 @@ public class MessengerService : IMessengerService
|
||||
await _discordService.SendClosedPosition(address, oldTrade);
|
||||
}
|
||||
|
||||
public async Task SendClosingPosition(Position position)
|
||||
public void SendClosingPosition(Position position)
|
||||
{
|
||||
await _discordService.SendClosingPosition(position);
|
||||
// Fire-and-forget: Send closing position notification without blocking the thread
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await _discordService.SendClosingPosition(position);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log the exception but don't let it affect the main thread
|
||||
Console.WriteLine($"Failed to send closing position notification: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendIncreasePosition(string address, Trade trade, string copyAccountName, Trade? oldTrade = null)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Trades;
|
||||
using Managing.Domain.Users;
|
||||
using MediatR;
|
||||
using static Managing.Common.Enums;
|
||||
@@ -29,7 +30,7 @@ namespace Managing.Application.Trading.Commands
|
||||
Date = date;
|
||||
User = user;
|
||||
|
||||
if (amountToTrade <= 10)
|
||||
if (amountToTrade <= Constants.GMX.Config.MinimumPositionAmount)
|
||||
{
|
||||
throw new ArgumentException("Bot trading balance must be greater than zero", nameof(amountToTrade));
|
||||
}
|
||||
@@ -39,7 +40,9 @@ namespace Managing.Application.Trading.Commands
|
||||
IsForPaperTrading = isForPaperTrading;
|
||||
Price = price;
|
||||
SignalIdentifier = signalIdentifier;
|
||||
InitiatorIdentifier = initiatorIdentifier ?? throw new ArgumentNullException(nameof(initiatorIdentifier), "InitiatorIdentifier is required");
|
||||
InitiatorIdentifier = initiatorIdentifier ??
|
||||
throw new ArgumentNullException(nameof(initiatorIdentifier),
|
||||
"InitiatorIdentifier is required");
|
||||
}
|
||||
|
||||
public string SignalIdentifier { get; set; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Models.Proxy;
|
||||
@@ -45,11 +46,17 @@ public class GmxPosition
|
||||
|
||||
[JsonProperty("liquidationPrice")] public double LiquidationPrice { get; set; }
|
||||
|
||||
[JsonProperty("stopLoss")] public GmxTrade StopLoss { get; set; }
|
||||
[JsonProperty("StopLoss")]
|
||||
[JsonPropertyName("StopLoss")]
|
||||
public GmxTrade StopLoss { get; set; }
|
||||
|
||||
[JsonProperty("takeProfit1")] public GmxTrade TakeProfit1 { get; set; }
|
||||
[JsonProperty("TakeProfit1")]
|
||||
[JsonPropertyName("TakeProfit1")]
|
||||
public GmxTrade TakeProfit1 { get; set; }
|
||||
|
||||
[JsonProperty("open")] public GmxTrade Open { get; set; }
|
||||
[JsonProperty("Open")]
|
||||
[JsonPropertyName("Open")]
|
||||
public GmxTrade Open { get; set; }
|
||||
}
|
||||
|
||||
public class GetGmxPositionsResponse : Web3ProxyBaseResponse
|
||||
|
||||
119
src/Managing.Web3Proxy/package-lock.json
generated
119
src/Managing.Web3Proxy/package-lock.json
generated
@@ -36,10 +36,11 @@
|
||||
"fastify-plugin": "^5.0.1",
|
||||
"form-data": "^4.0.1",
|
||||
"knex": "^3.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mysql2": "^3.11.3",
|
||||
"postgrator": "^8.0.0",
|
||||
"query-string": "^9.1.1",
|
||||
"viem": "^2.23.15",
|
||||
"viem": "2.37.1",
|
||||
"vitest": "^3.0.8",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
@@ -56,6 +57,8 @@
|
||||
},
|
||||
"node_modules/@adraffy/ens-normalize": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz",
|
||||
"integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
@@ -742,11 +745,25 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
|
||||
"integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.8.1",
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz",
|
||||
"integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.7.1"
|
||||
"@noble/hashes": "1.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
@@ -756,7 +773,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.7.1",
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^14.21.3 || >=16"
|
||||
@@ -1514,30 +1533,36 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@scure/base": {
|
||||
"version": "1.2.4",
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
|
||||
"integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip32": {
|
||||
"version": "1.6.2",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz",
|
||||
"integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.8.1",
|
||||
"@noble/hashes": "~1.7.1",
|
||||
"@scure/base": "~1.2.2"
|
||||
"@noble/curves": "~1.9.0",
|
||||
"@noble/hashes": "~1.8.0",
|
||||
"@scure/base": "~1.2.5"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39": {
|
||||
"version": "1.5.4",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz",
|
||||
"integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.7.1",
|
||||
"@scure/base": "~1.2.4"
|
||||
"@noble/hashes": "~1.8.0",
|
||||
"@scure/base": "~1.2.5"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
@@ -2050,6 +2075,8 @@
|
||||
},
|
||||
"node_modules/abitype": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz",
|
||||
"integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/wevm"
|
||||
@@ -5108,7 +5135,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/isows": {
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz",
|
||||
"integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -5482,6 +5511,8 @@
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
@@ -5937,7 +5968,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ox": {
|
||||
"version": "0.6.9",
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/ox/-/ox-0.9.3.tgz",
|
||||
"integrity": "sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -5946,12 +5979,13 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@adraffy/ens-normalize": "^1.10.1",
|
||||
"@noble/curves": "^1.6.0",
|
||||
"@noble/hashes": "^1.5.0",
|
||||
"@scure/bip32": "^1.5.0",
|
||||
"@scure/bip39": "^1.4.0",
|
||||
"abitype": "^1.0.6",
|
||||
"@adraffy/ens-normalize": "^1.11.0",
|
||||
"@noble/ciphers": "^1.3.0",
|
||||
"@noble/curves": "1.9.1",
|
||||
"@noble/hashes": "^1.8.0",
|
||||
"@scure/bip32": "^1.7.0",
|
||||
"@scure/bip39": "^1.6.0",
|
||||
"abitype": "^1.0.9",
|
||||
"eventemitter3": "5.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -5963,6 +5997,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ox/node_modules/abitype": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz",
|
||||
"integrity": "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/wevm"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.4",
|
||||
"zod": "^3.22.0 || ^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
},
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"dev": true,
|
||||
@@ -7758,9 +7813,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/viem": {
|
||||
"version": "2.23.15",
|
||||
"resolved": "https://registry.npmjs.org/viem/-/viem-2.23.15.tgz",
|
||||
"integrity": "sha512-2t9lROkSzj/ciEZ08NqAHZ6c+J1wKLwJ4qpUxcHdVHcLBt6GfO9+ycuZycTT05ckfJ6TbwnMXMa3bMonvhtUMw==",
|
||||
"version": "2.37.1",
|
||||
"resolved": "https://registry.npmjs.org/viem/-/viem-2.37.1.tgz",
|
||||
"integrity": "sha512-IzacdIXYlOvzDJwNKIVa53LP/LaP70qvBGAIoGH6R+n06S/ru/nnQxLNZ6+JImvIcxwNwgAl0jUA6FZEIQQWSw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -7769,14 +7824,14 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "1.8.1",
|
||||
"@noble/hashes": "1.7.1",
|
||||
"@scure/bip32": "1.6.2",
|
||||
"@scure/bip39": "1.5.4",
|
||||
"@noble/curves": "1.9.1",
|
||||
"@noble/hashes": "1.8.0",
|
||||
"@scure/bip32": "1.7.0",
|
||||
"@scure/bip39": "1.6.0",
|
||||
"abitype": "1.0.8",
|
||||
"isows": "1.0.6",
|
||||
"ox": "0.6.9",
|
||||
"ws": "8.18.1"
|
||||
"isows": "1.0.7",
|
||||
"ox": "0.9.3",
|
||||
"ws": "8.18.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.4"
|
||||
@@ -8161,7 +8216,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.1",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
||||
@@ -55,10 +55,11 @@
|
||||
"fastify-plugin": "^5.0.1",
|
||||
"form-data": "^4.0.1",
|
||||
"knex": "^3.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mysql2": "^3.11.3",
|
||||
"postgrator": "^8.0.0",
|
||||
"query-string": "^9.1.1",
|
||||
"viem": "^2.23.15",
|
||||
"viem": "2.37.1",
|
||||
"vitest": "^3.0.8",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,64 +1,64 @@
|
||||
import {Abi, erc20Abi} from "viem";
|
||||
|
||||
import ArbitrumNodeInterface from "./ArbitrumNodeInterface.json";
|
||||
import ClaimHandler from "./ClaimHandler.json";
|
||||
import CustomErrors from "./CustomErrors.json";
|
||||
import DataStore from "./DataStore.json";
|
||||
import ERC20PermitInterface from "./ERC20PermitInterface.json";
|
||||
import ERC721 from "./ERC721.json";
|
||||
import EventEmitter from "./EventEmitter.json";
|
||||
import ExchangeRouter from "./ExchangeRouter.json";
|
||||
import GelatoRelayRouter from "./GelatoRelayRouter.json";
|
||||
import GlpManager from "./GlpManager.json";
|
||||
import GlvReader from "./GlvReader.json";
|
||||
import GlvRouter from "./GlvRouter.json";
|
||||
import GMT from "./GMT.json";
|
||||
import GmxMigrator from "./GmxMigrator.json";
|
||||
import GovToken from "./GovToken.json";
|
||||
import LayerZeroProvider from "./LayerZeroProvider.json";
|
||||
import MintableBaseToken from "./MintableBaseToken.json";
|
||||
import Multicall from "./Multicall.json";
|
||||
import MultichainClaimsRouter from "./MultichainClaimsRouter.json";
|
||||
import MultichainGlvRouter from "./MultichainGlvRouter.json";
|
||||
import MultichainGmRouter from "./MultichainGmRouter.json";
|
||||
import MultichainOrderRouter from "./MultichainOrderRouter.json";
|
||||
import MultichainSubaccountRouter from "./MultichainSubaccountRouter.json";
|
||||
import MultichainTransferRouter from "./MultichainTransferRouter.json";
|
||||
import MultichainUtils from "./MultichainUtils.json";
|
||||
import MultichainVault from "./MultichainVault.json";
|
||||
import OrderBook from "./OrderBook.json";
|
||||
import OrderBookReader from "./OrderBookReader.json";
|
||||
import OrderExecutor from "./OrderExecutor.json";
|
||||
import PositionManager from "./PositionManager.json";
|
||||
import PositionRouter from "./PositionRouter.json";
|
||||
import Reader from "./Reader.json";
|
||||
import ReaderV2 from "./ReaderV2.json";
|
||||
import ReferralStorage from "./ReferralStorage.json";
|
||||
import RelayParams from "./RelayParams.json";
|
||||
import RewardReader from "./RewardReader.json";
|
||||
import RewardRouter from "./RewardRouter.json";
|
||||
import RewardTracker from "./RewardTracker.json";
|
||||
import RouterV2 from "./Router-v2.json";
|
||||
import Router from "./Router.json";
|
||||
import SmartAccount from "./SmartAccount.json";
|
||||
import StBTC from "./StBTC.json";
|
||||
import SubaccountGelatoRelayRouter from "./SubaccountGelatoRelayRouter.json";
|
||||
import SubaccountRouter from "./SubaccountRouter.json";
|
||||
import SyntheticsReader from "./SyntheticsReader.json";
|
||||
import SyntheticsRouter from "./SyntheticsRouter.json";
|
||||
import Timelock from "./Timelock.json";
|
||||
import Token from "./Token.json";
|
||||
import Treasury from "./Treasury.json";
|
||||
import UniPool from "./UniPool.json";
|
||||
import UniswapV2 from "./UniswapV2.json";
|
||||
import Vault from "./Vault.json";
|
||||
import VaultReader from "./VaultReader.json";
|
||||
import VaultV2 from "./VaultV2.json";
|
||||
import VaultV2b from "./VaultV2b.json";
|
||||
import Vester from "./Vester.json";
|
||||
import WETH from "./WETH.json";
|
||||
import YieldFarm from "./YieldFarm.json";
|
||||
import YieldToken from "./YieldToken.json";
|
||||
import ArbitrumNodeInterface from "./ArbitrumNodeInterface.json" with {type: "json"};
|
||||
import ClaimHandler from "./ClaimHandler.json" with {type: "json"};
|
||||
import CustomErrors from "./CustomErrors.json" with {type: "json"};
|
||||
import DataStore from "./DataStore.json" with {type: "json"};
|
||||
import ERC20PermitInterface from "./ERC20PermitInterface.json" with {type: "json"};
|
||||
import ERC721 from "./ERC721.json" with {type: "json"};
|
||||
import EventEmitter from "./EventEmitter.json" with {type: "json"};
|
||||
import ExchangeRouter from "./ExchangeRouter.json" with {type: "json"};
|
||||
import GelatoRelayRouter from "./GelatoRelayRouter.json" with {type: "json"};
|
||||
import GlpManager from "./GlpManager.json" with {type: "json"};
|
||||
import GlvReader from "./GlvReader.json" with {type: "json"};
|
||||
import GlvRouter from "./GlvRouter.json" with {type: "json"};
|
||||
import GMT from "./GMT.json" with {type: "json"};
|
||||
import GmxMigrator from "./GmxMigrator.json" with {type: "json"};
|
||||
import GovToken from "./GovToken.json" with {type: "json"};
|
||||
import LayerZeroProvider from "./LayerZeroProvider.json" with {type: "json"};
|
||||
import MintableBaseToken from "./MintableBaseToken.json" with {type: "json"};
|
||||
import Multicall from "./Multicall.json" with {type: "json"};
|
||||
import MultichainClaimsRouter from "./MultichainClaimsRouter.json" with {type: "json"};
|
||||
import MultichainGlvRouter from "./MultichainGlvRouter.json" with {type: "json"};
|
||||
import MultichainGmRouter from "./MultichainGmRouter.json" with {type: "json"};
|
||||
import MultichainOrderRouter from "./MultichainOrderRouter.json" with {type: "json"};
|
||||
import MultichainSubaccountRouter from "./MultichainSubaccountRouter.json" with {type: "json"};
|
||||
import MultichainTransferRouter from "./MultichainTransferRouter.json" with {type: "json"};
|
||||
import MultichainUtils from "./MultichainUtils.json" with {type: "json"};
|
||||
import MultichainVault from "./MultichainVault.json" with {type: "json"};
|
||||
import OrderBook from "./OrderBook.json" with {type: "json"};
|
||||
import OrderBookReader from "./OrderBookReader.json" with {type: "json"};
|
||||
import OrderExecutor from "./OrderExecutor.json" with {type: "json"};
|
||||
import PositionManager from "./PositionManager.json" with {type: "json"};
|
||||
import PositionRouter from "./PositionRouter.json" with {type: "json"};
|
||||
import Reader from "./Reader.json" with {type: "json"};
|
||||
import ReaderV2 from "./ReaderV2.json" with {type: "json"};
|
||||
import ReferralStorage from "./ReferralStorage.json" with {type: "json"};
|
||||
import RelayParams from "./RelayParams.json" with {type: "json"};
|
||||
import RewardReader from "./RewardReader.json" with {type: "json"};
|
||||
import RewardRouter from "./RewardRouter.json" with {type: "json"};
|
||||
import RewardTracker from "./RewardTracker.json" with {type: "json"};
|
||||
import RouterV2 from "./Router-v2.json" with {type: "json"};
|
||||
import Router from "./Router.json" with {type: "json"};
|
||||
import SmartAccount from "./SmartAccount.json" with {type: "json"};
|
||||
import StBTC from "./StBTC.json" with {type: "json"};
|
||||
import SubaccountGelatoRelayRouter from "./SubaccountGelatoRelayRouter.json" with {type: "json"};
|
||||
import SubaccountRouter from "./SubaccountRouter.json" with {type: "json"};
|
||||
import SyntheticsReader from "./SyntheticsReader.json" with {type: "json"};
|
||||
import SyntheticsRouter from "./SyntheticsRouter.json" with {type: "json"};
|
||||
import Timelock from "./Timelock.json" with {type: "json"};
|
||||
import Token from "./Token.json" with {type: "json"};
|
||||
import Treasury from "./Treasury.json" with {type: "json"};
|
||||
import UniPool from "./UniPool.json" with {type: "json"};
|
||||
import UniswapV2 from "./UniswapV2.json" with {type: "json"};
|
||||
import Vault from "./Vault.json" with {type: "json"};
|
||||
import VaultReader from "./VaultReader.json" with {type: "json"};
|
||||
import VaultV2 from "./VaultV2.json" with {type: "json"};
|
||||
import VaultV2b from "./VaultV2b.json" with {type: "json"};
|
||||
import Vester from "./Vester.json" with {type: "json"};
|
||||
import WETH from "./WETH.json" with {type: "json"};
|
||||
import YieldFarm from "./YieldFarm.json" with {type: "json"};
|
||||
import YieldToken from "./YieldToken.json" with {type: "json"};
|
||||
|
||||
export type AbiId =
|
||||
| "AbstractSubaccountApprovalNonceable"
|
||||
|
||||
@@ -10,8 +10,8 @@ export const BATCH_CONFIGS: Record<
|
||||
> = {
|
||||
[ARBITRUM]: {
|
||||
http: {
|
||||
batchSize: 0, // disable batches, here batchSize is the number of eth_calls in a batch
|
||||
wait: 0, // keep this setting in case batches are enabled in future
|
||||
batchSize: 10, // Enable batching with up to 10 eth_calls per batch
|
||||
wait: 16, // 16ms wait time to allow batching
|
||||
},
|
||||
client: {
|
||||
multicall: {
|
||||
|
||||
@@ -1,32 +1,77 @@
|
||||
import {arbitrum, avalanche, avalancheFuji, Chain} from "viem/chains";
|
||||
import {arbitrum, arbitrumSepolia, avalanche, avalancheFuji, base, Chain, optimismSepolia, sepolia} from "viem/chains";
|
||||
import {defineChain} from "viem/utils";
|
||||
import {GasLimitsConfig} from "../types/fees";
|
||||
|
||||
export const AVALANCHE = 43114;
|
||||
export const AVALANCHE_FUJI = 43113;
|
||||
export const ARBITRUM = 42161;
|
||||
export const BSС_MAINNET = 56;
|
||||
export const BSС_TESTNET = 97;
|
||||
export const ETH_MAINNET = 1;
|
||||
// Production
|
||||
export const AVALANCHE = 43114;
|
||||
export const ARBITRUM = 42161;
|
||||
export const BOTANIX = 3637;
|
||||
export const BASE = 8453;
|
||||
// Production source
|
||||
export const SOURCE_BASE_MAINNET = 8453;
|
||||
// Testnets
|
||||
export const AVALANCHE_FUJI = 43113;
|
||||
export const ARBITRUM_SEPOLIA = 421614;
|
||||
// Testnet source
|
||||
export const SOURCE_OPTIMISM_SEPOLIA = 11155420;
|
||||
export const SOURCE_SEPOLIA = 11155111;
|
||||
|
||||
export const SUPPORTED_CHAIN_IDS = [ARBITRUM, AVALANCHE];
|
||||
export const SUPPORTED_CHAIN_IDS_DEV = [...SUPPORTED_CHAIN_IDS, AVALANCHE_FUJI];
|
||||
export const CONTRACTS_CHAIN_IDS: ContractsChainId[] = [ARBITRUM, AVALANCHE, BOTANIX];
|
||||
export const CONTRACTS_CHAIN_IDS_DEV: ContractsChainId[] = [...CONTRACTS_CHAIN_IDS, AVALANCHE_FUJI, ARBITRUM_SEPOLIA];
|
||||
|
||||
export const HIGH_EXECUTION_FEES_MAP = {
|
||||
export type ContractsChainId =
|
||||
| typeof ARBITRUM
|
||||
| typeof AVALANCHE
|
||||
| typeof AVALANCHE_FUJI
|
||||
| typeof BOTANIX
|
||||
| typeof ARBITRUM_SEPOLIA;
|
||||
|
||||
export type SettlementChainId = typeof ARBITRUM_SEPOLIA | typeof ARBITRUM | typeof AVALANCHE;
|
||||
export type SourceChainId = typeof SOURCE_OPTIMISM_SEPOLIA | typeof SOURCE_SEPOLIA | typeof SOURCE_BASE_MAINNET;
|
||||
export type AnyChainId = ContractsChainId | SettlementChainId | SourceChainId;
|
||||
|
||||
export type ChainName =
|
||||
| "Arbitrum"
|
||||
| "Avalanche"
|
||||
| "Avalanche Fuji"
|
||||
| "Arbitrum Sepolia"
|
||||
| "Optimism Sepolia"
|
||||
| "Sepolia"
|
||||
| "Botanix"
|
||||
| "Base";
|
||||
|
||||
export const CHAIN_NAMES_MAP: Record<AnyChainId, ChainName> = {
|
||||
[ARBITRUM]: "Arbitrum",
|
||||
[AVALANCHE]: "Avalanche",
|
||||
[AVALANCHE_FUJI]: "Avalanche Fuji",
|
||||
[BOTANIX]: "Botanix",
|
||||
[ARBITRUM_SEPOLIA]: "Arbitrum Sepolia",
|
||||
[SOURCE_OPTIMISM_SEPOLIA]: "Optimism Sepolia",
|
||||
[SOURCE_SEPOLIA]: "Sepolia",
|
||||
[SOURCE_BASE_MAINNET]: "Base",
|
||||
};
|
||||
|
||||
export const HIGH_EXECUTION_FEES_MAP: Record<ContractsChainId, number> = {
|
||||
[ARBITRUM]: 5, // 5 USD
|
||||
[AVALANCHE]: 5, // 5 USD
|
||||
[AVALANCHE_FUJI]: 5, // 5 USD
|
||||
[BOTANIX]: 5, // 5 USD
|
||||
[ARBITRUM_SEPOLIA]: 5, // 5 USD
|
||||
};
|
||||
|
||||
// added to maxPriorityFeePerGas
|
||||
// applied to EIP-1559 transactions only
|
||||
// is not applied to execution fee calculation
|
||||
export const MAX_FEE_PER_GAS_MAP = {
|
||||
export const MAX_FEE_PER_GAS_MAP: Record<number, bigint> = {
|
||||
[AVALANCHE]: 200000000000n, // 200 gwei
|
||||
[BOTANIX]: 20n,
|
||||
};
|
||||
|
||||
// added to maxPriorityFeePerGas
|
||||
// applied to EIP-1559 transactions only
|
||||
// is also applied to the execution fee calculation
|
||||
export const GAS_PRICE_PREMIUM_MAP = {
|
||||
export const GAS_PRICE_PREMIUM_MAP: Record<number, bigint> = {
|
||||
[ARBITRUM]: 0n,
|
||||
[AVALANCHE]: 6000000000n, // 6 gwei
|
||||
};
|
||||
@@ -34,16 +79,28 @@ export const GAS_PRICE_PREMIUM_MAP = {
|
||||
/*
|
||||
that was a constant value in ethers v5, after ethers v6 migration we use it as a minimum for maxPriorityFeePerGas
|
||||
*/
|
||||
export const MAX_PRIORITY_FEE_PER_GAS_MAP: Record<number, bigint | undefined> = {
|
||||
export const MAX_PRIORITY_FEE_PER_GAS_MAP: Record<ContractsChainId, bigint | undefined> = {
|
||||
[ARBITRUM]: 1500000000n,
|
||||
[AVALANCHE]: 1500000000n,
|
||||
[AVALANCHE_FUJI]: 1500000000n,
|
||||
[ARBITRUM_SEPOLIA]: 1500000000n,
|
||||
[BOTANIX]: 7n,
|
||||
};
|
||||
|
||||
export const EXCESSIVE_EXECUTION_FEES_MAP = {
|
||||
export const EXCESSIVE_EXECUTION_FEES_MAP: Partial<Record<ContractsChainId, number>> = {
|
||||
[ARBITRUM]: 10, // 10 USD
|
||||
[AVALANCHE]: 10, // 10 USD
|
||||
[AVALANCHE_FUJI]: 10, // 10 USD
|
||||
[BOTANIX]: 10, // 10 USD
|
||||
};
|
||||
|
||||
// avoid botanix gas spikes when chain is not actively used
|
||||
// if set, execution fee value should not be less than this in USD equivalent
|
||||
export const MIN_EXECUTION_FEE_USD: Partial<Record<ContractsChainId, bigint | undefined>> = {
|
||||
[ARBITRUM]: undefined,
|
||||
[AVALANCHE]: undefined,
|
||||
[AVALANCHE_FUJI]: undefined,
|
||||
[BOTANIX]: 1000000000000000000000000000n, // 1e27 $0.001
|
||||
};
|
||||
|
||||
// added to gasPrice
|
||||
@@ -63,34 +120,74 @@ export const EXCESSIVE_EXECUTION_FEES_MAP = {
|
||||
//
|
||||
// this buffer could also cause issues on a blockchain that uses passed gas price
|
||||
// especially if execution fee buffer and lower than gas price buffer defined bellow
|
||||
export const GAS_PRICE_BUFFER_MAP = {
|
||||
export const GAS_PRICE_BUFFER_MAP: Record<number, bigint> = {
|
||||
[ARBITRUM]: 2000n, // 20%
|
||||
};
|
||||
|
||||
const CHAIN_BY_CHAIN_ID = {
|
||||
export const botanix: Chain = defineChain({
|
||||
id: BOTANIX,
|
||||
name: "Botanix",
|
||||
nativeCurrency: {
|
||||
name: "Bitcoin",
|
||||
symbol: "BTC",
|
||||
decimals: 18,
|
||||
},
|
||||
rpcUrls: {
|
||||
default: {
|
||||
http: [
|
||||
// this rpc returns incorrect gas price
|
||||
// "https://rpc.botanixlabs.com",
|
||||
|
||||
"https://rpc.ankr.com/botanix_mainnet",
|
||||
],
|
||||
},
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: "BotanixScan",
|
||||
url: "https://botanixscan.io",
|
||||
},
|
||||
},
|
||||
contracts: {
|
||||
multicall3: {
|
||||
address: "0x4BaA24f93a657f0c1b4A0Ffc72B91011E35cA46b",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const VIEM_CHAIN_BY_CHAIN_ID: Record<AnyChainId, Chain> = {
|
||||
[AVALANCHE_FUJI]: avalancheFuji,
|
||||
[ARBITRUM]: arbitrum,
|
||||
[AVALANCHE]: avalanche,
|
||||
[ARBITRUM_SEPOLIA]: arbitrumSepolia,
|
||||
[BOTANIX]: botanix,
|
||||
[SOURCE_OPTIMISM_SEPOLIA]: optimismSepolia,
|
||||
[SOURCE_SEPOLIA]: sepolia,
|
||||
[SOURCE_BASE_MAINNET]: base,
|
||||
};
|
||||
|
||||
export const getChain = (chainId: number): Chain => {
|
||||
return CHAIN_BY_CHAIN_ID[chainId];
|
||||
export function getChainName(chainId: number): ChainName {
|
||||
return CHAIN_NAMES_MAP[chainId];
|
||||
}
|
||||
|
||||
export const getViemChain = (chainId: number): Chain => {
|
||||
return VIEM_CHAIN_BY_CHAIN_ID[chainId];
|
||||
};
|
||||
|
||||
export function getHighExecutionFee(chainId) {
|
||||
export function getHighExecutionFee(chainId: number) {
|
||||
return HIGH_EXECUTION_FEES_MAP[chainId] ?? 5;
|
||||
}
|
||||
|
||||
export function getExcessiveExecutionFee(chainId) {
|
||||
export function getExcessiveExecutionFee(chainId: number) {
|
||||
return EXCESSIVE_EXECUTION_FEES_MAP[chainId] ?? 10;
|
||||
}
|
||||
|
||||
export function isSupportedChain(chainId: number, dev = false) {
|
||||
return (dev ? SUPPORTED_CHAIN_IDS_DEV : SUPPORTED_CHAIN_IDS).includes(chainId);
|
||||
export function isContractsChain(chainId: number, dev = false): chainId is ContractsChainId {
|
||||
return (dev ? CONTRACTS_CHAIN_IDS_DEV : CONTRACTS_CHAIN_IDS).includes(chainId as ContractsChainId);
|
||||
}
|
||||
|
||||
export const EXECUTION_FEE_CONFIG_V2: {
|
||||
[chainId: number]: {
|
||||
[chainId in ContractsChainId]: {
|
||||
shouldUseMaxPriorityFeePerGas: boolean;
|
||||
defaultBufferBps?: number;
|
||||
};
|
||||
@@ -107,4 +204,59 @@ export const EXECUTION_FEE_CONFIG_V2: {
|
||||
shouldUseMaxPriorityFeePerGas: false,
|
||||
defaultBufferBps: 3000, // 30%
|
||||
},
|
||||
[ARBITRUM_SEPOLIA]: {
|
||||
shouldUseMaxPriorityFeePerGas: false,
|
||||
defaultBufferBps: 1000, // 10%
|
||||
},
|
||||
[BOTANIX]: {
|
||||
shouldUseMaxPriorityFeePerGas: true,
|
||||
defaultBufferBps: 3000, // 30%
|
||||
},
|
||||
};
|
||||
|
||||
type StaticGasLimitsConfig = Pick<
|
||||
GasLimitsConfig,
|
||||
| "createOrderGasLimit"
|
||||
| "updateOrderGasLimit"
|
||||
| "cancelOrderGasLimit"
|
||||
| "tokenPermitGasLimit"
|
||||
| "gmxAccountCollateralGasLimit"
|
||||
>;
|
||||
|
||||
export const GAS_LIMITS_STATIC_CONFIG: Record<ContractsChainId, StaticGasLimitsConfig> = {
|
||||
[ARBITRUM]: {
|
||||
createOrderGasLimit: 1_000_000n,
|
||||
updateOrderGasLimit: 800_000n,
|
||||
cancelOrderGasLimit: 700_000n,
|
||||
tokenPermitGasLimit: 90_000n,
|
||||
gmxAccountCollateralGasLimit: 0n,
|
||||
},
|
||||
[AVALANCHE]: {
|
||||
createOrderGasLimit: 1_000_000n,
|
||||
updateOrderGasLimit: 800_000n,
|
||||
cancelOrderGasLimit: 700_000n,
|
||||
tokenPermitGasLimit: 90_000n,
|
||||
gmxAccountCollateralGasLimit: 0n,
|
||||
},
|
||||
[AVALANCHE_FUJI]: {
|
||||
createOrderGasLimit: 1_000_000n,
|
||||
updateOrderGasLimit: 800_000n,
|
||||
cancelOrderGasLimit: 700_000n,
|
||||
tokenPermitGasLimit: 90_000n,
|
||||
gmxAccountCollateralGasLimit: 0n,
|
||||
},
|
||||
[ARBITRUM_SEPOLIA]: {
|
||||
createOrderGasLimit: 1_000_000n,
|
||||
updateOrderGasLimit: 800_000n,
|
||||
cancelOrderGasLimit: 1_500_000n,
|
||||
tokenPermitGasLimit: 90_000n,
|
||||
gmxAccountCollateralGasLimit: 400_000n,
|
||||
},
|
||||
[BOTANIX]: {
|
||||
createOrderGasLimit: 1_000_000n,
|
||||
updateOrderGasLimit: 800_000n,
|
||||
cancelOrderGasLimit: 700_000n,
|
||||
tokenPermitGasLimit: 90_000n,
|
||||
gmxAccountCollateralGasLimit: 0n,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {type Address, zeroAddress} from "viem";
|
||||
import {ARBITRUM, AVALANCHE, AVALANCHE_FUJI} from "./chains.js";
|
||||
import {ARBITRUM, ARBITRUM_SEPOLIA, AVALANCHE, AVALANCHE_FUJI, BOTANIX, ContractsChainId} from "./chains.js";
|
||||
|
||||
|
||||
export const CONTRACTS = {
|
||||
[ARBITRUM]: {
|
||||
// arbitrum mainnet
|
||||
// V1
|
||||
Vault: "0x489ee077994B6658eAfA855C308275EAd8097C4A",
|
||||
Router: "0xaBBc5F99639c9B6bCb58544ddf04EFA6802F4064",
|
||||
VaultReader: "0xfebB9f4CAC4cD523598fE1C5771181440143F24A",
|
||||
@@ -59,18 +59,35 @@ export const CONTRACTS = {
|
||||
SyntheticsReader: "0x65A6CC451BAfF7e7B4FDAb4157763aB4b6b44D0E",
|
||||
SyntheticsRouter: "0x7452c558d45f8afC8c83dAe62C3f8A5BE19c71f6",
|
||||
|
||||
GlvReader: "0x6a9505D0B44cFA863d9281EA5B0b34cB36243b45",
|
||||
GlvRouter: "0x994c598e3b0661bb805d53c6fa6b4504b23b68dd",
|
||||
GlvReader: "0xb51e34dc3A7c80E4ABbC3800aD0e487b7b878339",
|
||||
GlvRouter: "0x10Fa5Bd343373101654E896B43Ca38Fd8f3789F9",
|
||||
GlvVault: "0x393053B58f9678C9c28c2cE941fF6cac49C3F8f9",
|
||||
|
||||
GelatoRelayRouter: "0x0C08518C41755C6907135266dCCf09d51aE53CC4",
|
||||
SubaccountGelatoRelayRouter: "0xA1D94802EcD642051B677dBF37c8E78ce6dd3784",
|
||||
|
||||
MultichainClaimsRouter: "0x2A7244EE5373D2F161cE99F0D144c12860D651Af",
|
||||
MultichainGlvRouter: "0xFdaFa6fbd4B480017FD37205Cb3A24AE93823956",
|
||||
MultichainGmRouter: "0xF53e30CE07f148fdE6e531Be7dC0b6ad670E8C6e",
|
||||
MultichainOrderRouter: "0x3c796504d47013Ea0552CCa57373B59DF03D34a0",
|
||||
MultichainSubaccountRouter: "0x99CD306B777C5aAb842bA65e4f7FF0554ECDe808",
|
||||
MultichainTransferRouter: "0xC1D1354A948bf717d6d873e5c0bE614359AF954D",
|
||||
MultichainVault: "0xCeaadFAf6A8C489B250e407987877c5fDfcDBE6E",
|
||||
LayerZeroProvider: "0x7129Ea01F0826c705d6F7ab01Cf3C06bb83E9397",
|
||||
|
||||
ChainlinkPriceFeedProvider: "0x38B8dB61b724b51e42A88Cb8eC564CD685a0f53B",
|
||||
ClaimHandler: "0x28f1F4AA95F49FAB62464536A269437B13d48976",
|
||||
|
||||
// External
|
||||
ExternalHandler: "0x389CEf541397e872dC04421f166B5Bc2E0b374a5",
|
||||
|
||||
OpenOceanRouter: "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64",
|
||||
|
||||
Multicall: "0x842ec2c7d803033edf55e478f461fc547bc54eb2",
|
||||
Multicall: "0xe79118d6D92a4b23369ba356C90b9A7ABf1CB961",
|
||||
ArbitrumNodeInterface: "0x00000000000000000000000000000000000000C8",
|
||||
LayerZeroEndpoint: "0x1a44076050125825900e736c501f859c50fE728c",
|
||||
GelatoRelayAddress: "0xaBcC9b596420A9E9172FD5938620E265a0f9Df92",
|
||||
},
|
||||
[AVALANCHE]: {
|
||||
// avalanche
|
||||
// V1
|
||||
Vault: "0x9ab2De34A33fB459b538c43f251eB825645e8595",
|
||||
Router: "0x5F719c2F1095F7B9fc68a68e35B51194f4b6abe8",
|
||||
VaultReader: "0x66eC8fc33A26feAEAe156afA3Cb46923651F6f0D",
|
||||
@@ -117,27 +134,126 @@ export const CONTRACTS = {
|
||||
// Synthetics
|
||||
DataStore: "0x2F0b22339414ADeD7D5F06f9D604c7fF5b2fe3f6",
|
||||
EventEmitter: "0xDb17B211c34240B014ab6d61d4A31FA0C0e20c26",
|
||||
SubaccountRouter: "0x5aEb6AD978f59e220aA9099e09574e1c5E03AafD",
|
||||
ExchangeRouter: "0xe37d052e1deb99901de205e7186e31a36e4ef70c",
|
||||
SubaccountRouter: "0x88a5c6D94634Abd7745f5348e5D8C42868ed4AC3",
|
||||
ExchangeRouter: "0xF0864BE1C39C0AB28a8f1918BC8321beF8F7C317",
|
||||
DepositVault: "0x90c670825d0C62ede1c5ee9571d6d9a17A722DFF",
|
||||
WithdrawalVault: "0xf5F30B10141E1F63FC11eD772931A8294a591996",
|
||||
OrderVault: "0xD3D60D22d415aD43b7e64b510D86A30f19B1B12C",
|
||||
ShiftVault: "0x7fC46CCb386e9bbBFB49A2639002734C3Ec52b39",
|
||||
SyntheticsReader: "0x618fCEe30D9A26e8533C3B244CAd2D6486AFf655",
|
||||
SyntheticsReader: "0x1EC018d2b6ACCA20a0bEDb86450b7E27D1D8355B",
|
||||
SyntheticsRouter: "0x820F5FfC5b525cD4d88Cd91aCf2c28F16530Cc68",
|
||||
|
||||
GlvReader: "0xae9596a1C438675AcC75f69d32E21Ac9c8fF99bD",
|
||||
GlvRouter: "0x16500c1d8ffe2f695d8dcadf753f664993287ae4",
|
||||
GlvReader: "0x12Ac77003B3D11b0853d1FD12E5AF22a9060eC4b",
|
||||
GlvRouter: "0x4729D9f61c0159F5e02D2C2e5937B3225e55442C",
|
||||
GlvVault: "0x527FB0bCfF63C47761039bB386cFE181A92a4701",
|
||||
|
||||
OpenOceanRouter: "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64",
|
||||
GelatoRelayRouter: "0xa61f92ab63cc5C3d60574d40A6e73861c37aaC95",
|
||||
SubaccountGelatoRelayRouter: "0x58b09FD12863218F2ca156808C2Ae48aaCD0c072",
|
||||
|
||||
MultichainClaimsRouter: "0x9080f8A35Da53F4200a68533FB1dC1cA05357bDB",
|
||||
MultichainGlvRouter: "0x2A7244EE5373D2F161cE99F0D144c12860D651Af",
|
||||
MultichainGmRouter: "0x10Fa5Bd343373101654E896B43Ca38Fd8f3789F9",
|
||||
MultichainOrderRouter: "0x99CD306B777C5aAb842bA65e4f7FF0554ECDe808",
|
||||
MultichainSubaccountRouter: "0xB36a4c6cDeDea3f31b3d16F33553F93b96b178F4",
|
||||
MultichainTransferRouter: "0x8c6e20A2211D1b70cD7c0789EcE44fDB19567621",
|
||||
MultichainVault: "0x6D5F3c723002847B009D07Fe8e17d6958F153E4e",
|
||||
LayerZeroProvider: "0xA1D94802EcD642051B677dBF37c8E78ce6dd3784",
|
||||
|
||||
ChainlinkPriceFeedProvider: "0x05d97cee050bfb81FB3EaD4A9368584F8e72C88e",
|
||||
ClaimHandler: "0x7FfedCAC2eCb2C29dDc027B60D6F8107295Ff2eA",
|
||||
|
||||
// External
|
||||
ExternalHandler: "0xD149573a098223a9185433290a5A5CDbFa54a8A9",
|
||||
OpenOceanRouter: "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64",
|
||||
Multicall: "0x50474CAe810B316c294111807F94F9f48527e7F8",
|
||||
ArbitrumNodeInterface: zeroAddress,
|
||||
LayerZeroEndpoint: "0x1a44076050125825900e736c501f859c50fE728c",
|
||||
GelatoRelayAddress: "0xaBcC9b596420A9E9172FD5938620E265a0f9Df92",
|
||||
},
|
||||
[BOTANIX]: {
|
||||
// Synthetics
|
||||
DataStore: "0xA23B81a89Ab9D7D89fF8fc1b5d8508fB75Cc094d",
|
||||
EventEmitter: "0xAf2E131d483cedE068e21a9228aD91E623a989C2",
|
||||
SubaccountRouter: "0x11E590f6092D557bF71BaDEd50D81521674F8275",
|
||||
ExchangeRouter: "0x72fa3978E2E330C7B2debc23CB676A3ae63333F6",
|
||||
DepositVault: "0x4D12C3D3e750e051e87a2F3f7750fBd94767742c",
|
||||
WithdrawalVault: "0x46BAeAEdbF90Ce46310173A04942e2B3B781Bf0e",
|
||||
OrderVault: "0xe52B3700D17B45dE9de7205DEe4685B4B9EC612D",
|
||||
ShiftVault: "0xa7EE2737249e0099906cB079BCEe85f0bbd837d4",
|
||||
|
||||
Multicall: "0xcA11bde05977b3631167028862bE2a173976CA11",
|
||||
SyntheticsReader: "0xa254B60cbB85a92F6151B10E1233639F601f2F0F",
|
||||
SyntheticsRouter: "0x3d472afcd66F954Fe4909EEcDd5c940e9a99290c",
|
||||
|
||||
GlvReader: "0x490d660A21fB75701b7781E191cB888D1FE38315",
|
||||
GlvRouter: "0x348Eca94e7c6F35430aF1cAccE27C29E9Bef9ae3",
|
||||
GlvVault: "0xd336087512BeF8Df32AF605b492f452Fd6436CD8",
|
||||
|
||||
GelatoRelayRouter: "0x7f8eF83C92B48a4B5B954A24D98a6cD0Ed4D160a",
|
||||
SubaccountGelatoRelayRouter: "0xfbb9C41046E27405224a911f44602C3667f9D8f6",
|
||||
|
||||
MultichainClaimsRouter: "0x790Ee987b9B253374d700b07F16347a7d4C4ff2e",
|
||||
MultichainGlvRouter: "0xEE027373517a6D96Fe62f70E9A0A395cB5a39Eee",
|
||||
MultichainGmRouter: "0x4ef8394CD5DD7E3EE6D30824689eF461783a3360",
|
||||
MultichainOrderRouter: "0x5c5DBbcDf420B5d81d4FfDBa5b26Eb24E6E60d52",
|
||||
MultichainSubaccountRouter: "0xd3B6E962f135634C43415d57A28E688Fb4f15A58",
|
||||
MultichainTransferRouter: "0x901f26a57edCe65Ef3FBcCD260433De9B2279852",
|
||||
MultichainVault: "0x9a535f9343434D96c4a39fF1d90cC685A4F6Fb20",
|
||||
LayerZeroProvider: "0x61af99b07995cb7Ee8c2FACF6D8fb6042FeAA0d9",
|
||||
|
||||
ChainlinkPriceFeedProvider: "0xDc613305e9267f0770072dEaB8c03162e0554b2d",
|
||||
ClaimHandler: "0x3ca0f3ad78a9d0b2a0c060fe86d1141118a285c4",
|
||||
|
||||
// External
|
||||
ExternalHandler: "0x36b906eA6AE7c74aeEE8cDE66D01B3f1f8843872",
|
||||
OpenOceanRouter: zeroAddress,
|
||||
Multicall: "0x4BaA24f93a657f0c1b4A0Ffc72B91011E35cA46b",
|
||||
LayerZeroEndpoint: "0x6F475642a6e85809B1c36Fa62763669b1b48DD5B",
|
||||
ArbitrumNodeInterface: zeroAddress,
|
||||
GelatoRelayAddress: "0x61aCe8fBA7B80AEf8ED67f37CB60bE00180872aD",
|
||||
|
||||
Vault: zeroAddress,
|
||||
Reader: zeroAddress,
|
||||
PositionRouter: zeroAddress,
|
||||
ReferralStorage: zeroAddress,
|
||||
ReferralReader: zeroAddress,
|
||||
VaultReader: zeroAddress,
|
||||
GlpManager: zeroAddress,
|
||||
RewardRouter: zeroAddress,
|
||||
RewardReader: zeroAddress,
|
||||
GlpRewardRouter: zeroAddress,
|
||||
StakedGmxTracker: zeroAddress,
|
||||
FeeGmxTracker: zeroAddress,
|
||||
GLP: zeroAddress,
|
||||
GMX: zeroAddress,
|
||||
ES_GMX: zeroAddress,
|
||||
BN_GMX: zeroAddress,
|
||||
USDG: zeroAddress,
|
||||
BonusGmxTracker: zeroAddress,
|
||||
StakedGlpTracker: zeroAddress,
|
||||
FeeGlpTracker: zeroAddress,
|
||||
ExtendedGmxTracker: zeroAddress,
|
||||
StakedGmxDistributor: zeroAddress,
|
||||
StakedGlpDistributor: zeroAddress,
|
||||
GmxVester: zeroAddress,
|
||||
GlpVester: zeroAddress,
|
||||
AffiliateVester: zeroAddress,
|
||||
Router: zeroAddress,
|
||||
GovToken: zeroAddress,
|
||||
ES_GMX_IOU: zeroAddress,
|
||||
OrderBook: zeroAddress,
|
||||
OrderExecutor: zeroAddress,
|
||||
OrderBookReader: zeroAddress,
|
||||
PositionManager: zeroAddress,
|
||||
UniswapGmxEthPool: zeroAddress,
|
||||
|
||||
// botanix specific
|
||||
NATIVE_TOKEN: "0x0D2437F93Fed6EA64Ef01cCde385FB1263910C56",
|
||||
StBTC: "0xF4586028FFdA7Eca636864F80f8a3f2589E33795",
|
||||
PBTC: "0x0D2437F93Fed6EA64Ef01cCde385FB1263910C56",
|
||||
},
|
||||
|
||||
[AVALANCHE_FUJI]: {
|
||||
// V1
|
||||
Vault: zeroAddress,
|
||||
Router: zeroAddress,
|
||||
VaultReader: zeroAddress,
|
||||
@@ -176,35 +292,128 @@ export const CONTRACTS = {
|
||||
PositionManager: zeroAddress,
|
||||
|
||||
TraderJoeGmxAvaxPool: zeroAddress,
|
||||
ReferralStorage: "0x58726dB901C9DF3654F45a37DD307a0C44b6420e",
|
||||
ReferralStorage: "0x192e82A18a4ab446dD9968f055431b60640B155D",
|
||||
ReferralReader: zeroAddress,
|
||||
|
||||
// Synthetics
|
||||
DataStore: "0xEA1BFb4Ea9A412dCCd63454AbC127431eBB0F0d4",
|
||||
EventEmitter: "0xc67D98AC5803aFD776958622CeEE332A0B2CabB9",
|
||||
ExchangeRouter: "0x52A1c10c462ca4e5219d0Eb4da5052cc73F9050D",
|
||||
SubaccountRouter: "0x0595f01860aa5c4C6091EED096515b4b9FE372CE",
|
||||
ExchangeRouter: "0x0a458C96Ac0B2a130DA4BdF1aAdD4cb7Be036d11",
|
||||
SubaccountRouter: "0xD5EE3ECAF5754CE5Ff74847d0caf094EBB12ed5e",
|
||||
DepositVault: "0x2964d242233036C8BDC1ADC795bB4DeA6fb929f2",
|
||||
WithdrawalVault: "0x74d49B6A630Bf519bDb6E4efc4354C420418A6A2",
|
||||
OrderVault: "0x25D23e8E655727F2687CC808BB9589525A6F599B",
|
||||
ShiftVault: "0x257D0EA0B040E2Cd1D456fB4C66d7814102aD346",
|
||||
SyntheticsReader: "0x16Fb5b8846fbfAe09c034fCdF3D3F9492484DA80",
|
||||
SyntheticsReader: "0xf82Cc6EB57F8FF86bc5c5e90B8BA83DbBFB517eE",
|
||||
SyntheticsRouter: "0x5e7d61e4C52123ADF651961e4833aCc349b61491",
|
||||
Timelock: zeroAddress,
|
||||
|
||||
GlvReader: "0x4599Ed5939C673505B7AFcd020E1d603b0dCAf69",
|
||||
GlvRouter: "0x377d979AB35Cd848497707ffa6Ee91783f925b80",
|
||||
GlvReader: "0xdeaC9ea3c72C102f2a9654b8E1A14Ef86Cdd3146",
|
||||
GlvRouter: "0x6B6595389A0196F882C0f66CB1F401f1D24afEdC",
|
||||
GlvVault: "0x76f93b5240DF811a3fc32bEDd58daA5784e46C96",
|
||||
|
||||
GelatoRelayRouter: "0xC2917611f422b1624D7316375690B532c149F54b",
|
||||
SubaccountGelatoRelayRouter: "0x9022ADce7c964852475aB0de801932BaDEB0C765",
|
||||
|
||||
MultichainClaimsRouter: "0xa080c3E026467E1fa6E76D29A057Bf1261a4ec86",
|
||||
MultichainGlvRouter: "0x5060A75868ca21A54C650a70E96fa92405831b15",
|
||||
MultichainGmRouter: "0xe32632F65198eF3080ccDe22A6d23819203dBc42",
|
||||
MultichainOrderRouter: "0x6169DD9Bc75B1d4B7138109Abe58f5645BA6B8fE",
|
||||
MultichainSubaccountRouter: "0xa51181CC37D23d3a4b4B263D2B54e1F34B834432",
|
||||
MultichainTransferRouter: "0x0bD6966B894D9704Ce540babcd425C93d2BD549C",
|
||||
MultichainVault: "0xFd86A5d9D6dF6f0cB6B0e6A18Bea7CB07Ada4F79",
|
||||
LayerZeroProvider: "0xdaa9194bFD143Af71A8d2cFc8F2c0643094a77C5",
|
||||
|
||||
ChainlinkPriceFeedProvider: "0x2e149AbC99cDC98FB0207d6F184DC323CEBB955B",
|
||||
ClaimHandler: "0x01D68cf13B8f67b041b8D565931e1370774cCeBd",
|
||||
|
||||
// External
|
||||
OpenOceanRouter: zeroAddress,
|
||||
|
||||
ExternalHandler: "0x0d9F90c66C392c4d0e70EE0d399c43729B942512",
|
||||
Multicall: "0x966D1F5c54a714C6443205F0Ec49eEF81F10fdfD",
|
||||
ArbitrumNodeInterface: zeroAddress,
|
||||
LayerZeroEndpoint: "0x6EDCE65403992e310A62460808c4b910D972f10f",
|
||||
},
|
||||
|
||||
Multicall: "0x0f53e512b49202a37c81c6085417C9a9005F2196",
|
||||
[ARBITRUM_SEPOLIA]: {
|
||||
// Synthetics
|
||||
DataStore: "0xCF4c2C4c53157BcC01A596e3788fFF69cBBCD201",
|
||||
EventEmitter: "0xa973c2692C1556E1a3d478e745e9a75624AEDc73",
|
||||
ExchangeRouter: "0x657F9215FA1e839FbA15cF44B1C00D95cF71ed10",
|
||||
SubaccountRouter: "0x7d4dD31B32F6Ae51893B6cffCAb15E75eA30D69b",
|
||||
DepositVault: "0x809Ea82C394beB993c2b6B0d73b8FD07ab92DE5A",
|
||||
WithdrawalVault: "0x7601c9dBbDCf1f5ED1E7Adba4EFd9f2cADa037A5",
|
||||
OrderVault: "0x1b8AC606de71686fd2a1AEDEcb6E0EFba28909a2",
|
||||
ShiftVault: "0x6b6F9B7B9a6b69942DAE74FB95E694ec277117af",
|
||||
SyntheticsReader: "0x37a0A165389B2f959a04685aC8fc126739e86926",
|
||||
SyntheticsRouter: "0x72F13a44C8ba16a678CAD549F17bc9e06d2B8bD2",
|
||||
|
||||
GlvReader: "0x4843D570c726cFb44574c1769f721a49c7e9c350",
|
||||
GlvRouter: "0x7F8af0741e8925C132E84147762902EBBc485d11",
|
||||
GlvVault: "0x40bD50de0977c68ecB958ED4A065E14E1091ce64",
|
||||
|
||||
GelatoRelayRouter: "0x44904137A4d8734a5AB13B32083FFd6B93664491",
|
||||
SubaccountGelatoRelayRouter: "0x209E4408D68EE049957dBba7Ac62177f10ee00ab",
|
||||
|
||||
MultichainClaimsRouter: "0xe06534c26c90AE8c3241BC90dDB69d4Af438f17f",
|
||||
MultichainGlvRouter: "0x29b9a624a29327b1c76317bfF08373281c582B79",
|
||||
MultichainGmRouter: "0x9868Ab73D1cB4DcEEd74e5eB9f86346C935488F3",
|
||||
MultichainOrderRouter: "0x2B977188b3Bf8fbCa2Ca5D6e00DC8542b7690C9E",
|
||||
MultichainSubaccountRouter: "0xf8fbE9411f90618B3c68A8826555Ab54dE090ED7",
|
||||
MultichainTransferRouter: "0xeCfcA6af46B9d20793f82b28bc749dfFC6DEE535",
|
||||
MultichainVault: "0xCd46EF5ed7d08B345c47b5a193A719861Aa2CD91",
|
||||
LayerZeroProvider: "0x3f85e237E950A7FB7cfb6DD4C262353A82588d51",
|
||||
|
||||
ChainlinkPriceFeedProvider: "0xa76BF7f977E80ac0bff49BDC98a27b7b070a937d",
|
||||
ReferralStorage: "0xBbCdA58c228Bb29B5769778181c81Ac8aC546c11",
|
||||
ClaimHandler: "0x96FE82b9C6FE46af537cE465B3befBD7b076C982",
|
||||
|
||||
// External
|
||||
Multicall: "0xD84793ae65842fFac5C20Ab8eaBD699ea1FC79F3",
|
||||
NATIVE_TOKEN: "0x980B62Da83eFf3D4576C647993b0c1D7faf17c73",
|
||||
LayerZeroEndpoint: "0x6EDCE65403992e310A62460808c4b910D972f10f",
|
||||
ArbitrumNodeInterface: "0x00000000000000000000000000000000000000C8",
|
||||
GelatoRelayAddress: "0xaBcC9b596420A9E9172FD5938620E265a0f9Df92",
|
||||
ExternalHandler: zeroAddress,
|
||||
|
||||
GLP: zeroAddress,
|
||||
GMX: zeroAddress,
|
||||
ES_GMX: zeroAddress,
|
||||
BN_GMX: zeroAddress,
|
||||
USDG: zeroAddress,
|
||||
ES_GMX_IOU: zeroAddress,
|
||||
OpenOceanRouter: zeroAddress,
|
||||
Vault: zeroAddress,
|
||||
PositionRouter: zeroAddress,
|
||||
RewardRouter: zeroAddress,
|
||||
StakedGmxTracker: zeroAddress,
|
||||
BonusGmxTracker: zeroAddress,
|
||||
FeeGmxTracker: zeroAddress,
|
||||
StakedGlpTracker: zeroAddress,
|
||||
FeeGlpTracker: zeroAddress,
|
||||
ExtendedGmxTracker: zeroAddress,
|
||||
StakedGmxDistributor: zeroAddress,
|
||||
StakedGlpDistributor: zeroAddress,
|
||||
GmxVester: zeroAddress,
|
||||
GlpVester: zeroAddress,
|
||||
AffiliateVester: zeroAddress,
|
||||
Router: zeroAddress,
|
||||
VaultReader: zeroAddress,
|
||||
Reader: zeroAddress,
|
||||
GlpManager: zeroAddress,
|
||||
RewardReader: zeroAddress,
|
||||
GlpRewardRouter: zeroAddress,
|
||||
Timelock: zeroAddress,
|
||||
},
|
||||
};
|
||||
|
||||
export function getContract(chainId: number, name: string): Address {
|
||||
type ExtractContractNames<T extends object> = {
|
||||
[K in keyof T]: keyof T[K];
|
||||
}[keyof T];
|
||||
|
||||
export type ContractName = ExtractContractNames<typeof CONTRACTS>;
|
||||
|
||||
export function getContract(chainId: ContractsChainId, name: ContractName): Address {
|
||||
if (!CONTRACTS[chainId]) {
|
||||
throw new Error(`Unknown chainId ${chainId}`);
|
||||
}
|
||||
@@ -215,3 +424,4 @@ export function getContract(chainId: number, name: string): Address {
|
||||
|
||||
return CONTRACTS[chainId][name];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {hashData, hashString} from "../utils/hash.js";
|
||||
import {hashData, hashString, keccakString} from "../utils/hash.js";
|
||||
|
||||
export const POSITION_IMPACT_FACTOR_KEY = hashString("POSITION_IMPACT_FACTOR");
|
||||
export const MAX_POSITION_IMPACT_FACTOR_KEY = hashString("MAX_POSITION_IMPACT_FACTOR");
|
||||
@@ -7,6 +7,7 @@ export const POSITION_FEE_FACTOR_KEY = hashString("POSITION_FEE_FACTOR");
|
||||
export const SWAP_IMPACT_FACTOR_KEY = hashString("SWAP_IMPACT_FACTOR");
|
||||
export const SWAP_IMPACT_EXPONENT_FACTOR_KEY = hashString("SWAP_IMPACT_EXPONENT_FACTOR");
|
||||
export const SWAP_FEE_FACTOR_KEY = hashString("SWAP_FEE_FACTOR");
|
||||
export const ATOMIC_SWAP_FEE_FACTOR_KEY = hashString("ATOMIC_SWAP_FEE_FACTOR");
|
||||
export const FEE_RECEIVER_DEPOSIT_FACTOR_KEY = hashString("FEE_RECEIVER_DEPOSIT_FACTOR");
|
||||
export const BORROWING_FEE_RECEIVER_FACTOR_KEY = hashString("BORROWING_FEE_RECEIVER_FACTOR");
|
||||
export const FEE_RECEIVER_WITHDRAWAL_FACTOR_KEY = hashString("FEE_RECEIVER_WITHDRAWAL_FACTOR");
|
||||
@@ -40,6 +41,14 @@ export const MAX_PNL_FACTOR_FOR_TRADERS_KEY = hashString("MAX_PNL_FACTOR_FOR_TRA
|
||||
export const MAX_POSITION_IMPACT_FACTOR_FOR_LIQUIDATIONS_KEY = hashString(
|
||||
"MAX_POSITION_IMPACT_FACTOR_FOR_LIQUIDATIONS"
|
||||
);
|
||||
export const CLAIMABLE_COLLATERAL_DELAY_KEY = hashString("CLAIMABLE_COLLATERAL_DELAY");
|
||||
export const CLAIMABLE_COLLATERAL_REDUCTION_FACTOR_KEY = hashString("CLAIMABLE_COLLATERAL_REDUCTION_FACTOR");
|
||||
export const CLAIMABLE_COLLATERAL_TIME_DIVISOR_KEY = hashString("CLAIMABLE_COLLATERAL_TIME_DIVISOR");
|
||||
export const MAX_LENDABLE_IMPACT_FACTOR_KEY = hashString("MAX_LENDABLE_IMPACT_FACTOR");
|
||||
export const MAX_LENDABLE_IMPACT_USD_KEY = hashString("MAX_LENDABLE_IMPACT_USD");
|
||||
export const MAX_LENDABLE_IMPACT_FACTOR_FOR_WITHDRAWALS_KEY = hashString("MAX_LENDABLE_IMPACT_FACTOR_FOR_WITHDRAWALS");
|
||||
export const LENT_POSITION_IMPACT_POOL_AMOUNT_KEY = hashString("LENT_POSITION_IMPACT_POOL_AMOUNT");
|
||||
|
||||
export const POSITION_IMPACT_POOL_AMOUNT_KEY = hashString("POSITION_IMPACT_POOL_AMOUNT");
|
||||
export const MIN_POSITION_IMPACT_POOL_AMOUNT_KEY = hashString("MIN_POSITION_IMPACT_POOL_AMOUNT");
|
||||
export const POSITION_IMPACT_POOL_DISTRIBUTION_RATE_KEY = hashString("POSITION_IMPACT_POOL_DISTRIBUTION_RATE");
|
||||
@@ -80,8 +89,10 @@ export const SUBACCOUNT_LIST_KEY = hashString("SUBACCOUNT_LIST");
|
||||
export const MAX_ALLOWED_SUBACCOUNT_ACTION_COUNT = hashString("MAX_ALLOWED_SUBACCOUNT_ACTION_COUNT");
|
||||
export const SUBACCOUNT_ACTION_COUNT = hashString("SUBACCOUNT_ACTION_COUNT");
|
||||
export const SUBACCOUNT_ORDER_ACTION = hashString("SUBACCOUNT_ORDER_ACTION");
|
||||
export const SUBACCOUNT_INTEGRATION_ID = hashString("SUBACCOUNT_INTEGRATION_ID");
|
||||
export const SUBACCOUNT_AUTO_TOP_UP_AMOUNT = hashString("SUBACCOUNT_AUTO_TOP_UP_AMOUNT");
|
||||
export const GLV_MAX_MARKET_TOKEN_BALANCE_USD = hashString("GLV_MAX_MARKET_TOKEN_BALANCE_USD");
|
||||
export const MIN_COLLATERAL_FACTOR_FOR_LIQUIDATION_KEY = hashString("MIN_COLLATERAL_FACTOR_FOR_LIQUIDATION");
|
||||
export const GLV_MAX_MARKET_TOKEN_BALANCE_AMOUNT = hashString("GLV_MAX_MARKET_TOKEN_BALANCE_AMOUNT");
|
||||
export const IS_GLV_MARKET_DISABLED = hashString("IS_GLV_MARKET_DISABLED");
|
||||
export const GLV_SHIFT_LAST_EXECUTED_AT = hashString("GLV_SHIFT_LAST_EXECUTED_AT");
|
||||
@@ -93,6 +104,23 @@ export const MAX_AUTO_CANCEL_ORDERS_KEY = hashString("MAX_AUTO_CANCEL_ORDERS");
|
||||
export const OPTIMAL_USAGE_FACTOR = hashString("OPTIMAL_USAGE_FACTOR");
|
||||
export const BASE_BORROWING_FACTOR = hashString("BASE_BORROWING_FACTOR");
|
||||
export const ABOVE_OPTIMAL_USAGE_BORROWING_FACTOR = hashString("ABOVE_OPTIMAL_USAGE_BORROWING_FACTOR");
|
||||
export const SUBACCOUNT_EXPIRES_AT = hashString("SUBACCOUNT_EXPIRES_AT");
|
||||
export const MULTICHAIN_BALANCE = hashString("MULTICHAIN_BALANCE");
|
||||
export const PRICE_FEED_KEY = hashString("PRICE_FEED");
|
||||
export const GASLESS_FEATURE_DISABLED_KEY = hashString("GASLESS_FEATURE_DISABLED");
|
||||
export const GELATO_RELAY_FEE_MULTIPLIER_FACTOR_KEY = hashString("GELATO_RELAY_FEE_MULTIPLIER_FACTOR");
|
||||
export const REQUEST_EXPIRATION_TIME_KEY = hashString("REQUEST_EXPIRATION_TIME");
|
||||
|
||||
export const GMX_SIMULATION_ORIGIN = "0x" + keccakString("GMX SIMULATION ORIGIN").slice(-40);
|
||||
export const CLAIM_TERMS_KEY = hashString("CLAIM_TERMS");
|
||||
export const GENERAL_CLAIM_FEATURE_DISABLED = hashString("GENERAL_CLAIM_FEATURE_DISABLED");
|
||||
|
||||
export function subaccountExpiresAtKey(account: string, subaccount: string, actionType: string) {
|
||||
return hashData(
|
||||
["bytes32", "address", "address", "bytes32"],
|
||||
[SUBACCOUNT_EXPIRES_AT, account, subaccount, actionType]
|
||||
);
|
||||
}
|
||||
|
||||
export function glvShiftLastExecutedAtKey(glvAddress: string) {
|
||||
return hashData(["bytes32", "address"], [GLV_SHIFT_LAST_EXECUTED_AT, glvAddress]);
|
||||
@@ -142,6 +170,10 @@ export function swapFeeFactorKey(market: string, forPositiveImpact: boolean) {
|
||||
return hashData(["bytes32", "address", "bool"], [SWAP_FEE_FACTOR_KEY, market, forPositiveImpact]);
|
||||
}
|
||||
|
||||
export function atomicSwapFeeFactorKey(market: string) {
|
||||
return hashData(["bytes32", "address"], [ATOMIC_SWAP_FEE_FACTOR_KEY, market]);
|
||||
}
|
||||
|
||||
export function openInterestKey(market: string, collateralToken: string, isLong: boolean) {
|
||||
return hashData(["bytes32", "address", "address", "bool"], [OPEN_INTEREST_KEY, market, collateralToken, isLong]);
|
||||
}
|
||||
@@ -250,7 +282,7 @@ export function depositGasLimitKey() {
|
||||
}
|
||||
|
||||
export function withdrawalGasLimitKey() {
|
||||
return hashData(["bytes32"], [WITHDRAWAL_GAS_LIMIT_KEY]);
|
||||
return WITHDRAWAL_GAS_LIMIT_KEY;
|
||||
}
|
||||
|
||||
export function shiftGasLimitKey() {
|
||||
@@ -281,6 +313,10 @@ export function minCollateralFactorKey(market: string) {
|
||||
return hashData(["bytes32", "address"], [MIN_COLLATERAL_FACTOR_KEY, market]);
|
||||
}
|
||||
|
||||
export function minCollateralFactorForLiquidationKey(market: string) {
|
||||
return hashData(["bytes32", "address"], [MIN_COLLATERAL_FACTOR_FOR_LIQUIDATION_KEY, market]);
|
||||
}
|
||||
|
||||
export function minCollateralFactorForOpenInterest(market: string, isLong: boolean) {
|
||||
return hashData(
|
||||
["bytes32", "address", "bool"],
|
||||
@@ -353,6 +389,42 @@ export function subaccountActionCountKey(account: string, subaccount: string, ac
|
||||
);
|
||||
}
|
||||
|
||||
export function maxLendableImpactFactorKey(market: string) {
|
||||
return hashData(["bytes32", "address"], [MAX_LENDABLE_IMPACT_FACTOR_KEY, market]);
|
||||
}
|
||||
|
||||
export function maxLendableImpactFactorForWithdrawalsKey(market: string) {
|
||||
return hashData(["bytes32", "address"], [MAX_LENDABLE_IMPACT_FACTOR_FOR_WITHDRAWALS_KEY, market]);
|
||||
}
|
||||
|
||||
export function maxLendableImpactUsdKey(market: string) {
|
||||
return hashData(["bytes32", "address"], [MAX_LENDABLE_IMPACT_USD_KEY, market]);
|
||||
}
|
||||
|
||||
export function subaccountIntegrationIdKey(account: string, subaccount: string) {
|
||||
return hashData(["bytes32", "address", "address"], [SUBACCOUNT_INTEGRATION_ID, account, subaccount]);
|
||||
}
|
||||
|
||||
export function subaccountAutoTopUpAmountKey(account: string, subaccount: string) {
|
||||
return hashData(["bytes32", "address", "address"], [SUBACCOUNT_AUTO_TOP_UP_AMOUNT, account, subaccount]);
|
||||
}
|
||||
|
||||
export function multichainBalanceKey(account: string, token: string) {
|
||||
return hashData(["bytes32", "address", "address"], [MULTICHAIN_BALANCE, account, token]);
|
||||
}
|
||||
|
||||
export function priceFeedKey(token: string) {
|
||||
return hashData(["bytes32", "address"], [PRICE_FEED_KEY, token]);
|
||||
}
|
||||
|
||||
export function gaslessFeatureDisabledKey(module: string) {
|
||||
return hashData(["bytes32", "address"], [GASLESS_FEATURE_DISABLED_KEY, module]);
|
||||
}
|
||||
|
||||
export function claimTermsKey(distributionId: bigint) {
|
||||
return hashData(["bytes32", "uint256"], [CLAIM_TERMS_KEY, distributionId]);
|
||||
}
|
||||
|
||||
export function claimsDisabledKey(distributionId: bigint) {
|
||||
return hashData(["bytes32", "uint256"], [GENERAL_CLAIM_FEATURE_DISABLED, distributionId]);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { zeroAddress } from "viem";
|
||||
import {zeroAddress} from "viem";
|
||||
|
||||
|
||||
import {ARBITRUM, AVALANCHE, AVALANCHE_FUJI} from "./chains.js";
|
||||
import {ARBITRUM, ARBITRUM_SEPOLIA, AVALANCHE, AVALANCHE_FUJI, BASE, BOTANIX} from "./chains.js";
|
||||
import {getContract} from "./contracts.js";
|
||||
import {Token, TokenCategory} from "../types/tokens.js";
|
||||
import {Token, TokenAddressTypesMap, TokenCategory} from "../types/tokens.js";
|
||||
|
||||
|
||||
export const NATIVE_TOKEN_ADDRESS = zeroAddress;
|
||||
|
||||
@@ -31,6 +32,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/2518/thumb/weth.png?1628852295",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/ethereum",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Wrapped Bitcoin",
|
||||
@@ -45,6 +48,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/wrapped-bitcoin",
|
||||
explorerUrl: "https://arbiscan.io/address/0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Arbitrum",
|
||||
@@ -56,6 +61,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
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",
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Wrapped SOL (Wormhole)",
|
||||
@@ -70,6 +77,9 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoSymbol: "SOL",
|
||||
explorerUrl: "https://arbiscan.io/token/0x2bCc6D6CdBbDC0a4071e48bb3B969b06B3330c07",
|
||||
explorerSymbol: "SOL",
|
||||
isPermitSupported: true,
|
||||
isPermitDisabled: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Chainlink",
|
||||
@@ -84,6 +94,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/chainlink",
|
||||
explorerUrl: "https://arbiscan.io/token/0xf97f4df75117a78c1a5a0dbb814af92458539fb4",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Uniswap",
|
||||
@@ -98,6 +110,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/uniswap",
|
||||
explorerUrl: "https://arbiscan.io/token/0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Bridged USDC (USDC.e)",
|
||||
@@ -109,6 +123,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/bridged-usdc-arbitrum",
|
||||
explorerUrl: "https://arbiscan.io/token/0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "USD Coin",
|
||||
@@ -120,6 +136,7 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
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",
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "Tether",
|
||||
@@ -131,6 +148,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
explorerUrl: "https://arbiscan.io/address/0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/tether",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Dai",
|
||||
@@ -142,6 +161,7 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/dai",
|
||||
explorerUrl: "https://arbiscan.io/token/0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "Frax",
|
||||
@@ -153,6 +173,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/frax",
|
||||
explorerUrl: "https://arbiscan.io/token/0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Magic Internet Money",
|
||||
@@ -163,6 +185,7 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
isTempHidden: true,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/16786/small/mimlogopng.png",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "Bitcoin",
|
||||
@@ -173,6 +196,7 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/bitcoin",
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "Dogecoin",
|
||||
@@ -277,6 +301,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/12645/standard/AAVE.png?1696512452",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/aave",
|
||||
coingeckoSymbol: "AAVE",
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Wrapped AVAX (Wormhole)",
|
||||
@@ -290,6 +316,9 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/avalanche",
|
||||
coingeckoSymbol: "AVAX",
|
||||
explorerSymbol: "WAVAX",
|
||||
isPermitSupported: true,
|
||||
isPermitDisabled: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Optimism",
|
||||
@@ -321,6 +350,9 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
categories: ["meme"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/33566/standard/dogwifhat.jpg?1702499428",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/dogwifhat",
|
||||
isPermitSupported: true,
|
||||
isPermitDisabled: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "ORDI",
|
||||
@@ -381,6 +413,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
imageUrl:
|
||||
"https://assets.coingecko.com/coins/images/11224/standard/0x18084fba666a33d37592fa2633fd49a74dd93a88.png?1696511155",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/tbtc",
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Eigen",
|
||||
@@ -390,6 +424,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
categories: ["layer2"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/37441/standard/eigen.jpg?1728023974",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/eigenlayer",
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Sats",
|
||||
@@ -577,6 +613,9 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/15069/standard/Pendle_Logo_Normal-03.png?1696514728",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/pendle",
|
||||
isPermitSupported: true,
|
||||
isPermitDisabled: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "ADA",
|
||||
@@ -883,6 +922,101 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/mantra",
|
||||
isSynthetic: true,
|
||||
},
|
||||
{
|
||||
name: "Dolomite",
|
||||
symbol: "DOLO",
|
||||
address: "0x97Ce1F309B949f7FBC4f58c5cb6aa417A5ff8964",
|
||||
decimals: 18,
|
||||
priceDecimals: 6,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/54710/standard/DOLO-small.png?1745398535",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/dolomite",
|
||||
isSynthetic: true,
|
||||
},
|
||||
{
|
||||
name: "LayerZero",
|
||||
symbol: "ZRO",
|
||||
address: "0xa8193C55C34Ed22e1Dbe73FD5Adc668E51578a67",
|
||||
decimals: 18,
|
||||
priceDecimals: 4,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/28206/standard/ftxG9_TJ_400x400.jpeg?1696527208",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/layerzero",
|
||||
isSynthetic: true,
|
||||
},
|
||||
{
|
||||
name: "Moodeng",
|
||||
symbol: "MOODENG",
|
||||
address: "0xd3898c6570974AEca38a8ACf22fd60739e528A99",
|
||||
decimals: 6,
|
||||
isSynthetic: true,
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/moo-deng",
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/50264/standard/MOODENG.jpg?1726726975",
|
||||
categories: ["meme"],
|
||||
},
|
||||
{
|
||||
name: "Monero",
|
||||
symbol: "XMR",
|
||||
address: "0x13674172E6E44D31d4bE489d5184f3457c40153A",
|
||||
decimals: 12,
|
||||
isSynthetic: true,
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/monero",
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/69/standard/monero_logo.png?1696501460",
|
||||
categories: ["layer1", "defi"],
|
||||
},
|
||||
{
|
||||
name: "Pi Network",
|
||||
symbol: "PI",
|
||||
address: "0xd1738d37401a0A71f7E382d2cFeCD3ab69687017",
|
||||
decimals: 18,
|
||||
isSynthetic: true,
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/pi-network",
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/54342/standard/pi_network.jpg?1739347576",
|
||||
categories: ["layer1"],
|
||||
},
|
||||
{
|
||||
name: "Curve DAO Token",
|
||||
symbol: "CRV",
|
||||
address: "0xe5f01aeAcc8288E9838A60016AB00d7b6675900b",
|
||||
decimals: 18,
|
||||
isSynthetic: true,
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/curve-dao-token",
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/12124/standard/Curve.png?1696511967",
|
||||
categories: ["defi"],
|
||||
},
|
||||
{
|
||||
name: "Pump",
|
||||
symbol: "PUMP",
|
||||
address: "0x9c060B2fA953b5f69879a8B7B81f62BFfEF360be",
|
||||
decimals: 18,
|
||||
priceDecimals: 6,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/67164/standard/pump.jpg?1751949376",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/pump-fun",
|
||||
isSynthetic: true,
|
||||
categories: ["meme"],
|
||||
},
|
||||
{
|
||||
name: "SPX6900",
|
||||
symbol: "SPX6900",
|
||||
address: "0xb736be525A65326513351058427d1f47B0CfB045",
|
||||
decimals: 8,
|
||||
priceDecimals: 4,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/31401/standard/centeredcoin_%281%29.png?1737048493",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/spx6900",
|
||||
isSynthetic: true,
|
||||
categories: ["meme"],
|
||||
},
|
||||
{
|
||||
name: "Mantle",
|
||||
symbol: "MNT",
|
||||
address: "0x955cd91eEaE618F5a7b49E1e3c7482833B10DAb4",
|
||||
decimals: 18,
|
||||
priceDecimals: 5,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/30980/standard/token-logo.png?1696529819",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/mantle",
|
||||
isSynthetic: true,
|
||||
categories: ["layer2", "defi"],
|
||||
},
|
||||
{
|
||||
name: "GMX LP",
|
||||
symbol: "GLP",
|
||||
@@ -909,6 +1043,138 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
imageUrl: "https://raw.githubusercontent.com/gmx-io/gmx-assets/main/GMX-Assets/PNG/GLV_LOGO.png",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "Algorand",
|
||||
symbol: "ALGO",
|
||||
address: "0x72Cd3a21aA7A898028d9501868Fbe6dED0020434",
|
||||
decimals: 6,
|
||||
priceDecimals: 5,
|
||||
isSynthetic: true,
|
||||
categories: ["layer1", "defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/4380/standard/download.png?1696504978",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/algorand",
|
||||
},
|
||||
{
|
||||
name: "Cronos",
|
||||
symbol: "CRO",
|
||||
address: "0xB7EfE7c7f059E84Ab87A83A169c583Fb4A54fAc3",
|
||||
decimals: 8,
|
||||
priceDecimals: 5,
|
||||
isSynthetic: true,
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/7310/standard/cro_token_logo.png?1696507599",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/cronos",
|
||||
},
|
||||
{
|
||||
name: "Hedera",
|
||||
symbol: "HBAR",
|
||||
address: "0xEb2A83b973f4dbB9511D92dd40d2ba4C683f0971",
|
||||
decimals: 8,
|
||||
priceDecimals: 5,
|
||||
isSynthetic: true,
|
||||
categories: ["layer1", "defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/3688/standard/hbar.png?1696504364",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/hedera-hashgraph",
|
||||
},
|
||||
{
|
||||
name: "Convex Finance",
|
||||
symbol: "CVX",
|
||||
address: "0x3B6f801C0052Dfe0Ac80287D611F31B7c47B9A6b",
|
||||
decimals: 18,
|
||||
priceDecimals: 4,
|
||||
isSynthetic: true,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/15585/standard/convex.png?1696515221",
|
||||
coingeckoUrl: "https://www.coingecko.com/nl/coins/convex-finance",
|
||||
},
|
||||
{
|
||||
name: "Kaspa",
|
||||
symbol: "KAS",
|
||||
address: "0x91c6a8F6aFAC036F4ABf1bA55f4E76892E865E4a",
|
||||
decimals: 8,
|
||||
priceDecimals: 6,
|
||||
isSynthetic: true,
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/25751/standard/kaspa-icon-exchanges.png?1696524837",
|
||||
coingeckoUrl: "https://www.coingecko.com/nl/coins/kaspa",
|
||||
},
|
||||
{
|
||||
name: "Aero",
|
||||
symbol: "AERO",
|
||||
address: "0xEcc5eb985Ddbb8335b175b0A2A1144E4c978F1f6",
|
||||
decimals: 18,
|
||||
priceDecimals: 4,
|
||||
isSynthetic: true,
|
||||
categories: ["defi"],
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/aerodrome-finance",
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/31745/standard/token.png?1696530564",
|
||||
},
|
||||
{
|
||||
name: "Brett",
|
||||
symbol: "BRETT",
|
||||
address: "0x4249F6e0808bEfF7368AaAD3F7A3Fd511F61Ee60",
|
||||
decimals: 18,
|
||||
priceDecimals: 4,
|
||||
isSynthetic: true,
|
||||
categories: ["meme"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/54317/standard/AERO.png?1728309870",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/brett-2",
|
||||
},
|
||||
{
|
||||
name: "World Liberty Financial",
|
||||
symbol: "WLFI",
|
||||
address: "0xC5799ab6E2818fD8d0788dB8D156B0c5db1Bf97b",
|
||||
decimals: 18,
|
||||
priceDecimals: 5,
|
||||
isSynthetic: true,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/50767/standard/wlfi.png?1756438915",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/world-liberty-financial",
|
||||
},
|
||||
{
|
||||
name: "OKB",
|
||||
symbol: "OKB",
|
||||
address: "0xd37F01A3379f052FEF70F63c0Be27931891aa2B9",
|
||||
decimals: 18,
|
||||
priceDecimals: 3,
|
||||
isSynthetic: true,
|
||||
categories: ["layer2"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/4463/standard/WeChat_Image_20220118095654.png?1696505053",
|
||||
coingeckoUrl: "https://www.coingecko.com/nl/coins/okb",
|
||||
},
|
||||
{
|
||||
name: "Morpho",
|
||||
symbol: "MORPHO",
|
||||
address: "0xF67b2a901D674B443Fa9f6DB2A689B37c07fD4fE",
|
||||
decimals: 18,
|
||||
priceDecimals: 4,
|
||||
isSynthetic: true,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/29837/standard/Morpho-token-icon.png?1726771230",
|
||||
coingeckoUrl: "https://www.coingecko.com/nl/coins/morpho",
|
||||
},
|
||||
{
|
||||
name: "Venice Token",
|
||||
symbol: "VVV",
|
||||
address: "0xB79Eb5BA64A167676694bB41bc1640F95d309a2F",
|
||||
decimals: 18,
|
||||
priceDecimals: 4,
|
||||
isSynthetic: true,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/54023/standard/VVV_Token_Transparent.png?1741856877",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/venice-token",
|
||||
},
|
||||
{
|
||||
name: "Moonwell",
|
||||
symbol: "WELL",
|
||||
address: "0x465A31E5bA29b8EAcC860d499D714a6f07e56E85",
|
||||
decimals: 18,
|
||||
priceDecimals: 4,
|
||||
isSynthetic: true,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/26133/standard/WELL.png?1696525221",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/moonwell",
|
||||
},
|
||||
],
|
||||
[AVALANCHE]: [
|
||||
{
|
||||
@@ -987,6 +1253,7 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/usd-coin",
|
||||
explorerUrl: "https://snowtrace.io/address/0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "Bridged USDC (USDC.e)",
|
||||
@@ -1008,6 +1275,8 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/325/small/Tether-logo.png",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/tether",
|
||||
explorerUrl: "https://snowtrace.io/address/0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
|
||||
isPermitSupported: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "Tether",
|
||||
@@ -1040,6 +1309,7 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/magic-internet-money",
|
||||
explorerUrl: "https://snowtrace.io/address/0x130966628846BFd36ff31a822705796e8cb8C18D",
|
||||
isV1Available: true,
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "Chainlink",
|
||||
@@ -1088,6 +1358,9 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/solana",
|
||||
coingeckoSymbol: "SOL",
|
||||
explorerUrl: "https://snowtrace.io/address/0xFE6B19286885a4F7F55AdAD09C3Cd1f906D2478F",
|
||||
isPermitSupported: true,
|
||||
isPermitDisabled: true,
|
||||
contractVersion: "1",
|
||||
},
|
||||
{
|
||||
name: "XRP",
|
||||
@@ -1133,6 +1406,28 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/melania-meme",
|
||||
isSynthetic: true,
|
||||
},
|
||||
{
|
||||
name: "Pump",
|
||||
symbol: "PUMP",
|
||||
address: "0xdA598795DfE56388ca3D35e2ccFA96EFf83eC306",
|
||||
decimals: 18,
|
||||
priceDecimals: 6,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/67164/standard/pump.jpg?1751949376",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/pump-fun",
|
||||
isSynthetic: true,
|
||||
categories: ["meme"],
|
||||
},
|
||||
{
|
||||
name: "World Liberty Financial",
|
||||
symbol: "WLFI",
|
||||
address: "0xbDF8a77ACB7A54597E7760b34D3E632912bB59b7",
|
||||
decimals: 18,
|
||||
priceDecimals: 5,
|
||||
isSynthetic: true,
|
||||
categories: ["defi"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/50767/standard/wlfi.png?1756438915",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/world-liberty-financial",
|
||||
},
|
||||
{
|
||||
name: "Escrowed GMX",
|
||||
symbol: "ESGMX",
|
||||
@@ -1192,6 +1487,7 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/12559/small/coin-round-red.png?1604021818",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/avalanche",
|
||||
explorerUrl: "https://testnet.snowtrace.io/address/0x1D308089a2D1Ced3f1Ce36B1FcaF815b07217be3",
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "Ethereum (WETH.e)",
|
||||
@@ -1397,12 +1693,234 @@ export const TOKENS: { [chainId: number]: Token[] } = {
|
||||
isPlatformToken: true,
|
||||
},
|
||||
],
|
||||
[ARBITRUM_SEPOLIA]: [
|
||||
{
|
||||
name: "Ethereum",
|
||||
symbol: "ETH",
|
||||
decimals: 18,
|
||||
address: zeroAddress,
|
||||
wrappedAddress: "0x980B62Da83eFf3D4576C647993b0c1D7faf17c73",
|
||||
isNative: true,
|
||||
isShortable: true,
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/ethereum",
|
||||
},
|
||||
{
|
||||
name: "Wrapped ETH",
|
||||
symbol: "WETH",
|
||||
address: "0x980B62Da83eFf3D4576C647993b0c1D7faf17c73",
|
||||
decimals: 18,
|
||||
isWrapped: true,
|
||||
baseSymbol: "ETH",
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/279/small/ethereum.png?1595348880",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/ethereum",
|
||||
},
|
||||
{
|
||||
name: "Bitcoin",
|
||||
symbol: "BTC",
|
||||
address: "0xF79cE1Cf38A09D572b021B4C5548b75A14082F12",
|
||||
decimals: 8,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1746042828",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/bitcoin",
|
||||
},
|
||||
{
|
||||
name: "USD Coin GMX",
|
||||
symbol: "USDC",
|
||||
address: "0x3321Fd36aEaB0d5CdfD26f4A3A93E2D2aAcCB99f",
|
||||
decimals: 6,
|
||||
isStable: true,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/usd-coin",
|
||||
},
|
||||
{
|
||||
name: "USD Coin Stargate",
|
||||
symbol: "USDC.SG",
|
||||
address: "0x3253a335E7bFfB4790Aa4C25C4250d206E9b9773",
|
||||
decimals: 6,
|
||||
isStable: true,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/usd-coin",
|
||||
},
|
||||
{
|
||||
name: "CRV",
|
||||
symbol: "CRV",
|
||||
address: "0xD5DdAED48B09fa1D7944bd662CB05265FCD7077C",
|
||||
decimals: 18,
|
||||
priceDecimals: 5,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/12134/small/curve.png?1596358786",
|
||||
isSynthetic: true,
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/curve-dao-token",
|
||||
},
|
||||
/** Placeholder tokens */
|
||||
{
|
||||
name: "GMX",
|
||||
symbol: "GMX",
|
||||
address: "<gmx-address>",
|
||||
decimals: 18,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/18323/small/arbit.png?1631532468",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "Escrowed GMX",
|
||||
symbol: "ESGMX",
|
||||
address: "<esgmx-address>",
|
||||
decimals: 18,
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "GMX LP",
|
||||
symbol: "GLP",
|
||||
address: "<glp-address>",
|
||||
decimals: 18,
|
||||
imageUrl: "https://github.com/gmx-io/gmx-assets/blob/main/GMX-Assets/PNG/GLP_LOGO%20ONLY.png?raw=true",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "GMX Market tokens",
|
||||
symbol: "GM",
|
||||
address: "<market-token-address>",
|
||||
decimals: 18,
|
||||
imageUrl: "https://raw.githubusercontent.com/gmx-io/gmx-assets/main/GMX-Assets/PNG/GM_LOGO.png",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "GLV Market tokens",
|
||||
symbol: "GLV",
|
||||
address: "<market-token-address>",
|
||||
decimals: 18,
|
||||
imageUrl: "https://raw.githubusercontent.com/gmx-io/gmx-assets/main/GMX-Assets/PNG/GLV_LOGO.png",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
],
|
||||
[BOTANIX]: [
|
||||
{
|
||||
name: "Bitcoin",
|
||||
symbol: "BTC",
|
||||
assetSymbol: "BTC",
|
||||
address: NATIVE_TOKEN_ADDRESS,
|
||||
decimals: 18,
|
||||
isNative: true,
|
||||
isShortable: true,
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/1/standard/bitcoin.png?1696501400",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/bitcoin",
|
||||
baseSymbol: "BTC",
|
||||
},
|
||||
{
|
||||
name: "Pegged BTC",
|
||||
symbol: "PBTC",
|
||||
assetSymbol: "pBTC",
|
||||
address: "0x0D2437F93Fed6EA64Ef01cCde385FB1263910C56",
|
||||
decimals: 18,
|
||||
isShortable: true,
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/1/standard/bitcoin.png?1696501400",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/bitcoin",
|
||||
baseSymbol: "BTC",
|
||||
isWrapped: true,
|
||||
},
|
||||
{
|
||||
name: "Staked BTC",
|
||||
symbol: "STBTC",
|
||||
assetSymbol: "stBTC",
|
||||
address: "0xF4586028FFdA7Eca636864F80f8a3f2589E33795",
|
||||
decimals: 18,
|
||||
isShortable: true,
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/1/standard/bitcoin.png?1696501400",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/bitcoin",
|
||||
baseSymbol: "BTC",
|
||||
isStaking: true,
|
||||
},
|
||||
{
|
||||
name: "BTC",
|
||||
symbol: "BTC",
|
||||
address: "0x1B9e25f54225bcdCf347569E38C41Ade9BB686e5",
|
||||
decimals: 8,
|
||||
isShortable: true,
|
||||
categories: ["layer1"],
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/1/standard/bitcoin.png?1696501400",
|
||||
coingeckoUrl: "https://www.coingecko.com/en/coins/bitcoin",
|
||||
isSynthetic: true,
|
||||
},
|
||||
{
|
||||
name: "USDC.E",
|
||||
symbol: "USDC.E",
|
||||
assetSymbol: "USDC.e",
|
||||
address: "0x29eE6138DD4C9815f46D34a4A1ed48F46758A402",
|
||||
decimals: 6,
|
||||
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",
|
||||
isPermitSupported: true,
|
||||
},
|
||||
{
|
||||
name: "GMX",
|
||||
symbol: "GMX",
|
||||
address: "",
|
||||
decimals: 18,
|
||||
imageUrl: "https://assets.coingecko.com/coins/images/18323/small/arbit.png?1631532468",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "Escrowed GMX",
|
||||
symbol: "ESGMX",
|
||||
address: "",
|
||||
decimals: 18,
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "GMX LP",
|
||||
symbol: "GLP",
|
||||
address: "",
|
||||
decimals: 18,
|
||||
imageUrl: "https://github.com/gmx-io/gmx-assets/blob/main/GMX-Assets/PNG/GLP_LOGO%20ONLY.png?raw=true",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
/** Placeholder tokens */
|
||||
{
|
||||
name: "GMX Market tokens",
|
||||
symbol: "GM",
|
||||
address: "<market-token-address>",
|
||||
decimals: 18,
|
||||
imageUrl: "https://raw.githubusercontent.com/gmx-io/gmx-assets/main/GMX-Assets/PNG/GM_LOGO.png",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
{
|
||||
name: "GLV Market tokens",
|
||||
symbol: "GLV",
|
||||
address: "<market-token-address>",
|
||||
decimals: 18,
|
||||
imageUrl: "https://raw.githubusercontent.com/gmx-io/gmx-assets/main/GMX-Assets/PNG/GLV_LOGO.png",
|
||||
isPlatformToken: true,
|
||||
},
|
||||
],
|
||||
[BASE]: [
|
||||
{
|
||||
name: "Ethereum",
|
||||
symbol: "ETH",
|
||||
address: "0x0000000000000000000000000000000000000000",
|
||||
decimals: 18,
|
||||
},
|
||||
//usdc
|
||||
{
|
||||
name: "USD Coin",
|
||||
symbol: "USDC",
|
||||
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
||||
decimals: 6,
|
||||
isStable: true,
|
||||
},
|
||||
],
|
||||
|
||||
};
|
||||
|
||||
export const TOKEN_COLOR_MAP = {
|
||||
ETH: "#6062a6",
|
||||
BTC: "#F7931A",
|
||||
WBTC: "#F7931A",
|
||||
PBTC: "#F7931A",
|
||||
USDC: "#2775CA",
|
||||
"USDC.E": "#2A5ADA",
|
||||
USDT: "#67B18A",
|
||||
@@ -1441,7 +1959,7 @@ export const TOKENS_BY_SYMBOL_MAP: { [chainId: number]: { [symbol: string]: Toke
|
||||
export const WRAPPED_TOKENS_MAP: { [chainId: number]: Token } = {};
|
||||
export const NATIVE_TOKENS_MAP: { [chainId: number]: Token } = {};
|
||||
|
||||
const CHAIN_IDS = [ARBITRUM, AVALANCHE, AVALANCHE_FUJI];
|
||||
const CHAIN_IDS = [ARBITRUM, AVALANCHE, AVALANCHE_FUJI, BOTANIX, ARBITRUM_SEPOLIA];
|
||||
|
||||
for (let j = 0; j < CHAIN_IDS.length; j++) {
|
||||
const chainId = CHAIN_IDS[j];
|
||||
@@ -1528,7 +2046,16 @@ export function isValidToken(chainId: number, address: string) {
|
||||
return address in TOKENS_MAP[chainId];
|
||||
}
|
||||
|
||||
export function isValidTokenSafe(chainId: number, address: string) {
|
||||
return address in TOKENS_MAP[chainId];
|
||||
}
|
||||
|
||||
export function getToken(chainId: number, address: string) {
|
||||
// FIXME APE_deprecated token which is not in use but can be displayed
|
||||
if (chainId === ARBITRUM && address === "0x74885b4D524d497261259B38900f54e6dbAd2210") {
|
||||
return getTokenBySymbol(chainId, "APE");
|
||||
}
|
||||
|
||||
if (!TOKENS_MAP[chainId]) {
|
||||
throw new Error(`Incorrect chainId ${chainId}`);
|
||||
}
|
||||
@@ -1575,23 +2102,29 @@ export function getTokenBySymbol(
|
||||
return token;
|
||||
}
|
||||
|
||||
export function convertTokenAddress(chainId: number, address: string, convertTo?: "wrapped" | "native") {
|
||||
export function convertTokenAddress<T extends keyof TokenAddressTypesMap, R extends TokenAddressTypesMap[T]>(
|
||||
chainId: number,
|
||||
address: string,
|
||||
convertTo?: T
|
||||
): R {
|
||||
const wrappedToken = getWrappedToken(chainId);
|
||||
|
||||
if (convertTo === "wrapped" && address === NATIVE_TOKEN_ADDRESS) {
|
||||
return wrappedToken.address;
|
||||
return wrappedToken.address as R;
|
||||
}
|
||||
|
||||
if (convertTo === "native" && address === wrappedToken.address) {
|
||||
return NATIVE_TOKEN_ADDRESS;
|
||||
return NATIVE_TOKEN_ADDRESS as R;
|
||||
}
|
||||
|
||||
return address;
|
||||
return address as R;
|
||||
}
|
||||
|
||||
export function getNormalizedTokenSymbol(tokenSymbol) {
|
||||
export function getNormalizedTokenSymbol(tokenSymbol: string) {
|
||||
if (["WBTC", "WETH", "WAVAX"].includes(tokenSymbol)) {
|
||||
return tokenSymbol.substr(1);
|
||||
} else if (["PBTC", "STBTC"].includes(tokenSymbol)) {
|
||||
return "BTC";
|
||||
} else if (tokenSymbol.includes(".")) {
|
||||
return tokenSymbol.split(".")[0];
|
||||
}
|
||||
@@ -1664,8 +2197,11 @@ export function getCategoryTokenAddresses(chainId: number, category: TokenCatego
|
||||
}
|
||||
|
||||
export const createTokensMap = (tokens: Token[]) => {
|
||||
return tokens.reduce((acc, token) => {
|
||||
acc[token.address] = token;
|
||||
return acc;
|
||||
}, {});
|
||||
return tokens.reduce(
|
||||
(acc, token) => {
|
||||
acc[token.address] = token;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, Token>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {Abi, Address, createPublicClient, createWalletClient, http, PublicClient, WalletClient} from "viem";
|
||||
import {Abi, Address, createPublicClient, http, PublicClient, WalletClient} from "viem";
|
||||
|
||||
import {Accounts} from "./modules/accounts/accounts.js";
|
||||
import {BATCH_CONFIGS} from "./configs/batch.js";
|
||||
import {getChain} from "./configs/chains.js";
|
||||
import {Markets} from "./modules/markets/index.js";
|
||||
import {Oracle} from "./modules/oracle.js";
|
||||
import {Orders} from "./modules/orders/orders.js";
|
||||
@@ -13,6 +12,7 @@ import {Utils} from "./modules/utils/utils.js";
|
||||
import {GmxSdkConfig} from "./types/sdk.js";
|
||||
import {callContract, CallContractOpts} from "./utils/callContract.js";
|
||||
import {MAX_TIMEOUT, Multicall, MulticallRequestConfig} from "./utils/multicall.js";
|
||||
import {getViemChain} from "./configs/chains.js";
|
||||
|
||||
export class GmxSdk {
|
||||
public readonly markets = new Markets(this);
|
||||
@@ -43,23 +43,13 @@ export class GmxSdk {
|
||||
}),
|
||||
pollingInterval: undefined,
|
||||
batch: BATCH_CONFIGS[this.config.chainId].client,
|
||||
chain: getChain(this.config.chainId),
|
||||
chain: getViemChain(this.config.chainId),
|
||||
};
|
||||
this.publicClient = createPublicClient(clientParams) as any;
|
||||
}
|
||||
|
||||
this.walletClient =
|
||||
config.walletClient ??
|
||||
createWalletClient({
|
||||
account: config.account as Address,
|
||||
chain: getChain(config.chainId),
|
||||
transport: http(config.rpcUrl, {
|
||||
retryCount: 0,
|
||||
retryDelay: 10000000,
|
||||
batch: BATCH_CONFIGS[config.chainId].http,
|
||||
timeout: MAX_TIMEOUT,
|
||||
}),
|
||||
});
|
||||
config.walletClient;
|
||||
}
|
||||
|
||||
setAccount(account: Address) {
|
||||
@@ -80,7 +70,7 @@ export class GmxSdk {
|
||||
}
|
||||
|
||||
get chain() {
|
||||
return getChain(this.chainId);
|
||||
return getViemChain(this.chainId);
|
||||
}
|
||||
|
||||
get account() {
|
||||
|
||||
@@ -51,8 +51,8 @@ export class Markets extends Module {
|
||||
const marketDivisor = getMarketDivisor(market);
|
||||
|
||||
claimableFundingData[marketAddress] = {
|
||||
claimableFundingAmountLong: callsResult.claimableFundingAmountLong.returnValues[0] / BigInt(marketDivisor),
|
||||
claimableFundingAmountShort: callsResult.claimableFundingAmountShort.returnValues[0] / BigInt(marketDivisor),
|
||||
claimableFundingAmountLong: callsResult.claimableFundingAmountLong.returnValues[0] / marketDivisor,
|
||||
claimableFundingAmountShort: callsResult.claimableFundingAmountShort.returnValues[0] / marketDivisor,
|
||||
};
|
||||
|
||||
return claimableFundingData;
|
||||
@@ -71,7 +71,9 @@ export class Markets extends Module {
|
||||
marketsAddresses: string[] | undefined;
|
||||
marketsData: MarketsData | undefined;
|
||||
tokensData: TokensData | undefined;
|
||||
}): Promise<MarketsResult> {
|
||||
}): Promise<{
|
||||
[marketAddress: string]: MarketValues;
|
||||
}> {
|
||||
const dataStoreAddress = getContract(this.chainId, "DataStore");
|
||||
const syntheticsReaderAddress = getContract(this.chainId, "SyntheticsReader");
|
||||
|
||||
@@ -248,8 +250,16 @@ export class Markets extends Module {
|
||||
minCollateralFactorForOpenInterestShort:
|
||||
dataStoreValues.minCollateralFactorForOpenInterestShort.returnValues[0],
|
||||
|
||||
positionFeeFactorForPositiveImpact: dataStoreValues.positionFeeFactorForPositiveImpact.returnValues[0],
|
||||
positionFeeFactorForNegativeImpact: dataStoreValues.positionFeeFactorForNegativeImpact.returnValues[0],
|
||||
minCollateralFactorForLiquidation: dataStoreValues.minCollateralFactorForLiquidation.returnValues[0],
|
||||
|
||||
positionFeeFactorForBalanceWasImproved:
|
||||
dataStoreValues.positionFeeFactorForBalanceWasImproved.returnValues[0],
|
||||
positionFeeFactorForBalanceWasNotImproved:
|
||||
dataStoreValues.positionFeeFactorForBalanceWasNotImproved.returnValues[0],
|
||||
positionFeeFactorForPositiveImpact:
|
||||
dataStoreValues.positionFeeFactorForPositiveImpact.returnValues[0],
|
||||
positionFeeFactorForNegativeImpact:
|
||||
dataStoreValues.positionFeeFactorForNegativeImpact.returnValues[0],
|
||||
positionImpactFactorPositive: dataStoreValues.positionImpactFactorPositive.returnValues[0],
|
||||
positionImpactFactorNegative: dataStoreValues.positionImpactFactorNegative.returnValues[0],
|
||||
maxPositionImpactFactorPositive: dataStoreValues.maxPositionImpactFactorPositive.returnValues[0],
|
||||
@@ -257,11 +267,15 @@ export class Markets extends Module {
|
||||
maxPositionImpactFactorForLiquidations:
|
||||
dataStoreValues.maxPositionImpactFactorForLiquidations.returnValues[0],
|
||||
positionImpactExponentFactor: dataStoreValues.positionImpactExponentFactor.returnValues[0],
|
||||
swapFeeFactorForBalanceWasImproved: dataStoreValues.swapFeeFactorForBalanceWasImproved.returnValues[0],
|
||||
swapFeeFactorForBalanceWasNotImproved:
|
||||
dataStoreValues.swapFeeFactorForBalanceWasNotImproved.returnValues[0],
|
||||
swapFeeFactorForPositiveImpact: dataStoreValues.swapFeeFactorForPositiveImpact.returnValues[0],
|
||||
swapFeeFactorForNegativeImpact: dataStoreValues.swapFeeFactorForNegativeImpact.returnValues[0],
|
||||
swapImpactFactorPositive: dataStoreValues.swapImpactFactorPositive.returnValues[0],
|
||||
swapImpactFactorNegative: dataStoreValues.swapImpactFactorNegative.returnValues[0],
|
||||
swapImpactExponentFactor: dataStoreValues.swapImpactExponentFactor.returnValues[0],
|
||||
atomicSwapFeeFactor: dataStoreValues.atomicSwapFeeFactor.returnValues[0],
|
||||
|
||||
virtualMarketId: dataStoreValues.virtualMarketId.returnValues[0],
|
||||
virtualLongTokenId: dataStoreValues.virtualLongTokenId.returnValues[0],
|
||||
@@ -334,7 +348,15 @@ export class Markets extends Module {
|
||||
const chainId = this.chainId;
|
||||
|
||||
const marketsResult = markets.reduce(
|
||||
(acc: MarketsResult, market) => {
|
||||
(
|
||||
acc: MarketsResult,
|
||||
market: {
|
||||
marketTokenAddress: string;
|
||||
indexTokenAddress: string;
|
||||
longTokenAddress: string;
|
||||
shortTokenAddress: string;
|
||||
}
|
||||
) => {
|
||||
try {
|
||||
if (!marketsMap[market.marketTokenAddress]?.isListed) {
|
||||
return acc;
|
||||
@@ -373,6 +395,7 @@ export class Markets extends Module {
|
||||
|
||||
async getMarketsInfo(): Promise<MarketsInfoResult> {
|
||||
const { marketsData, marketsAddresses } = await this.getMarkets();
|
||||
|
||||
const { tokensData, pricesUpdatedAt } = await this.sdk.tokens.getTokensData();
|
||||
|
||||
const [marketsValues, marketsConfigs, claimableFundingData] = await Promise.all([
|
||||
|
||||
@@ -8,7 +8,8 @@ import {getContractMarketPrices} from "../../utils/markets.js";
|
||||
import {getByKey} from "../../utils/objects.js";
|
||||
import {MarketConfigMulticallRequestConfig, MarketValuesMulticallRequestConfig} from "./types.js";
|
||||
import {getContract} from "../../configs/contracts.js";
|
||||
import { ContractCallsConfig } from "../../utils/multicall.js";
|
||||
import {ContractCallsConfig} from "../../utils/multicall.js";
|
||||
import {ContractsChainId} from "../../configs/chains.js";
|
||||
|
||||
export function buildClaimableFundingDataRequest({
|
||||
marketsAddresses,
|
||||
@@ -19,47 +20,50 @@ export function buildClaimableFundingDataRequest({
|
||||
marketsAddresses: string[] | undefined;
|
||||
marketsData: MarketsData | undefined;
|
||||
account: string;
|
||||
chainId: number;
|
||||
chainId: ContractsChainId;
|
||||
}) {
|
||||
if (!marketsAddresses) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return marketsAddresses.reduce((request, marketAddress) => {
|
||||
const market = getByKey(marketsData, marketAddress);
|
||||
return marketsAddresses.reduce(
|
||||
(request, marketAddress) => {
|
||||
const market = getByKey(marketsData, marketAddress);
|
||||
|
||||
if (!market) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const keys = hashDataMap({
|
||||
claimableFundingAmountLong: [
|
||||
["bytes32", "address", "address", "address"],
|
||||
[CLAIMABLE_FUNDING_AMOUNT, marketAddress, market.longTokenAddress, account],
|
||||
],
|
||||
claimableFundingAmountShort: [
|
||||
["bytes32", "address", "address", "address"],
|
||||
[CLAIMABLE_FUNDING_AMOUNT, marketAddress, market.shortTokenAddress, account],
|
||||
],
|
||||
});
|
||||
|
||||
request[marketAddress] = {
|
||||
contractAddress: getContract(chainId, "DataStore"),
|
||||
abiId: "DataStore",
|
||||
calls: {
|
||||
claimableFundingAmountLong: {
|
||||
methodName: "getUint",
|
||||
params: [keys.claimableFundingAmountLong],
|
||||
},
|
||||
claimableFundingAmountShort: {
|
||||
methodName: "getUint",
|
||||
params: [keys.claimableFundingAmountShort],
|
||||
},
|
||||
},
|
||||
} satisfies ContractCallsConfig<any>;
|
||||
|
||||
if (!market) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const keys = hashDataMap({
|
||||
claimableFundingAmountLong: [
|
||||
["bytes32", "address", "address", "address"],
|
||||
[CLAIMABLE_FUNDING_AMOUNT, marketAddress, market.longTokenAddress, account],
|
||||
],
|
||||
claimableFundingAmountShort: [
|
||||
["bytes32", "address", "address", "address"],
|
||||
[CLAIMABLE_FUNDING_AMOUNT, marketAddress, market.shortTokenAddress, account],
|
||||
],
|
||||
});
|
||||
|
||||
request[marketAddress] = {
|
||||
contractAddress: getContract(chainId, "DataStore"),
|
||||
abiId: "DataStore",
|
||||
calls: {
|
||||
claimableFundingAmountLong: {
|
||||
methodName: "getUint",
|
||||
params: [keys.claimableFundingAmountLong],
|
||||
},
|
||||
claimableFundingAmountShort: {
|
||||
methodName: "getUint",
|
||||
params: [keys.claimableFundingAmountShort],
|
||||
},
|
||||
},
|
||||
} satisfies ContractCallsConfig<any>;
|
||||
|
||||
return request;
|
||||
}, {});
|
||||
},
|
||||
{} as Record<string, ContractCallsConfig<any>>
|
||||
);
|
||||
}
|
||||
|
||||
export async function buildMarketsValuesRequest(
|
||||
@@ -82,6 +86,13 @@ export async function buildMarketsValuesRequest(
|
||||
|
||||
for (const marketAddress of marketsAddresses || []) {
|
||||
const market = getByKey(marketsData, marketAddress)!;
|
||||
|
||||
if (!market) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`No market data found for ${marketAddress}, skipping market values request`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const marketPrices = getContractMarketPrices(tokensData!, market)!;
|
||||
|
||||
if (!marketPrices) {
|
||||
@@ -243,204 +254,120 @@ export async function buildMarketsConfigsRequest(
|
||||
prebuiltHashedKeys = hashMarketConfigKeys(marketsData[marketAddress]);
|
||||
}
|
||||
|
||||
// Add validation to check for undefined keys
|
||||
const requiredKeys = [
|
||||
'isDisabled', 'maxLongPoolAmount', 'maxShortPoolAmount', 'maxLongPoolUsdForDeposit',
|
||||
'maxShortPoolUsdForDeposit', 'longPoolAmountAdjustment', 'shortPoolAmountAdjustment',
|
||||
'reserveFactorLong', 'reserveFactorShort', 'openInterestReserveFactorLong',
|
||||
'openInterestReserveFactorShort', 'maxOpenInterestLong', 'maxOpenInterestShort',
|
||||
'minPositionImpactPoolAmount', 'positionImpactPoolDistributionRate', 'borrowingFactorLong',
|
||||
'borrowingFactorShort', 'borrowingExponentFactorLong', 'borrowingExponentFactorShort',
|
||||
'fundingFactor', 'fundingExponentFactor', 'fundingIncreaseFactorPerSecond',
|
||||
'fundingDecreaseFactorPerSecond', 'thresholdForStableFunding', 'thresholdForDecreaseFunding',
|
||||
'minFundingFactorPerSecond', 'maxFundingFactorPerSecond', 'maxPnlFactorForTradersLong',
|
||||
'maxPnlFactorForTradersShort', 'positionFeeFactorForBalanceWasImproved',
|
||||
'positionFeeFactorForBalanceWasNotImproved', 'positionFeeFactorForPositiveImpact',
|
||||
'positionFeeFactorForNegativeImpact', 'positionImpactFactorPositive', 'positionImpactFactorNegative',
|
||||
'maxPositionImpactFactorPositive', 'maxPositionImpactFactorNegative', 'maxPositionImpactFactorForLiquidations',
|
||||
'maxLendableImpactFactor', 'maxLendableImpactFactorForWithdrawals', 'maxLendableImpactUsd',
|
||||
'lentPositionImpactPoolAmount', 'minCollateralFactor', 'minCollateralFactorForLiquidation',
|
||||
'minCollateralFactorForOpenInterestLong', 'minCollateralFactorForOpenInterestShort',
|
||||
'positionImpactExponentFactor', 'swapFeeFactorForBalanceWasImproved', 'swapFeeFactorForBalanceWasNotImproved',
|
||||
'swapFeeFactorForPositiveImpact', 'swapFeeFactorForNegativeImpact', 'atomicSwapFeeFactor',
|
||||
'swapImpactFactorPositive', 'swapImpactFactorNegative', 'swapImpactExponentFactor',
|
||||
'virtualMarketId', 'virtualShortTokenId', 'virtualLongTokenId'
|
||||
];
|
||||
|
||||
for (const key of requiredKeys) {
|
||||
if (prebuiltHashedKeys[key] === undefined) {
|
||||
if (!marketsData?.[marketAddress]) {
|
||||
throw new Error(`No market data found for the market ${marketAddress}`);
|
||||
}
|
||||
// Regenerate all keys if any are missing
|
||||
prebuiltHashedKeys = hashMarketConfigKeys(marketsData[marketAddress]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create calls object, filtering out any undefined keys
|
||||
const calls: Record<string, any> = {};
|
||||
|
||||
// Helper function to add call if key exists
|
||||
const addCall = (callKey: string, methodName: string, hashKey: any) => {
|
||||
if (hashKey !== undefined) {
|
||||
calls[callKey] = {
|
||||
methodName,
|
||||
params: [hashKey],
|
||||
};
|
||||
} else {
|
||||
console.warn(`Skipping undefined hash key: ${callKey} for market ${marketAddress}`);
|
||||
}
|
||||
};
|
||||
|
||||
addCall('isDisabled', 'getBool', prebuiltHashedKeys.isDisabled);
|
||||
addCall('maxLongPoolAmount', 'getUint', prebuiltHashedKeys.maxLongPoolAmount);
|
||||
addCall('maxShortPoolAmount', 'getUint', prebuiltHashedKeys.maxShortPoolAmount);
|
||||
addCall('maxLongPoolUsdForDeposit', 'getUint', prebuiltHashedKeys.maxLongPoolUsdForDeposit);
|
||||
addCall('maxShortPoolUsdForDeposit', 'getUint', prebuiltHashedKeys.maxShortPoolUsdForDeposit);
|
||||
addCall('longPoolAmountAdjustment', 'getUint', prebuiltHashedKeys.longPoolAmountAdjustment);
|
||||
addCall('shortPoolAmountAdjustment', 'getUint', prebuiltHashedKeys.shortPoolAmountAdjustment);
|
||||
addCall('reserveFactorLong', 'getUint', prebuiltHashedKeys.reserveFactorLong);
|
||||
addCall('reserveFactorShort', 'getUint', prebuiltHashedKeys.reserveFactorShort);
|
||||
addCall('openInterestReserveFactorLong', 'getUint', prebuiltHashedKeys.openInterestReserveFactorLong);
|
||||
addCall('openInterestReserveFactorShort', 'getUint', prebuiltHashedKeys.openInterestReserveFactorShort);
|
||||
addCall('maxOpenInterestLong', 'getUint', prebuiltHashedKeys.maxOpenInterestLong);
|
||||
addCall('maxOpenInterestShort', 'getUint', prebuiltHashedKeys.maxOpenInterestShort);
|
||||
addCall('minPositionImpactPoolAmount', 'getUint', prebuiltHashedKeys.minPositionImpactPoolAmount);
|
||||
addCall('positionImpactPoolDistributionRate', 'getUint', prebuiltHashedKeys.positionImpactPoolDistributionRate);
|
||||
addCall('borrowingFactorLong', 'getUint', prebuiltHashedKeys.borrowingFactorLong);
|
||||
addCall('borrowingFactorShort', 'getUint', prebuiltHashedKeys.borrowingFactorShort);
|
||||
addCall('borrowingExponentFactorLong', 'getUint', prebuiltHashedKeys.borrowingExponentFactorLong);
|
||||
addCall('borrowingExponentFactorShort', 'getUint', prebuiltHashedKeys.borrowingExponentFactorShort);
|
||||
addCall('fundingFactor', 'getUint', prebuiltHashedKeys.fundingFactor);
|
||||
addCall('fundingExponentFactor', 'getUint', prebuiltHashedKeys.fundingExponentFactor);
|
||||
addCall('fundingIncreaseFactorPerSecond', 'getUint', prebuiltHashedKeys.fundingIncreaseFactorPerSecond);
|
||||
addCall('fundingDecreaseFactorPerSecond', 'getUint', prebuiltHashedKeys.fundingDecreaseFactorPerSecond);
|
||||
addCall('thresholdForStableFunding', 'getUint', prebuiltHashedKeys.thresholdForStableFunding);
|
||||
addCall('thresholdForDecreaseFunding', 'getUint', prebuiltHashedKeys.thresholdForDecreaseFunding);
|
||||
addCall('minFundingFactorPerSecond', 'getUint', prebuiltHashedKeys.minFundingFactorPerSecond);
|
||||
addCall('maxFundingFactorPerSecond', 'getUint', prebuiltHashedKeys.maxFundingFactorPerSecond);
|
||||
addCall('maxPnlFactorForTradersLong', 'getUint', prebuiltHashedKeys.maxPnlFactorForTradersLong);
|
||||
addCall('maxPnlFactorForTradersShort', 'getUint', prebuiltHashedKeys.maxPnlFactorForTradersShort);
|
||||
addCall('positionFeeFactorForBalanceWasImproved', 'getUint', prebuiltHashedKeys.positionFeeFactorForBalanceWasImproved);
|
||||
addCall('positionFeeFactorForBalanceWasNotImproved', 'getUint', prebuiltHashedKeys.positionFeeFactorForBalanceWasNotImproved);
|
||||
addCall('positionFeeFactorForPositiveImpact', 'getUint', prebuiltHashedKeys.positionFeeFactorForPositiveImpact);
|
||||
addCall('positionFeeFactorForNegativeImpact', 'getUint', prebuiltHashedKeys.positionFeeFactorForNegativeImpact);
|
||||
addCall('positionImpactFactorPositive', 'getUint', prebuiltHashedKeys.positionImpactFactorPositive);
|
||||
addCall('positionImpactFactorNegative', 'getUint', prebuiltHashedKeys.positionImpactFactorNegative);
|
||||
addCall('maxPositionImpactFactorPositive', 'getUint', prebuiltHashedKeys.maxPositionImpactFactorPositive);
|
||||
addCall('maxPositionImpactFactorNegative', 'getUint', prebuiltHashedKeys.maxPositionImpactFactorNegative);
|
||||
addCall('maxPositionImpactFactorForLiquidations', 'getUint', prebuiltHashedKeys.maxPositionImpactFactorForLiquidations);
|
||||
addCall('maxLendableImpactFactor', 'getUint', prebuiltHashedKeys.maxLendableImpactFactor);
|
||||
addCall('maxLendableImpactFactorForWithdrawals', 'getUint', prebuiltHashedKeys.maxLendableImpactFactorForWithdrawals);
|
||||
addCall('maxLendableImpactUsd', 'getUint', prebuiltHashedKeys.maxLendableImpactUsd);
|
||||
addCall('lentPositionImpactPoolAmount', 'getUint', prebuiltHashedKeys.lentPositionImpactPoolAmount);
|
||||
addCall('minCollateralFactor', 'getUint', prebuiltHashedKeys.minCollateralFactor);
|
||||
addCall('minCollateralFactorForLiquidation', 'getUint', prebuiltHashedKeys.minCollateralFactorForLiquidation);
|
||||
addCall('minCollateralFactorForOpenInterestLong', 'getUint', prebuiltHashedKeys.minCollateralFactorForOpenInterestLong);
|
||||
addCall('minCollateralFactorForOpenInterestShort', 'getUint', prebuiltHashedKeys.minCollateralFactorForOpenInterestShort);
|
||||
addCall('positionImpactExponentFactor', 'getUint', prebuiltHashedKeys.positionImpactExponentFactor);
|
||||
addCall('swapFeeFactorForBalanceWasImproved', 'getUint', prebuiltHashedKeys.swapFeeFactorForBalanceWasImproved);
|
||||
addCall('swapFeeFactorForBalanceWasNotImproved', 'getUint', prebuiltHashedKeys.swapFeeFactorForBalanceWasNotImproved);
|
||||
addCall('swapFeeFactorForPositiveImpact', 'getUint', prebuiltHashedKeys.swapFeeFactorForPositiveImpact);
|
||||
addCall('swapFeeFactorForNegativeImpact', 'getUint', prebuiltHashedKeys.swapFeeFactorForNegativeImpact);
|
||||
addCall('atomicSwapFeeFactor', 'getUint', prebuiltHashedKeys.atomicSwapFeeFactor);
|
||||
addCall('swapImpactFactorPositive', 'getUint', prebuiltHashedKeys.swapImpactFactorPositive);
|
||||
addCall('swapImpactFactorNegative', 'getUint', prebuiltHashedKeys.swapImpactFactorNegative);
|
||||
addCall('swapImpactExponentFactor', 'getUint', prebuiltHashedKeys.swapImpactExponentFactor);
|
||||
addCall('virtualMarketId', 'getBytes32', prebuiltHashedKeys.virtualMarketId);
|
||||
addCall('virtualShortTokenId', 'getBytes32', prebuiltHashedKeys.virtualShortTokenId);
|
||||
addCall('virtualLongTokenId', 'getBytes32', prebuiltHashedKeys.virtualLongTokenId);
|
||||
|
||||
request[`${marketAddress}-dataStore`] = {
|
||||
contractAddress: dataStoreAddress,
|
||||
abiId: "DataStore",
|
||||
calls: {
|
||||
isDisabled: {
|
||||
methodName: "getBool",
|
||||
params: [prebuiltHashedKeys.isDisabled],
|
||||
},
|
||||
maxLongPoolAmount: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxLongPoolAmount],
|
||||
},
|
||||
maxShortPoolAmount: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxShortPoolAmount],
|
||||
},
|
||||
maxLongPoolUsdForDeposit: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxLongPoolUsdForDeposit],
|
||||
},
|
||||
maxShortPoolUsdForDeposit: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxShortPoolUsdForDeposit],
|
||||
},
|
||||
longPoolAmountAdjustment: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.longPoolAmountAdjustment],
|
||||
},
|
||||
shortPoolAmountAdjustment: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.shortPoolAmountAdjustment],
|
||||
},
|
||||
reserveFactorLong: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.reserveFactorLong],
|
||||
},
|
||||
reserveFactorShort: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.reserveFactorShort],
|
||||
},
|
||||
openInterestReserveFactorLong: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.openInterestReserveFactorLong],
|
||||
},
|
||||
openInterestReserveFactorShort: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.openInterestReserveFactorShort],
|
||||
},
|
||||
maxOpenInterestLong: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxOpenInterestLong],
|
||||
},
|
||||
maxOpenInterestShort: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxOpenInterestShort],
|
||||
},
|
||||
minPositionImpactPoolAmount: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.minPositionImpactPoolAmount],
|
||||
},
|
||||
positionImpactPoolDistributionRate: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.positionImpactPoolDistributionRate],
|
||||
},
|
||||
borrowingFactorLong: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.borrowingFactorLong],
|
||||
},
|
||||
borrowingFactorShort: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.borrowingFactorShort],
|
||||
},
|
||||
borrowingExponentFactorLong: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.borrowingExponentFactorLong],
|
||||
},
|
||||
borrowingExponentFactorShort: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.borrowingExponentFactorShort],
|
||||
},
|
||||
fundingFactor: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.fundingFactor],
|
||||
},
|
||||
fundingExponentFactor: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.fundingExponentFactor],
|
||||
},
|
||||
fundingIncreaseFactorPerSecond: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.fundingIncreaseFactorPerSecond],
|
||||
},
|
||||
fundingDecreaseFactorPerSecond: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.fundingDecreaseFactorPerSecond],
|
||||
},
|
||||
thresholdForStableFunding: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.thresholdForStableFunding],
|
||||
},
|
||||
thresholdForDecreaseFunding: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.thresholdForDecreaseFunding],
|
||||
},
|
||||
minFundingFactorPerSecond: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.minFundingFactorPerSecond],
|
||||
},
|
||||
maxFundingFactorPerSecond: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxFundingFactorPerSecond],
|
||||
},
|
||||
maxPnlFactorForTradersLong: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxPnlFactorForTradersLong],
|
||||
},
|
||||
maxPnlFactorForTradersShort: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxPnlFactorForTradersShort],
|
||||
},
|
||||
positionFeeFactorForPositiveImpact: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.positionFeeFactorForPositiveImpact],
|
||||
},
|
||||
positionFeeFactorForNegativeImpact: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.positionFeeFactorForNegativeImpact],
|
||||
},
|
||||
positionImpactFactorPositive: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.positionImpactFactorPositive],
|
||||
},
|
||||
positionImpactFactorNegative: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.positionImpactFactorNegative],
|
||||
},
|
||||
maxPositionImpactFactorPositive: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxPositionImpactFactorPositive],
|
||||
},
|
||||
maxPositionImpactFactorNegative: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxPositionImpactFactorNegative],
|
||||
},
|
||||
maxPositionImpactFactorForLiquidations: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.maxPositionImpactFactorForLiquidations],
|
||||
},
|
||||
minCollateralFactor: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.minCollateralFactor],
|
||||
},
|
||||
minCollateralFactorForOpenInterestLong: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.minCollateralFactorForOpenInterestLong],
|
||||
},
|
||||
minCollateralFactorForOpenInterestShort: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.minCollateralFactorForOpenInterestShort],
|
||||
},
|
||||
positionImpactExponentFactor: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.positionImpactExponentFactor],
|
||||
},
|
||||
swapFeeFactorForPositiveImpact: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.swapFeeFactorForPositiveImpact],
|
||||
},
|
||||
swapFeeFactorForNegativeImpact: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.swapFeeFactorForNegativeImpact],
|
||||
},
|
||||
swapImpactFactorPositive: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.swapImpactFactorPositive],
|
||||
},
|
||||
swapImpactFactorNegative: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.swapImpactFactorNegative],
|
||||
},
|
||||
swapImpactExponentFactor: {
|
||||
methodName: "getUint",
|
||||
params: [prebuiltHashedKeys.swapImpactExponentFactor],
|
||||
},
|
||||
virtualMarketId: {
|
||||
methodName: "getBytes32",
|
||||
params: [prebuiltHashedKeys.virtualMarketId],
|
||||
},
|
||||
virtualShortTokenId: {
|
||||
methodName: "getBytes32",
|
||||
params: [prebuiltHashedKeys.virtualShortTokenId],
|
||||
},
|
||||
virtualLongTokenId: {
|
||||
methodName: "getBytes32",
|
||||
params: [prebuiltHashedKeys.virtualLongTokenId],
|
||||
},
|
||||
},
|
||||
} satisfies ContractCallsConfig<any>;
|
||||
calls,
|
||||
} as any;
|
||||
}
|
||||
|
||||
return request;
|
||||
|
||||
@@ -93,6 +93,12 @@ export type MarketConfig = Pick<
|
||||
| "virtualMarketId"
|
||||
| "virtualLongTokenId"
|
||||
| "virtualShortTokenId"
|
||||
| "atomicSwapFeeFactor"
|
||||
| "swapFeeFactorForBalanceWasImproved"
|
||||
| "swapFeeFactorForBalanceWasNotImproved"
|
||||
| "positionFeeFactorForBalanceWasImproved"
|
||||
| "positionFeeFactorForBalanceWasNotImproved"
|
||||
| "minCollateralFactorForLiquidation"
|
||||
>;
|
||||
|
||||
export type MarketValuesMulticallRequestConfig = MulticallRequestConfig<{
|
||||
@@ -178,7 +184,19 @@ export type MarketConfigMulticallRequestConfig = MulticallRequestConfig<{
|
||||
| "swapImpactExponentFactor"
|
||||
| "virtualMarketId"
|
||||
| "virtualLongTokenId"
|
||||
| "virtualShortTokenId",
|
||||
| "virtualShortTokenId"
|
||||
| "positionFeeFactorForBalanceWasImproved"
|
||||
| "positionFeeFactorForBalanceWasNotImproved"
|
||||
| "swapFeeFactorForBalanceWasImproved"
|
||||
| "swapFeeFactorForBalanceWasNotImproved"
|
||||
| "atomicSwapFeeFactor"
|
||||
| "minCollateralFactorForLiquidation"
|
||||
| "maxLendableImpactFactor"
|
||||
| "maxLendableImpactFactorForWithdrawals"
|
||||
| "maxLendableImpactUsd"
|
||||
| "lentPositionImpactPoolAmount"
|
||||
| "minCollateralFactor",
|
||||
|
||||
{
|
||||
methodName: string;
|
||||
params: any[];
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { describe, it, expect, vi, beforeEach, beforeAll } from "vitest";
|
||||
import {beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import * as swapPath from "../../utils/swap/swapPath.js";
|
||||
import * as tradeAmounts from "../../utils/trade/amounts.js";
|
||||
import { arbitrumSdk } from "../../utils/testUtil.js";
|
||||
import { ARBITRUM } from "../../configs/chains.js";
|
||||
import { getByKey } from "../../utils/objects.js";
|
||||
import { MarketInfo, MarketsInfoData } from "../../types/markets.js";
|
||||
import { TokenData, TokensData } from "../../types/tokens.js";
|
||||
import {arbitrumSdk} from "../../utils/testUtil.js";
|
||||
import {ARBITRUM} from "../../configs/chains.js";
|
||||
import {getByKey} from "../../utils/objects.js";
|
||||
import {MarketInfo, MarketsInfoData} from "../../types/markets.js";
|
||||
import {TokenData, TokensData} from "../../types/tokens.js";
|
||||
import * as tradeAmounts from "../../utils/trade/increase.js";
|
||||
|
||||
describe("increaseOrderHelper", () => {
|
||||
let mockParams;
|
||||
let createIncreaseOrderSpy;
|
||||
|
||||
let marketsInfoData: MarketsInfoData;
|
||||
let tokensData: TokensData;
|
||||
|
||||
let mockParams: any;
|
||||
let createIncreaseOrderSpy: any;
|
||||
let market: MarketInfo;
|
||||
let payToken: TokenData;
|
||||
let collateralToken: TokenData;
|
||||
@@ -27,12 +25,6 @@ describe("increaseOrderHelper", () => {
|
||||
|
||||
marketsInfoData = result.marketsInfoData;
|
||||
tokensData = result.tokensData;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
createIncreaseOrderSpy = vi.spyOn(arbitrumSdk.orders, "createIncreaseOrder").mockResolvedValue();
|
||||
|
||||
market = getByKey(marketsInfoData, "0x70d95587d40A2caf56bd97485aB3Eec10Bee6336")!;
|
||||
|
||||
@@ -55,64 +47,112 @@ describe("increaseOrderHelper", () => {
|
||||
};
|
||||
});
|
||||
|
||||
it("should call createIncreaseOrder with correct parameters for a market order with payAmount", async () => {
|
||||
const findSwapPathSpy = vi.spyOn(swapPath, "createFindSwapPath");
|
||||
const getIncreasePositionAmountsSpy = vi.spyOn(tradeAmounts, "getIncreasePositionAmounts");
|
||||
describe("validation", () => {
|
||||
it("should throw an error if wrong collateral token selected", async () => {
|
||||
const e = await arbitrumSdk.orders
|
||||
.long({
|
||||
...mockParams,
|
||||
marketAddress: "0x47c031236e19d024b42f8AE6780E44A573170703",
|
||||
collateralTokenAddress: "0xC4da4c24fd591125c3F47b340b6f4f76111883d8",
|
||||
})
|
||||
.catch((error) => {
|
||||
return error.message;
|
||||
});
|
||||
|
||||
await arbitrumSdk.orders.long(mockParams);
|
||||
await expect(e).toBe("collateralTokenAddress: synthetic tokens are not supported");
|
||||
});
|
||||
|
||||
expect(findSwapPathSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
chainId: ARBITRUM,
|
||||
fromTokenAddress: payToken.address,
|
||||
toTokenAddress: collateralToken.address,
|
||||
marketsInfoData: expect.any(Object),
|
||||
estimator: expect.any(Function),
|
||||
allPaths: expect.any(Array),
|
||||
})
|
||||
);
|
||||
it("should throw an error if wrong collateral token selected", async () => {
|
||||
const e = await arbitrumSdk.orders
|
||||
.long({
|
||||
...mockParams,
|
||||
collateralTokenAddress: "0xFEa7a6a0B346362BF88A9e4A88416B77a57D6c2A",
|
||||
})
|
||||
.catch((error) => {
|
||||
return error.message;
|
||||
});
|
||||
|
||||
expect(getIncreasePositionAmountsSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
isLong: true,
|
||||
initialCollateralAmount: 1000n,
|
||||
leverage: 50000n,
|
||||
strategy: "leverageByCollateral",
|
||||
marketInfo: market,
|
||||
})
|
||||
);
|
||||
await expect(e).toBe("collateralTokenAddress: token is not available");
|
||||
});
|
||||
|
||||
expect(createIncreaseOrderSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
marketsInfoData: expect.any(Object),
|
||||
tokensData: expect.any(Object),
|
||||
marketInfo: market,
|
||||
indexToken: market.indexToken,
|
||||
isLimit: false,
|
||||
marketAddress: market.marketTokenAddress,
|
||||
allowedSlippage: 125,
|
||||
collateralTokenAddress: collateralToken.address,
|
||||
collateralToken,
|
||||
isLong: true,
|
||||
receiveTokenAddress: collateralToken.address,
|
||||
increaseAmounts: expect.objectContaining({
|
||||
it("should throw an error if wrong collateral token selected", async () => {
|
||||
const e = await arbitrumSdk.orders
|
||||
.long({
|
||||
...mockParams,
|
||||
collateralTokenAddress: "0x912CE59144191C1204E64559FE8253a0e49E6548",
|
||||
})
|
||||
.catch((error) => {
|
||||
return error.message;
|
||||
});
|
||||
|
||||
await expect(e).toBe("Invalid collateral token. Only long WETH and short USDC tokens are available.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("parameters", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
createIncreaseOrderSpy = vi.spyOn(arbitrumSdk.orders, "createIncreaseOrder").mockResolvedValue();
|
||||
});
|
||||
|
||||
it("should call createIncreaseOrder with correct parameters for a market order with payAmount", async () => {
|
||||
const findSwapPathSpy = vi.spyOn(swapPath, "createFindSwapPath");
|
||||
const getIncreasePositionAmountsSpy = vi.spyOn(tradeAmounts, "getIncreasePositionAmounts");
|
||||
|
||||
await arbitrumSdk.orders.long(mockParams);
|
||||
|
||||
expect(findSwapPathSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
chainId: ARBITRUM,
|
||||
fromTokenAddress: payToken.address,
|
||||
toTokenAddress: collateralToken.address,
|
||||
marketsInfoData: expect.any(Object),
|
||||
})
|
||||
);
|
||||
|
||||
expect(getIncreasePositionAmountsSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
isLong: true,
|
||||
initialCollateralAmount: 1000n,
|
||||
estimatedLeverage: 50000n,
|
||||
triggerPrice: undefined,
|
||||
acceptablePrice: 0n,
|
||||
acceptablePriceDeltaBps: 0n,
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
feeDiscountUsd: 0n,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
positionPriceImpactDeltaUsd: 0n,
|
||||
limitOrderType: undefined,
|
||||
triggerThresholdType: undefined,
|
||||
externalSwapQuote: undefined,
|
||||
}),
|
||||
})
|
||||
);
|
||||
leverage: 50000n,
|
||||
strategy: "leverageByCollateral",
|
||||
marketInfo: market,
|
||||
})
|
||||
);
|
||||
|
||||
expect(createIncreaseOrderSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
marketsInfoData: expect.any(Object),
|
||||
tokensData: expect.any(Object),
|
||||
marketInfo: market,
|
||||
indexToken: market.indexToken,
|
||||
isLimit: false,
|
||||
marketAddress: market.marketTokenAddress,
|
||||
allowedSlippage: 125,
|
||||
collateralTokenAddress: collateralToken.address,
|
||||
collateralToken,
|
||||
isLong: true,
|
||||
receiveTokenAddress: collateralToken.address,
|
||||
increaseAmounts: expect.objectContaining({
|
||||
initialCollateralAmount: 1000n,
|
||||
estimatedLeverage: 50000n,
|
||||
triggerPrice: undefined,
|
||||
acceptablePrice: 0n,
|
||||
acceptablePriceDeltaBps: 0n,
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
feeDiscountUsd: 0n,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
positionPriceImpactDeltaUsd: 0n,
|
||||
limitOrderType: undefined,
|
||||
triggerThresholdType: undefined,
|
||||
swapStrategy: expect.any(Object),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {MarketsInfoData} from "../../types/markets.js";
|
||||
import {DecreasePositionSwapType, OrderType} from "../../types/orders.js";
|
||||
import {TokenData, TokensData, TokensRatio} from "../../types/tokens.js";
|
||||
import {DecreasePositionAmounts, FindSwapPath, SwapAmounts} from "../../types/trade.js";
|
||||
import {DecreasePositionAmounts, SwapAmounts, SwapOptimizationOrderArray} from "../../types/trade.js";
|
||||
import {getByKey} from "../../utils/objects.js";
|
||||
import {createFindSwapPath, findAllSwapPaths, getWrappedAddress} from "../../utils/swap/swapPath.js";
|
||||
import {createFindSwapPath} from "../../utils/swap/swapPath.js";
|
||||
import {
|
||||
convertToUsd,
|
||||
getIsEquivalentTokens,
|
||||
@@ -11,33 +11,34 @@ import {
|
||||
getIsWrap,
|
||||
getTokensRatioByPrice
|
||||
} from "../../utils/tokens.js";
|
||||
import {getIncreasePositionAmounts} from "../../utils/trade/amounts.js";
|
||||
import {getAcceptablePriceInfo, getDefaultAcceptablePriceImpactBps, getOrderThresholdType} from "../../utils/prices.js";
|
||||
|
||||
import type {GmxSdk} from "../..";
|
||||
import {createSwapEstimator, getMarketsGraph} from "../../utils/swap/swapRouting.js";
|
||||
import {getSwapAmountsByFromValue, getSwapAmountsByToValue} from "../../utils/swap/index.js";
|
||||
import {EntryField, SidecarSlTpOrderEntryValid} from "../../types/sidecarOrders.js";
|
||||
import {ethers} from "ethers";
|
||||
import {GasLimitsConfig} from "../../types/fees.js";
|
||||
import {getIncreasePositionAmounts} from "../../utils/trade/increase.js";
|
||||
|
||||
/** Base Optional params for helpers, allows to avoid calling markets, tokens and uiFeeFactor methods if they are already passed */
|
||||
interface BaseOptionalParams {
|
||||
marketsInfoData?: MarketsInfoData;
|
||||
tokensData?: TokensData;
|
||||
uiFeeFactor?: bigint;
|
||||
skipSimulation?: boolean;
|
||||
}
|
||||
gasPrice?: bigint;
|
||||
gasLimits?: GasLimitsConfig;
|
||||
}
|
||||
|
||||
export type PositionIncreaseParams = (
|
||||
export type PositionIncreaseParams = (
|
||||
| {
|
||||
/** Increase amounts will be calculated based on collateral amount */
|
||||
payAmount: bigint;
|
||||
}
|
||||
/** Increase amounts will be calculated based on collateral amount */
|
||||
payAmount: bigint;
|
||||
}
|
||||
| {
|
||||
/** Increase amounts will be calculated based on position size amount */
|
||||
sizeAmount: bigint;
|
||||
}
|
||||
) & {
|
||||
/** Increase amounts will be calculated based on position size amount */
|
||||
sizeAmount: bigint;
|
||||
}
|
||||
) & {
|
||||
marketAddress: string;
|
||||
payTokenAddress: string;
|
||||
collateralTokenAddress: string;
|
||||
@@ -55,45 +56,84 @@ export type PositionIncreaseParams = (
|
||||
skipSimulation?: boolean;
|
||||
stopLossPrice?: bigint;
|
||||
takeProfitPrice?: bigint;
|
||||
} & BaseOptionalParams;
|
||||
} & BaseOptionalParams;
|
||||
|
||||
async function getAndValidateBaseParams(sdk: GmxSdk, params: BaseOptionalParams) {
|
||||
let tokensData: TokensData | undefined = params.tokensData;
|
||||
let marketsInfoData: MarketsInfoData | undefined = params.marketsInfoData;
|
||||
|
||||
if (!params.marketsInfoData && !params.tokensData) {
|
||||
const result = await sdk.markets.getMarketsInfo();
|
||||
marketsInfoData = result.marketsInfoData;
|
||||
tokensData = result.tokensData;
|
||||
function passThoughOrFetch<T>(value: T, condition: (input: T) => boolean, fetchFn: () => Promise<T>) {
|
||||
if (condition(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (!tokensData) {
|
||||
throw new Error("Tokens data is not available");
|
||||
return fetchFn();
|
||||
}
|
||||
|
||||
async function getAndValidateBaseParams(
|
||||
sdk: GmxSdk,
|
||||
params: BaseOptionalParams
|
||||
): Promise<Required<BaseOptionalParams>> {
|
||||
const [marketsInfoResult, uiFeeFactor, gasPrice, gasLimits] = await Promise.all([
|
||||
passThoughOrFetch(
|
||||
{
|
||||
marketsInfoData: params.marketsInfoData,
|
||||
tokensData: params.tokensData,
|
||||
},
|
||||
(input) => Boolean(input.marketsInfoData) && Boolean(input.tokensData),
|
||||
() => sdk.markets.getMarketsInfo()
|
||||
),
|
||||
passThoughOrFetch(
|
||||
params.uiFeeFactor,
|
||||
(input) => input !== undefined,
|
||||
() => sdk.utils.getUiFeeFactor()
|
||||
),
|
||||
passThoughOrFetch(
|
||||
params.gasPrice,
|
||||
(input) => input !== undefined,
|
||||
() => sdk.utils.getGasPrice()
|
||||
),
|
||||
passThoughOrFetch(
|
||||
params.gasLimits,
|
||||
(input) => input !== undefined,
|
||||
() => sdk.utils.getGasLimits()
|
||||
),
|
||||
]);
|
||||
|
||||
if (!marketsInfoResult.marketsInfoData) {
|
||||
throw new Error("Markets info data is not available");
|
||||
}
|
||||
|
||||
if (!marketsInfoData) {
|
||||
throw new Error("Markets info data is not available");
|
||||
if (!marketsInfoResult.tokensData) {
|
||||
throw new Error("Tokens data is not available");
|
||||
}
|
||||
|
||||
let uiFeeFactor = params.uiFeeFactor;
|
||||
if (!uiFeeFactor) {
|
||||
uiFeeFactor = await sdk.utils.getUiFeeFactor();
|
||||
if (uiFeeFactor === undefined) {
|
||||
throw new Error("Ui fee factor is not available");
|
||||
}
|
||||
|
||||
if (gasPrice === undefined) {
|
||||
throw new Error("Gas price is not available");
|
||||
}
|
||||
|
||||
if (gasLimits === undefined) {
|
||||
throw new Error("Gas limits are not available");
|
||||
}
|
||||
|
||||
return {
|
||||
tokensData,
|
||||
marketsInfoData,
|
||||
uiFeeFactor,
|
||||
tokensData: marketsInfoResult.tokensData,
|
||||
marketsInfoData: marketsInfoResult.marketsInfoData,
|
||||
uiFeeFactor,
|
||||
gasPrice,
|
||||
gasLimits,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function increaseOrderHelper(
|
||||
export async function increaseOrderHelper(
|
||||
sdk: GmxSdk,
|
||||
params: PositionIncreaseParams & {
|
||||
isLong: boolean;
|
||||
isLong: boolean;
|
||||
stopLossPrice?: bigint;
|
||||
takeProfitPrice?: bigint;
|
||||
}
|
||||
) {
|
||||
const {tokensData, marketsInfoData, uiFeeFactor} = await getAndValidateBaseParams(sdk, params);
|
||||
) {
|
||||
const { tokensData, marketsInfoData, uiFeeFactor, gasLimits, gasPrice } = await getAndValidateBaseParams(sdk, params);
|
||||
|
||||
const isLimit = Boolean(params.limitPrice);
|
||||
|
||||
@@ -101,315 +141,323 @@ export async function increaseOrderHelper(
|
||||
const collateralToken = tokensData[params.collateralTokenAddress];
|
||||
|
||||
if (!fromToken) {
|
||||
throw new Error("From token is not available");
|
||||
throw new Error("payTokenAddress: token is not available");
|
||||
}
|
||||
|
||||
if (!collateralToken) {
|
||||
throw new Error("Collateral token is not available");
|
||||
throw new Error("collateralTokenAddress: token is not available");
|
||||
}
|
||||
|
||||
if (fromToken.isSynthetic) {
|
||||
throw new Error("payTokenAddress: synthetic tokens are not supported");
|
||||
}
|
||||
|
||||
if (collateralToken.isSynthetic) {
|
||||
throw new Error("collateralTokenAddress: synthetic tokens are not supported");
|
||||
}
|
||||
|
||||
const marketInfo = getByKey(marketsInfoData, params.marketAddress);
|
||||
|
||||
if (!marketInfo) {
|
||||
throw new Error("Market info is not available");
|
||||
throw new Error("Market info is not available");
|
||||
}
|
||||
|
||||
const collateralTokenAddress = collateralToken.address;
|
||||
const allowedSlippage = params.allowedSlippageBps ?? 100;
|
||||
|
||||
const graph = getMarketsGraph(Object.values(marketsInfoData));
|
||||
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.payTokenAddress);
|
||||
const wrappedToAddress = getWrappedAddress(sdk.chainId, collateralTokenAddress);
|
||||
|
||||
const allPaths = findAllSwapPaths({
|
||||
chainId: sdk.chainId,
|
||||
fromTokenAddress: params.payTokenAddress,
|
||||
toTokenAddress: collateralTokenAddress,
|
||||
marketsInfoData,
|
||||
graph,
|
||||
wrappedFromAddress,
|
||||
wrappedToAddress,
|
||||
});
|
||||
|
||||
const estimator = createSwapEstimator(marketsInfoData);
|
||||
|
||||
const findSwapPath = createFindSwapPath({
|
||||
chainId: sdk.chainId,
|
||||
fromTokenAddress: params.payTokenAddress,
|
||||
toTokenAddress: collateralTokenAddress,
|
||||
marketsInfoData,
|
||||
estimator,
|
||||
allPaths,
|
||||
chainId: sdk.chainId,
|
||||
fromTokenAddress: params.payTokenAddress,
|
||||
toTokenAddress: collateralTokenAddress,
|
||||
marketsInfoData,
|
||||
gasEstimationParams: {
|
||||
gasLimits,
|
||||
gasPrice,
|
||||
tokensData,
|
||||
},
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const payOrSizeAmount = "payAmount" in params ? params.payAmount : params.sizeAmount;
|
||||
|
||||
const increaseAmounts = getIncreasePositionAmounts({
|
||||
marketInfo,
|
||||
indexToken: marketInfo.indexToken,
|
||||
initialCollateralToken: fromToken,
|
||||
collateralToken,
|
||||
isLong: params.isLong,
|
||||
initialCollateralAmount: payOrSizeAmount,
|
||||
position: undefined,
|
||||
indexTokenAmount: payOrSizeAmount,
|
||||
leverage: params.leverage,
|
||||
triggerPrice: params.limitPrice,
|
||||
limitOrderType: params.limitPrice ? OrderType.LimitIncrease : undefined,
|
||||
userReferralInfo: undefined,
|
||||
strategy: "payAmount" in params ? "leverageByCollateral" : "leverageBySize",
|
||||
findSwapPath: findSwapPath,
|
||||
uiFeeFactor,
|
||||
acceptablePriceImpactBuffer: params.acceptablePriceImpactBuffer,
|
||||
fixedAcceptablePriceImpactBps: params.fixedAcceptablePriceImpactBps,
|
||||
externalSwapQuote: undefined,
|
||||
marketInfo,
|
||||
indexToken: marketInfo.indexToken,
|
||||
initialCollateralToken: fromToken,
|
||||
collateralToken,
|
||||
isLong: params.isLong,
|
||||
initialCollateralAmount: payOrSizeAmount,
|
||||
position: undefined,
|
||||
indexTokenAmount: payOrSizeAmount,
|
||||
leverage: params.leverage,
|
||||
triggerPrice: params.limitPrice,
|
||||
limitOrderType: params.limitPrice ? OrderType.LimitIncrease : undefined,
|
||||
userReferralInfo: undefined,
|
||||
strategy: "payAmount" in params ? "leverageByCollateral" : "leverageBySize",
|
||||
findSwapPath: findSwapPath,
|
||||
uiFeeFactor,
|
||||
acceptablePriceImpactBuffer: params.acceptablePriceImpactBuffer,
|
||||
fixedAcceptablePriceImpactBps: params.fixedAcceptablePriceImpactBps,
|
||||
externalSwapQuote: undefined,
|
||||
marketsInfoData,
|
||||
chainId: sdk.chainId,
|
||||
externalSwapQuoteParams: undefined,
|
||||
});
|
||||
|
||||
const createSltpEntries: SidecarSlTpOrderEntryValid[] = []
|
||||
|
||||
let stopLossDecreaseAmounts: DecreasePositionAmounts | undefined;
|
||||
if (params.stopLossPrice) {
|
||||
const stopLossCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.stopLossPrice);
|
||||
const createSltpEntries: SidecarSlTpOrderEntryValid[] = []
|
||||
|
||||
// Use a higher acceptable price impact buffer for stop loss orders to ensure execution
|
||||
const stopLossAcceptablePriceImpactBps = params.fixedAcceptablePriceImpactBps ??
|
||||
getDefaultAcceptablePriceImpactBps({
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.stopLossPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: 0n, // We'll calculate this based on current market conditions
|
||||
acceptablePriceImapctBuffer: params.acceptablePriceImpactBuffer ?? 100, // Default 1% buffer for SL orders
|
||||
});
|
||||
let stopLossDecreaseAmounts: DecreasePositionAmounts | undefined;
|
||||
if (params.stopLossPrice) {
|
||||
const stopLossCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.stopLossPrice);
|
||||
|
||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.stopLossPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
maxNegativePriceImpactBps: stopLossAcceptablePriceImpactBps,
|
||||
});
|
||||
// Use a higher acceptable price impact buffer for stop loss orders to ensure execution
|
||||
const stopLossAcceptablePriceImpactBps = params.fixedAcceptablePriceImpactBps ??
|
||||
getDefaultAcceptablePriceImpactBps({
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.stopLossPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: 0n, // We'll calculate this based on current market conditions
|
||||
acceptablePriceImapctBuffer: params.acceptablePriceImpactBuffer ?? 100, // Default 1% buffer for SL orders
|
||||
});
|
||||
|
||||
// Calculate collateral price based on GMX UI logic
|
||||
const stopLossCollateralPrice = getIsEquivalentTokens(marketInfo.indexToken, collateralToken)
|
||||
? params.stopLossPrice // Use trigger price if index token equals collateral token
|
||||
: collateralToken.prices.minPrice; // Otherwise use collateral token's min price
|
||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.stopLossPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
maxNegativePriceImpactBps: stopLossAcceptablePriceImpactBps,
|
||||
isLimit: false,
|
||||
});
|
||||
|
||||
stopLossDecreaseAmounts = {
|
||||
isFullClose: true,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
||||
collateralDeltaUsd: stopLossCollateralDeltaUsd,
|
||||
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
||||
indexPrice: params.stopLossPrice,
|
||||
collateralPrice: stopLossCollateralPrice,
|
||||
acceptablePrice: params.isLong ? 0n : ethers.MaxUint256,
|
||||
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps,
|
||||
recommendedAcceptablePriceDeltaBps: 30n,
|
||||
estimatedPnl: 0n,
|
||||
estimatedPnlPercentage: 0n,
|
||||
realizedPnl: 0n,
|
||||
realizedPnlPercentage: 0n,
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
feeDiscountUsd: 0n,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
swapProfitFeeUsd: 0n,
|
||||
positionPriceImpactDeltaUsd: 0n,
|
||||
priceImpactDiffUsd: acceptablePriceInfo.priceImpactDeltaUsd,
|
||||
payedRemainingCollateralAmount: 0n,
|
||||
payedOutputUsd: 0n,
|
||||
payedRemainingCollateralUsd: 0n,
|
||||
receiveTokenAmount: 0n,
|
||||
receiveUsd: 0n,
|
||||
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
||||
triggerOrderType: OrderType.StopLossDecrease,
|
||||
triggerPrice: params.stopLossPrice,
|
||||
triggerThresholdType: getOrderThresholdType(OrderType.StopLossDecrease, params.isLong),
|
||||
}
|
||||
// Calculate collateral price based on GMX UI logic
|
||||
const stopLossCollateralPrice = getIsEquivalentTokens(marketInfo.indexToken, collateralToken)
|
||||
? params.stopLossPrice // Use trigger price if index token equals collateral token
|
||||
: collateralToken.prices.minPrice; // Otherwise use collateral token's min price
|
||||
|
||||
console.log(stopLossDecreaseAmounts);
|
||||
stopLossDecreaseAmounts = {
|
||||
isFullClose: true,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
||||
collateralDeltaUsd: stopLossCollateralDeltaUsd,
|
||||
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
||||
indexPrice: params.stopLossPrice,
|
||||
collateralPrice: stopLossCollateralPrice,
|
||||
acceptablePrice: params.isLong ? 0n : ethers.MaxUint256,
|
||||
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps,
|
||||
recommendedAcceptablePriceDeltaBps: 30n,
|
||||
estimatedPnl: 0n,
|
||||
estimatedPnlPercentage: 0n,
|
||||
realizedPnl: 0n,
|
||||
realizedPnlPercentage: 0n,
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
feeDiscountUsd: 0n,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
swapProfitFeeUsd: 0n,
|
||||
priceImpactDiffUsd: acceptablePriceInfo.priceImpactDeltaUsd,
|
||||
proportionalPendingImpactDeltaUsd: 0n,
|
||||
closePriceImpactDeltaUsd: 0n,
|
||||
totalPendingImpactDeltaUsd: 0n,
|
||||
balanceWasImproved: false,
|
||||
payedRemainingCollateralAmount: 0n,
|
||||
payedOutputUsd: 0n,
|
||||
payedRemainingCollateralUsd: 0n,
|
||||
receiveTokenAmount: 0n,
|
||||
receiveUsd: 0n,
|
||||
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
||||
triggerOrderType: OrderType.StopLossDecrease,
|
||||
triggerPrice: params.stopLossPrice,
|
||||
triggerThresholdType: getOrderThresholdType(OrderType.StopLossDecrease, params.isLong),
|
||||
}
|
||||
|
||||
const stopLossEntry: SidecarSlTpOrderEntryValid = {
|
||||
decreaseAmounts: stopLossDecreaseAmounts,
|
||||
id: "sl-order",
|
||||
price: {
|
||||
input: params.stopLossPrice?.toString() ?? "",
|
||||
value: params.stopLossPrice,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
sizeUsd: {
|
||||
input: increaseAmounts.sizeDeltaUsd.toString(),
|
||||
value: increaseAmounts.sizeDeltaUsd,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
percentage: {
|
||||
input: "100",
|
||||
value: 100n,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
txnType: "create",
|
||||
mode: "keepSize",
|
||||
order: null,
|
||||
increaseAmounts: undefined
|
||||
}
|
||||
console.log(stopLossDecreaseAmounts);
|
||||
|
||||
createSltpEntries.push(stopLossEntry)
|
||||
}
|
||||
const stopLossEntry: SidecarSlTpOrderEntryValid = {
|
||||
decreaseAmounts: stopLossDecreaseAmounts,
|
||||
id: "sl-order",
|
||||
price: {
|
||||
input: params.stopLossPrice?.toString() ?? "",
|
||||
value: params.stopLossPrice,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
sizeUsd: {
|
||||
input: increaseAmounts.sizeDeltaUsd.toString(),
|
||||
value: increaseAmounts.sizeDeltaUsd,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
percentage: {
|
||||
input: "100",
|
||||
value: 100n,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
txnType: "create",
|
||||
mode: "keepSize",
|
||||
order: null,
|
||||
increaseAmounts: undefined
|
||||
}
|
||||
|
||||
let takeProfitDecreaseAmounts: DecreasePositionAmounts | undefined;
|
||||
if (params.takeProfitPrice) {
|
||||
const takeProfitCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.takeProfitPrice);
|
||||
createSltpEntries.push(stopLossEntry)
|
||||
}
|
||||
|
||||
// Use a higher acceptable price impact buffer for take profit orders to ensure execution
|
||||
const takeProfitAcceptablePriceImpactBps = params.fixedAcceptablePriceImpactBps ??
|
||||
getDefaultAcceptablePriceImpactBps({
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.takeProfitPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: 0n, // We'll calculate this based on current market conditions
|
||||
acceptablePriceImapctBuffer: params.acceptablePriceImpactBuffer ?? 100, // Default 1% buffer for TP orders
|
||||
});
|
||||
let takeProfitDecreaseAmounts: DecreasePositionAmounts | undefined;
|
||||
if (params.takeProfitPrice) {
|
||||
const takeProfitCollateralDeltaUsd = convertToUsd(increaseAmounts.collateralDeltaAmount, collateralToken.decimals, params.takeProfitPrice);
|
||||
|
||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.takeProfitPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
maxNegativePriceImpactBps: takeProfitAcceptablePriceImpactBps,
|
||||
});
|
||||
// Use a higher acceptable price impact buffer for take profit orders to ensure execution
|
||||
const takeProfitAcceptablePriceImpactBps = params.fixedAcceptablePriceImpactBps ??
|
||||
getDefaultAcceptablePriceImpactBps({
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.takeProfitPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: 0n, // We'll calculate this based on current market conditions
|
||||
acceptablePriceImapctBuffer: params.acceptablePriceImpactBuffer ?? 100, // Default 1% buffer for TP orders
|
||||
});
|
||||
|
||||
// Calculate collateral price based on GMX UI logic
|
||||
const takeProfitCollateralPrice = getIsEquivalentTokens(marketInfo.indexToken, collateralToken)
|
||||
? params.takeProfitPrice // Use trigger price if index token equals collateral token
|
||||
: collateralToken.prices.minPrice; // Otherwise use collateral token's min price
|
||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: false,
|
||||
isLong: params.isLong,
|
||||
indexPrice: params.takeProfitPrice,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
maxNegativePriceImpactBps: takeProfitAcceptablePriceImpactBps,
|
||||
isLimit: false,
|
||||
});
|
||||
|
||||
takeProfitDecreaseAmounts = {
|
||||
isFullClose: true,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
||||
collateralDeltaUsd: takeProfitCollateralDeltaUsd,
|
||||
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
||||
indexPrice: params.takeProfitPrice, // Keep original trigger price for indexPrice
|
||||
collateralPrice: takeProfitCollateralPrice,
|
||||
acceptablePrice: acceptablePriceInfo.acceptablePrice,
|
||||
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps, // Add 0.5% buffer to acceptable price impact
|
||||
recommendedAcceptablePriceDeltaBps: 30n, // Set recommended buffer to 0.5%
|
||||
estimatedPnl: 0n,
|
||||
estimatedPnlPercentage: 0n,
|
||||
realizedPnl: 0n,
|
||||
realizedPnlPercentage: 0n,
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
feeDiscountUsd: 0n,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
swapProfitFeeUsd: 0n,
|
||||
positionPriceImpactDeltaUsd: 0n,
|
||||
priceImpactDiffUsd: acceptablePriceInfo.priceImpactDeltaUsd,
|
||||
payedRemainingCollateralAmount: 0n,
|
||||
payedOutputUsd: 0n,
|
||||
payedRemainingCollateralUsd: 0n,
|
||||
receiveTokenAmount: 0n,
|
||||
receiveUsd: 0n,
|
||||
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
||||
triggerOrderType: OrderType.LimitDecrease,
|
||||
triggerPrice: params.takeProfitPrice,
|
||||
triggerThresholdType: getOrderThresholdType(OrderType.LimitDecrease, params.isLong),
|
||||
}
|
||||
// Calculate collateral price based on GMX UI logic
|
||||
const takeProfitCollateralPrice = getIsEquivalentTokens(marketInfo.indexToken, collateralToken)
|
||||
? params.takeProfitPrice // Use trigger price if index token equals collateral token
|
||||
: collateralToken.prices.minPrice; // Otherwise use collateral token's min price
|
||||
|
||||
const takeProfitEntry: SidecarSlTpOrderEntryValid = {
|
||||
decreaseAmounts: takeProfitDecreaseAmounts,
|
||||
id: "tp-order",
|
||||
price: {
|
||||
input: params.takeProfitPrice?.toString() ?? "",
|
||||
value: params.takeProfitPrice,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
sizeUsd: {
|
||||
input: increaseAmounts.sizeDeltaUsd.toString(),
|
||||
value: increaseAmounts.sizeDeltaUsd,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
percentage: {
|
||||
input: "100",
|
||||
value: 100n,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
txnType: "create",
|
||||
mode: "keepSize",
|
||||
order: null,
|
||||
increaseAmounts: undefined
|
||||
}
|
||||
takeProfitDecreaseAmounts = {
|
||||
isFullClose: true,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
||||
collateralDeltaUsd: takeProfitCollateralDeltaUsd,
|
||||
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
||||
indexPrice: params.takeProfitPrice, // Keep original trigger price for indexPrice
|
||||
collateralPrice: takeProfitCollateralPrice,
|
||||
acceptablePrice: acceptablePriceInfo.acceptablePrice,
|
||||
acceptablePriceDeltaBps: acceptablePriceInfo.acceptablePriceDeltaBps, // Add 0.5% buffer to acceptable price impact
|
||||
recommendedAcceptablePriceDeltaBps: 30n, // Set recommended buffer to 0.5%
|
||||
estimatedPnl: 0n,
|
||||
estimatedPnlPercentage: 0n,
|
||||
realizedPnl: 0n,
|
||||
realizedPnlPercentage: 0n,
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
feeDiscountUsd: 0n,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
swapProfitFeeUsd: 0n,
|
||||
proportionalPendingImpactDeltaUsd: 0n,
|
||||
closePriceImpactDeltaUsd: 0n,
|
||||
totalPendingImpactDeltaUsd: 0n,
|
||||
balanceWasImproved: false,
|
||||
priceImpactDiffUsd: acceptablePriceInfo.priceImpactDeltaUsd,
|
||||
payedRemainingCollateralAmount: 0n,
|
||||
payedOutputUsd: 0n,
|
||||
payedRemainingCollateralUsd: 0n,
|
||||
receiveTokenAmount: 0n,
|
||||
receiveUsd: 0n,
|
||||
decreaseSwapType: DecreasePositionSwapType.SwapPnlTokenToCollateralToken,
|
||||
triggerOrderType: OrderType.LimitDecrease,
|
||||
triggerPrice: params.takeProfitPrice,
|
||||
triggerThresholdType: getOrderThresholdType(OrderType.LimitDecrease, params.isLong),
|
||||
}
|
||||
|
||||
createSltpEntries.push(takeProfitEntry)
|
||||
}
|
||||
const takeProfitEntry: SidecarSlTpOrderEntryValid = {
|
||||
decreaseAmounts: takeProfitDecreaseAmounts,
|
||||
id: "tp-order",
|
||||
price: {
|
||||
input: params.takeProfitPrice?.toString() ?? "",
|
||||
value: params.takeProfitPrice,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
sizeUsd: {
|
||||
input: increaseAmounts.sizeDeltaUsd.toString(),
|
||||
value: increaseAmounts.sizeDeltaUsd,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
percentage: {
|
||||
input: "100",
|
||||
value: 100n,
|
||||
error: null,
|
||||
} as EntryField,
|
||||
txnType: "create",
|
||||
mode: "keepSize",
|
||||
order: null,
|
||||
increaseAmounts: undefined
|
||||
}
|
||||
|
||||
createSltpEntries.push(takeProfitEntry)
|
||||
}
|
||||
|
||||
const createIncreaseOrderParams: Parameters<typeof sdk.orders.createIncreaseOrder>[0] = {
|
||||
marketsInfoData,
|
||||
tokensData,
|
||||
isLimit,
|
||||
marketAddress: params.marketAddress,
|
||||
fromToken: tokensData[params.payTokenAddress],
|
||||
allowedSlippage,
|
||||
collateralToken,
|
||||
referralCodeForTxn: params.referralCodeForTxn,
|
||||
triggerPrice: params.limitPrice,
|
||||
collateralTokenAddress: collateralToken.address,
|
||||
isLong: params.isLong,
|
||||
receiveTokenAddress: collateralTokenAddress,
|
||||
indexToken: marketInfo.indexToken,
|
||||
marketInfo,
|
||||
skipSimulation: params.skipSimulation,
|
||||
increaseAmounts,
|
||||
createSltpEntries: createSltpEntries.length > 0 ? createSltpEntries : undefined,
|
||||
marketsInfoData,
|
||||
tokensData,
|
||||
isLimit,
|
||||
marketAddress: params.marketAddress,
|
||||
fromToken: tokensData[params.payTokenAddress],
|
||||
allowedSlippage,
|
||||
collateralToken,
|
||||
referralCodeForTxn: params.referralCodeForTxn,
|
||||
triggerPrice: params.limitPrice,
|
||||
collateralTokenAddress: collateralToken.address,
|
||||
isLong: params.isLong,
|
||||
receiveTokenAddress: collateralTokenAddress,
|
||||
indexToken: marketInfo.indexToken,
|
||||
marketInfo,
|
||||
skipSimulation: params.skipSimulation,
|
||||
increaseAmounts,
|
||||
createSltpEntries: createSltpEntries,
|
||||
};
|
||||
|
||||
return sdk.orders.createIncreaseOrder(createIncreaseOrderParams);
|
||||
}
|
||||
}
|
||||
|
||||
function getTriggerRatio({
|
||||
toToken,
|
||||
fromToken,
|
||||
triggerPrice,
|
||||
}: {
|
||||
function getTriggerRatio({
|
||||
toToken,
|
||||
fromToken,
|
||||
triggerPrice,
|
||||
}: {
|
||||
toToken: TokenData;
|
||||
fromToken: TokenData;
|
||||
triggerPrice: bigint;
|
||||
}) {
|
||||
}) {
|
||||
const fromTokenPrice = fromToken?.prices.minPrice;
|
||||
const markPrice = toToken.prices.minPrice;
|
||||
|
||||
const markRatio = getTokensRatioByPrice({
|
||||
fromToken,
|
||||
toToken,
|
||||
fromPrice: fromTokenPrice,
|
||||
toPrice: markPrice,
|
||||
fromToken,
|
||||
toToken,
|
||||
fromPrice: fromTokenPrice,
|
||||
toPrice: markPrice,
|
||||
});
|
||||
|
||||
const triggerRatio: TokensRatio = {
|
||||
ratio: triggerPrice > 0 ? triggerPrice : markRatio.ratio,
|
||||
largestToken: markRatio.largestToken,
|
||||
smallestToken: markRatio.smallestToken,
|
||||
ratio: triggerPrice > 0 ? triggerPrice : markRatio.ratio,
|
||||
largestToken: markRatio.largestToken,
|
||||
smallestToken: markRatio.smallestToken,
|
||||
};
|
||||
|
||||
return triggerRatio;
|
||||
}
|
||||
}
|
||||
|
||||
export type SwapParams = (
|
||||
export type SwapParams = (
|
||||
| {
|
||||
fromAmount: bigint;
|
||||
}
|
||||
fromAmount: bigint;
|
||||
}
|
||||
| {
|
||||
toAmount: bigint;
|
||||
}
|
||||
) & {
|
||||
toAmount: bigint;
|
||||
}
|
||||
) & {
|
||||
fromTokenAddress: string;
|
||||
toTokenAddress: string;
|
||||
allowedSlippageBps?: number;
|
||||
@@ -417,126 +465,126 @@ export type SwapParams = (
|
||||
|
||||
/** If presented, then it's limit swap order */
|
||||
triggerPrice?: bigint;
|
||||
} & BaseOptionalParams;
|
||||
} & BaseOptionalParams;
|
||||
|
||||
export async function swap(sdk: GmxSdk, params: SwapParams) {
|
||||
const {tokensData, marketsInfoData, uiFeeFactor} = await getAndValidateBaseParams(sdk, params);
|
||||
export async function swap(sdk: GmxSdk, params: SwapParams) {
|
||||
const { tokensData, marketsInfoData, uiFeeFactor, gasLimits, gasPrice } = await getAndValidateBaseParams(sdk, params);
|
||||
|
||||
const fromToken = tokensData[params.fromTokenAddress];
|
||||
const toToken = tokensData[params.toTokenAddress];
|
||||
|
||||
if (!fromToken || !toToken) {
|
||||
throw new Error("From or to token is not available");
|
||||
throw new Error("From or to token is not available");
|
||||
}
|
||||
|
||||
if (toToken.isSynthetic) {
|
||||
throw new Error(`Synthetic tokens are not supported: ${toToken.symbol}`);
|
||||
}
|
||||
|
||||
if (fromToken.isSynthetic) {
|
||||
throw new Error(`Synthetic tokens are not supported: ${fromToken.symbol}`);
|
||||
}
|
||||
|
||||
const isLimit = Boolean(params.triggerPrice);
|
||||
|
||||
if (!fromToken || !toToken) {
|
||||
return undefined;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const graph = getMarketsGraph(Object.values(marketsInfoData));
|
||||
const wrappedFromAddress = getWrappedAddress(sdk.chainId, params.fromTokenAddress);
|
||||
const wrappedToAddress = getWrappedAddress(sdk.chainId, params.toTokenAddress);
|
||||
|
||||
const allPaths = findAllSwapPaths({
|
||||
chainId: sdk.chainId,
|
||||
fromTokenAddress: params.fromTokenAddress,
|
||||
toTokenAddress: params.toTokenAddress,
|
||||
marketsInfoData,
|
||||
graph,
|
||||
wrappedFromAddress,
|
||||
wrappedToAddress,
|
||||
});
|
||||
|
||||
|
||||
const estimator = createSwapEstimator(marketsInfoData);
|
||||
|
||||
const findSwapPath = createFindSwapPath({
|
||||
chainId: sdk.chainId,
|
||||
fromTokenAddress: params.fromTokenAddress,
|
||||
toTokenAddress: params.toTokenAddress,
|
||||
marketsInfoData,
|
||||
estimator,
|
||||
allPaths,
|
||||
chainId: sdk.chainId,
|
||||
fromTokenAddress: params.fromTokenAddress,
|
||||
toTokenAddress: params.toTokenAddress,
|
||||
marketsInfoData,
|
||||
gasEstimationParams: {
|
||||
gasLimits,
|
||||
gasPrice,
|
||||
tokensData,
|
||||
},
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const isWrapOrUnwrap = Boolean(
|
||||
fromToken && toToken && (getIsWrap(fromToken, toToken) || getIsUnwrap(fromToken, toToken))
|
||||
fromToken && toToken && (getIsWrap(fromToken, toToken) || getIsUnwrap(fromToken, toToken))
|
||||
);
|
||||
|
||||
if (isWrapOrUnwrap) {
|
||||
const fromTokenPrice = fromToken.prices.minPrice;
|
||||
const tokenAmount = "fromAmount" in params ? params.fromAmount : params.toAmount;
|
||||
const usdAmount = convertToUsd(tokenAmount, fromToken.decimals, fromTokenPrice)!;
|
||||
const price = fromTokenPrice;
|
||||
|
||||
const swapOptimizationOrder: Parameters<FindSwapPath>[1]["order"] = isLimit ? ["length", "liquidity"] : undefined;
|
||||
return {
|
||||
amountIn: tokenAmount,
|
||||
usdIn: usdAmount!,
|
||||
amountOut: tokenAmount,
|
||||
usdOut: usdAmount!,
|
||||
swapPathStats: undefined,
|
||||
priceIn: price,
|
||||
priceOut: price,
|
||||
minOutputAmount: tokenAmount,
|
||||
};
|
||||
}
|
||||
|
||||
const swapOptimizationOrder: SwapOptimizationOrderArray | undefined = isLimit ? ["length", "liquidity"] : undefined;
|
||||
|
||||
let swapAmounts: SwapAmounts | undefined;
|
||||
|
||||
const fromTokenPrice = fromToken.prices.minPrice;
|
||||
const triggerRatio = params.triggerPrice
|
||||
? getTriggerRatio({
|
||||
fromToken,
|
||||
toToken,
|
||||
triggerPrice: params.triggerPrice,
|
||||
? getTriggerRatio({
|
||||
fromToken,
|
||||
toToken,
|
||||
triggerPrice: params.triggerPrice,
|
||||
})
|
||||
: undefined;
|
||||
: undefined;
|
||||
|
||||
if (isWrapOrUnwrap) {
|
||||
const tokenAmount = "fromAmount" in params ? params.fromAmount : params.toAmount;
|
||||
const usdAmount = convertToUsd(tokenAmount, fromToken.decimals, fromTokenPrice)!;
|
||||
const price = fromTokenPrice;
|
||||
|
||||
swapAmounts = {
|
||||
amountIn: tokenAmount,
|
||||
usdIn: usdAmount!,
|
||||
amountOut: tokenAmount,
|
||||
usdOut: usdAmount!,
|
||||
swapPathStats: undefined,
|
||||
priceIn: price,
|
||||
priceOut: price,
|
||||
minOutputAmount: tokenAmount,
|
||||
};
|
||||
|
||||
return swapAmounts;
|
||||
} else if ("fromAmount" in params) {
|
||||
swapAmounts = getSwapAmountsByFromValue({
|
||||
tokenIn: fromToken,
|
||||
tokenOut: toToken,
|
||||
amountIn: params.fromAmount,
|
||||
triggerRatio,
|
||||
isLimit,
|
||||
findSwapPath: findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
||||
});
|
||||
if ("fromAmount" in params) {
|
||||
swapAmounts = getSwapAmountsByFromValue({
|
||||
tokenIn: fromToken,
|
||||
tokenOut: toToken,
|
||||
amountIn: params.fromAmount,
|
||||
triggerRatio,
|
||||
isLimit,
|
||||
findSwapPath: findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
||||
marketsInfoData,
|
||||
chainId: sdk.chainId,
|
||||
externalSwapQuoteParams: undefined,
|
||||
});
|
||||
} else {
|
||||
swapAmounts = getSwapAmountsByToValue({
|
||||
tokenIn: fromToken,
|
||||
tokenOut: toToken,
|
||||
amountOut: params.toAmount,
|
||||
triggerRatio,
|
||||
isLimit: isLimit,
|
||||
findSwapPath: findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
||||
});
|
||||
swapAmounts = getSwapAmountsByToValue({
|
||||
tokenIn: fromToken,
|
||||
tokenOut: toToken,
|
||||
amountOut: params.toAmount,
|
||||
triggerRatio,
|
||||
isLimit: isLimit,
|
||||
findSwapPath: findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
allowedSwapSlippageBps: isLimit ? BigInt(params.allowedSlippageBps ?? 100) : undefined,
|
||||
marketsInfoData,
|
||||
chainId: sdk.chainId,
|
||||
externalSwapQuoteParams: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
if (!swapAmounts) {
|
||||
return undefined;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const createSwapOrderParams: Parameters<typeof sdk.orders.createSwapOrder>[0] = {
|
||||
tokensData,
|
||||
fromToken: tokensData[params.fromTokenAddress],
|
||||
toToken: tokensData[params.toTokenAddress],
|
||||
swapAmounts,
|
||||
isLimit,
|
||||
allowedSlippage: params.allowedSlippageBps ?? 100,
|
||||
referralCodeForTxn: params.referralCodeForTxn,
|
||||
triggerPrice: params.triggerPrice,
|
||||
skipSimulation: params.skipSimulation,
|
||||
tokensData,
|
||||
fromToken: tokensData[params.fromTokenAddress],
|
||||
toToken: tokensData[params.toTokenAddress],
|
||||
swapAmounts,
|
||||
isLimit,
|
||||
allowedSlippage: params.allowedSlippageBps ?? 100,
|
||||
referralCodeForTxn: params.referralCodeForTxn,
|
||||
triggerPrice: params.triggerPrice,
|
||||
};
|
||||
|
||||
return sdk.orders.createSwapOrder(createSwapOrderParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +220,8 @@ export class Orders extends Module {
|
||||
indexToken,
|
||||
};
|
||||
|
||||
const swapPath = increaseAmounts.swapStrategy.swapPathStats?.swapPath || [];
|
||||
|
||||
return createIncreaseOrderTxn({
|
||||
sdk: this.sdk,
|
||||
createIncreaseOrderParams: {
|
||||
@@ -229,7 +231,7 @@ export class Orders extends Module {
|
||||
initialCollateralAmount: increaseAmounts.initialCollateralAmount,
|
||||
targetCollateralAddress: collateralToken.address,
|
||||
collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
|
||||
swapPath: increaseAmounts.swapPathStats?.swapPath || [],
|
||||
swapPath: swapPath,
|
||||
sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
|
||||
sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
|
||||
triggerPrice: isLimit ? triggerPrice : undefined,
|
||||
@@ -375,7 +377,6 @@ export class Orders extends Module {
|
||||
referralCodeForTxn,
|
||||
tokensData,
|
||||
triggerPrice,
|
||||
skipSimulation,
|
||||
}: {
|
||||
isLimit: boolean;
|
||||
allowedSlippage: number;
|
||||
@@ -385,7 +386,6 @@ export class Orders extends Module {
|
||||
toToken: TokenData;
|
||||
tokensData: TokensData;
|
||||
triggerPrice?: bigint;
|
||||
skipSimulation?: boolean;
|
||||
}) {
|
||||
const orderType = isLimit ? OrderType.LimitSwap : OrderType.MarketSwap;
|
||||
|
||||
@@ -393,19 +393,14 @@ export class Orders extends Module {
|
||||
swapAmounts,
|
||||
});
|
||||
|
||||
if (!swapAmounts?.swapPathStats || !executionFee) {
|
||||
if (!swapAmounts?.swapStrategy.swapPathStats || !executionFee) {
|
||||
throw new Error("Swap data is not defined");
|
||||
}
|
||||
|
||||
// Validate that swapPathStats has a valid swapPath array
|
||||
if (!swapAmounts.swapPathStats.swapPath || !Array.isArray(swapAmounts.swapPathStats.swapPath)) {
|
||||
throw new Error("Invalid swap path: swapPath is not a valid array");
|
||||
}
|
||||
|
||||
return createSwapOrderTxn(this.sdk, {
|
||||
fromTokenAddress: fromToken.address,
|
||||
fromTokenAmount: swapAmounts.amountIn,
|
||||
swapPath: swapAmounts.swapPathStats.swapPath,
|
||||
swapPath: swapAmounts.swapStrategy.swapPathStats?.swapPath,
|
||||
toTokenAddress: toToken.address,
|
||||
orderType,
|
||||
minOutputAmount: swapAmounts.minOutputAmount,
|
||||
@@ -414,7 +409,6 @@ export class Orders extends Module {
|
||||
allowedSlippage,
|
||||
tokensData,
|
||||
triggerPrice: isLimit && triggerPrice !== undefined ? triggerPrice : undefined,
|
||||
skipSimulation: skipSimulation,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ export function createDecreaseEncodedPayload({
|
||||
initialCollateralToken: initialCollateralTokenAddress,
|
||||
callbackContract: zeroAddress,
|
||||
market: p.marketAddress,
|
||||
swapPath: p.swapPath,
|
||||
swapPath: p.swapPath || [],
|
||||
uiFeeReceiver: process.env.GMX_UI_FEE_RECEIVER,
|
||||
},
|
||||
numbers: {
|
||||
@@ -132,6 +132,7 @@ export function createDecreaseEncodedPayload({
|
||||
shouldUnwrapNativeToken: isNativeReceive,
|
||||
autoCancel: p.autoCancel,
|
||||
referralCode: p.referralCode || zeroHash,
|
||||
dataList: [],
|
||||
};
|
||||
|
||||
return [
|
||||
@@ -144,11 +145,15 @@ export function createDecreaseEncodedPayload({
|
||||
}),
|
||||
];
|
||||
|
||||
return multicall.filter(Boolean).map((call) =>
|
||||
encodeFunctionData({
|
||||
return multicall.filter(Boolean).map((call) => {
|
||||
// Ensure params is always an array
|
||||
const params = Array.isArray(call!.params) ? call!.params : [];
|
||||
console.log(`Encoding ${call!.method} with params:`, params);
|
||||
|
||||
return encodeFunctionData({
|
||||
abi: abis.ExchangeRouter as Abi,
|
||||
functionName: call!.method,
|
||||
args: call!.params as any,
|
||||
})
|
||||
);
|
||||
functionName: call!.method as any,
|
||||
args: params as any,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ export async function createIncreaseOrderTxn({
|
||||
acceptablePrice,
|
||||
isNativePayment,
|
||||
initialCollateralTokenAddress,
|
||||
uiFeeReceiver: sdk.config.settings?.uiFeeReceiverAccount,
|
||||
});
|
||||
|
||||
const simulationEncodedPayload = await createEncodedPayload({
|
||||
@@ -123,6 +124,7 @@ export async function createIncreaseOrderTxn({
|
||||
acceptablePrice,
|
||||
isNativePayment,
|
||||
initialCollateralTokenAddress,
|
||||
uiFeeReceiver: sdk.config.settings?.uiFeeReceiverAccount,
|
||||
});
|
||||
|
||||
const decreaseEncodedPayload = createDecreaseEncodedPayload({
|
||||
@@ -189,6 +191,7 @@ async function createEncodedPayload({
|
||||
acceptablePrice,
|
||||
isNativePayment,
|
||||
initialCollateralTokenAddress,
|
||||
uiFeeReceiver,
|
||||
}: {
|
||||
routerAbi: Abi;
|
||||
orderVaultAddress: string;
|
||||
@@ -197,13 +200,16 @@ async function createEncodedPayload({
|
||||
acceptablePrice: bigint;
|
||||
isNativePayment: boolean;
|
||||
initialCollateralTokenAddress: string;
|
||||
uiFeeReceiver: string | undefined;
|
||||
}) {
|
||||
const orderParams = createOrderParams({
|
||||
p,
|
||||
acceptablePrice,
|
||||
initialCollateralTokenAddress,
|
||||
isNativePayment,
|
||||
uiFeeReceiver,
|
||||
});
|
||||
|
||||
const multicall = [
|
||||
{ method: "sendWnt", params: [orderVaultAddress, totalWntAmount] },
|
||||
|
||||
@@ -216,13 +222,17 @@ async function createEncodedPayload({
|
||||
params: [orderParams],
|
||||
},
|
||||
];
|
||||
return multicall.filter(Boolean).map((call) =>
|
||||
encodeFunctionData({
|
||||
const validCalls = multicall.filter(Boolean) as Array<{ method: string; params: any[] }>;
|
||||
return validCalls.map((call) => {
|
||||
// Ensure params is always an array
|
||||
const params = Array.isArray(call.params) ? call.params : [];
|
||||
console.log(`Encoding ${call.method} with params:`, params);
|
||||
return encodeFunctionData({
|
||||
abi: routerAbi,
|
||||
functionName: call!.method,
|
||||
args: call!.params as any,
|
||||
})
|
||||
);
|
||||
functionName: call.method as any,
|
||||
args: params,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createOrderParams({
|
||||
@@ -230,11 +240,13 @@ function createOrderParams({
|
||||
acceptablePrice,
|
||||
initialCollateralTokenAddress,
|
||||
isNativePayment,
|
||||
uiFeeReceiver,
|
||||
}: {
|
||||
p: IncreaseOrderParams;
|
||||
acceptablePrice: bigint;
|
||||
initialCollateralTokenAddress: string;
|
||||
isNativePayment: boolean;
|
||||
uiFeeReceiver: string | undefined;
|
||||
}) {
|
||||
return {
|
||||
addresses: {
|
||||
@@ -243,8 +255,8 @@ function createOrderParams({
|
||||
initialCollateralToken: initialCollateralTokenAddress,
|
||||
callbackContract: zeroAddress,
|
||||
market: p.marketAddress,
|
||||
swapPath: p.swapPath,
|
||||
uiFeeReceiver: process.env.GMX_UI_FEE_RECEIVER,
|
||||
swapPath: p.swapPath || [],
|
||||
uiFeeReceiver: uiFeeReceiver || zeroAddress,
|
||||
},
|
||||
numbers: {
|
||||
sizeDeltaUsd: p.sizeDeltaUsd,
|
||||
@@ -262,6 +274,7 @@ function createOrderParams({
|
||||
shouldUnwrapNativeToken: isNativePayment,
|
||||
autoCancel: false,
|
||||
referralCode: p.referralCode || zeroHash,
|
||||
dataList: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -275,6 +288,7 @@ export function getPendingOrderFromParams(
|
||||
const shouldApplySlippage = isMarketOrderType(p.orderType);
|
||||
let minOutputAmount = 0n;
|
||||
if ("minOutputUsd" in p) {
|
||||
// eslint-disable-next-line
|
||||
shouldApplySlippage ? applySlippageToMinOut(p.allowedSlippage, p.minOutputUsd) : p.minOutputUsd;
|
||||
}
|
||||
if ("minOutputAmount" in p) {
|
||||
|
||||
@@ -32,6 +32,7 @@ export async function createSwapOrderTxn(sdk: GmxSdk, p: SwapOrderParams) {
|
||||
const { encodedPayload, totalWntAmount } = await getParams(sdk, p);
|
||||
const { encodedPayload: simulationEncodedPayload, totalWntAmount: sumaltionTotalWntAmount } = await getParams(sdk, p);
|
||||
|
||||
p.skipSimulation = true;
|
||||
if (p.orderType !== OrderType.LimitSwap && !p.skipSimulation) {
|
||||
await simulateExecuteOrder(sdk, {
|
||||
primaryPriceOverrides: {},
|
||||
@@ -41,6 +42,7 @@ export async function createSwapOrderTxn(sdk: GmxSdk, p: SwapOrderParams) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
await sdk.callContract(
|
||||
getContract(sdk.chainId, "ExchangeRouter"),
|
||||
abis.ExchangeRouter as Abi,
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
|
||||
import { Address, isAddressEqual } from "viem";
|
||||
import { GmxSdk } from "../../index.js";
|
||||
import { accountOrderListKey } from "../../configs/dataStore.js";
|
||||
import { getWrappedToken } from "../../configs/tokens.js";
|
||||
import { GasLimitsConfig } from "../../types/fees.js";
|
||||
import { MarketsInfoData } from "../../types/markets.js";
|
||||
import { DecreasePositionSwapType, Order, OrderType } from "../../types/orders.js";
|
||||
import { SidecarSlTpOrderEntry, SidecarLimitOrderEntry } from "../../types/sidecarOrders";
|
||||
import { TokensData } from "../../types/tokens.js";
|
||||
import { estimateExecuteDecreaseOrderGasLimit, estimateOrderOraclePriceCount, getExecutionFee } from "../../utils/fees/index.js";
|
||||
import { isSwapOrderType, isLimitOrderType, isIncreaseOrderType, isTriggerDecreaseOrderType } from "../../utils/orders.js";
|
||||
import { getSwapPathOutputAddresses } from "../../utils/swap/index.js";
|
||||
import { MarketFilterLongShortItemData, MarketFilterLongShortDirection } from "../trades/trades.js";
|
||||
import { MulticallRequestConfig, MulticallResult } from "../../utils/multicall.js";
|
||||
import { getContract } from "../../configs/contracts.js";
|
||||
import {Address, isAddressEqual} from "viem";
|
||||
import {GmxSdk} from "../../index.js";
|
||||
import {accountOrderListKey} from "../../configs/dataStore.js";
|
||||
import {getWrappedToken} from "../../configs/tokens.js";
|
||||
import {GasLimitsConfig} from "../../types/fees.js";
|
||||
import {MarketsInfoData} from "../../types/markets.js";
|
||||
import {DecreasePositionSwapType, Order, OrderType} from "../../types/orders.js";
|
||||
import {SidecarLimitOrderEntry, SidecarSlTpOrderEntry} from "../../types/sidecarOrders";
|
||||
import {TokensData} from "../../types/tokens.js";
|
||||
import {
|
||||
estimateExecuteDecreaseOrderGasLimit,
|
||||
estimateOrderOraclePriceCount,
|
||||
getExecutionFee
|
||||
} from "../../utils/fees/index.js";
|
||||
import {
|
||||
isIncreaseOrderType,
|
||||
isLimitOrderType,
|
||||
isSwapOrderType,
|
||||
isTriggerDecreaseOrderType
|
||||
} from "../../utils/orders.js";
|
||||
import {getSwapPathOutputAddresses} from "../../utils/swap/index.js";
|
||||
import {MarketFilterLongShortDirection, MarketFilterLongShortItemData} from "../trades/trades.js";
|
||||
import {MulticallRequestConfig, MulticallResult} from "../../utils/multicall.js";
|
||||
import {getContract} from "../../configs/contracts.js";
|
||||
import {ContractsChainId} from "../../configs/chains.js";
|
||||
|
||||
export const getOrderExecutionFee = (
|
||||
sdk: GmxSdk,
|
||||
@@ -152,7 +161,7 @@ export function matchByMarket({
|
||||
|
||||
export const DEFAULT_COUNT = 1000;
|
||||
|
||||
export function buildGetOrdersMulticall(chainId: number, account: string) {
|
||||
export function buildGetOrdersMulticall(chainId: ContractsChainId, account: string) {
|
||||
return {
|
||||
dataStore: {
|
||||
contractAddress: getContract(chainId, "DataStore"),
|
||||
@@ -184,13 +193,42 @@ export function buildGetOrdersMulticall(chainId: number, account: string) {
|
||||
export function parseGetOrdersResponse(res: MulticallResult<ReturnType<typeof buildGetOrdersMulticall>>) {
|
||||
const count = Number(res.data.dataStore.count.returnValues[0]);
|
||||
const orderKeys = res.data.dataStore.keys.returnValues;
|
||||
const orders = res.data.reader.orders.returnValues as any[];
|
||||
const orders = res.data.reader.orders.returnValues as {
|
||||
orderKey: string;
|
||||
order: {
|
||||
addresses: {
|
||||
account: string;
|
||||
receiver: string;
|
||||
cancellationReceiver: string;
|
||||
callbackContract: string;
|
||||
uiFeeReceiver: string;
|
||||
market: string;
|
||||
initialCollateralToken: string;
|
||||
swapPath: string[];
|
||||
};
|
||||
numbers: {
|
||||
orderType: bigint;
|
||||
decreasePositionSwapType: bigint;
|
||||
sizeDeltaUsd: bigint;
|
||||
initialCollateralDeltaAmount: bigint;
|
||||
triggerPrice: bigint;
|
||||
acceptablePrice: bigint;
|
||||
executionFee: bigint;
|
||||
callbackGasLimit: bigint;
|
||||
minOutputAmount: bigint;
|
||||
updatedAtTime: bigint;
|
||||
validFromTime: bigint;
|
||||
srcChainId: bigint;
|
||||
};
|
||||
flags: { isLong: boolean; shouldUnwrapNativeToken: boolean; isFrozen: boolean; autoCancel: boolean };
|
||||
_dataList: string[];
|
||||
};
|
||||
}[];
|
||||
|
||||
return {
|
||||
count,
|
||||
orders: orders.map((order, i) => {
|
||||
orders: orders.map(({ order }, i) => {
|
||||
const key = orderKeys[i];
|
||||
const { data } = order;
|
||||
|
||||
const orderData: Order = {
|
||||
key,
|
||||
@@ -211,10 +249,12 @@ export function parseGetOrdersResponse(res: MulticallResult<ReturnType<typeof bu
|
||||
isLong: order.flags.isLong as boolean,
|
||||
shouldUnwrapNativeToken: order.flags.shouldUnwrapNativeToken as boolean,
|
||||
isFrozen: order.flags.isFrozen as boolean,
|
||||
orderType: order.numbers.orderType as OrderType,
|
||||
decreasePositionSwapType: order.numbers.decreasePositionSwapType as DecreasePositionSwapType,
|
||||
orderType: Number(order.numbers.orderType) as OrderType,
|
||||
decreasePositionSwapType: Number(order.numbers.decreasePositionSwapType) as DecreasePositionSwapType,
|
||||
autoCancel: order.flags.autoCancel as boolean,
|
||||
data,
|
||||
uiFeeReceiver: order.addresses.uiFeeReceiver as Address,
|
||||
validFromTime: BigInt(order.numbers.validFromTime),
|
||||
data: order._dataList,
|
||||
};
|
||||
|
||||
return orderData;
|
||||
|
||||
@@ -1,43 +1,46 @@
|
||||
import { zeroAddress, zeroHash } from "viem";
|
||||
import {zeroAddress, zeroHash} from "viem";
|
||||
|
||||
import { getContract } from "../../configs/contracts.js";
|
||||
import {getContract} from "../../configs/contracts.js";
|
||||
import {
|
||||
hashedPositionKey,
|
||||
MAX_AUTO_CANCEL_ORDERS_KEY,
|
||||
MIN_COLLATERAL_USD_KEY,
|
||||
MIN_POSITION_SIZE_USD_KEY,
|
||||
uiFeeFactorKey,
|
||||
hashedPositionKey,
|
||||
MAX_AUTO_CANCEL_ORDERS_KEY,
|
||||
MIN_COLLATERAL_USD_KEY,
|
||||
MIN_POSITION_SIZE_USD_KEY,
|
||||
uiFeeFactorKey,
|
||||
} from "../../configs/dataStore.js";
|
||||
import { ContractMarketPrices, MarketsData, MarketsInfoData } from "../../types/markets.js";
|
||||
import { Position, PositionsData, PositionsInfoData } from "../../types/positions.js";
|
||||
import { TokensData } from "../../types/tokens.js";
|
||||
import {ContractMarketPrices, MarketsData, MarketsInfoData} from "../../types/markets.js";
|
||||
import {Position, PositionsData, PositionsInfoData} from "../../types/positions.js";
|
||||
import {TokensData} from "../../types/tokens.js";
|
||||
import {
|
||||
getContractMarketPrices,
|
||||
getMarketIndexName,
|
||||
getMarketPoolName,
|
||||
getMaxAllowedLeverageByMinCollateralFactor,
|
||||
getContractMarketPrices,
|
||||
getMarketIndexName,
|
||||
getMarketPoolName,
|
||||
getMaxAllowedLeverageByMinCollateralFactor,
|
||||
} from "../../utils/markets.js";
|
||||
import { getByKey } from "../../utils/objects.js";
|
||||
import {getByKey} from "../../utils/objects.js";
|
||||
import {
|
||||
getEntryPrice,
|
||||
getLeverage,
|
||||
getLiquidationPrice,
|
||||
getPositionKey,
|
||||
getPositionNetValue,
|
||||
getPositionPnlUsd,
|
||||
getEntryPrice,
|
||||
getLeverage,
|
||||
getLiquidationPrice,
|
||||
getNetPriceImpactDeltaUsdForDecrease,
|
||||
getPositionKey,
|
||||
getPositionNetValue,
|
||||
getPositionPendingFeesUsd,
|
||||
getPositionPnlAfterFees,
|
||||
getPositionPnlUsd,
|
||||
} from "../../utils/positions.js";
|
||||
|
||||
import { Module } from "../base.js";
|
||||
import {Module} from "../base.js";
|
||||
|
||||
import { UserReferralInfo } from "../../types/referrals.js";
|
||||
import { getPositionFee, getPriceImpactForPosition } from "../../utils/fees/index.js";
|
||||
import { basisPointsToFloat, getBasisPoints } from "../../utils/numbers.js";
|
||||
import { getPositionPendingFeesUsd } from "../../utils/positions.js";
|
||||
import { getMarkPrice } from "../../utils/prices.js";
|
||||
import { decodeReferralCode } from "../../utils/referrals.js";
|
||||
import { convertToTokenAmount, convertToUsd } from "../../utils/tokens.js";
|
||||
import { OrderInfo } from "../../types/orders.js";
|
||||
import type { MulticallRequestConfig } from "../../utils/multicall.js";
|
||||
import {UserReferralInfo} from "../../types/referrals.js";
|
||||
import {getPositionFee} from "../../utils/fees/index.js";
|
||||
import {basisPointsToFloat, getBasisPoints} from "../../utils/numbers.js";
|
||||
import {getAcceptablePriceInfo, getMarkPrice} from "../../utils/prices.js";
|
||||
import {decodeReferralCode} from "../../utils/referrals.js";
|
||||
import {convertToTokenAmount, convertToUsd} from "../../utils/tokens.js";
|
||||
import {OrderInfo} from "../../types/orders.js";
|
||||
import type {MulticallRequestConfig} from "../../utils/multicall.js";
|
||||
import {BOTANIX} from "../../configs/chains.js";
|
||||
|
||||
type PositionsResult = {
|
||||
positionsData?: PositionsData;
|
||||
@@ -158,7 +161,7 @@ export class Positions extends Module {
|
||||
const positions = await this.sdk.executeMulticall(request).then((res) => {
|
||||
const positions = res.data.reader.positions.returnValues;
|
||||
|
||||
return positions.reduce((positionsMap: PositionsData, positionInfo) => {
|
||||
return positions.reduce((positionsMap: PositionsData, positionInfo: any) => {
|
||||
const { position, fees, basePnlUsd } = positionInfo;
|
||||
const { addresses, numbers, flags, data } = position;
|
||||
const { account, market: marketAddress, collateralToken: collateralTokenAddress } = addresses;
|
||||
@@ -182,6 +185,7 @@ export class Positions extends Module {
|
||||
collateralAmount: numbers.collateralAmount,
|
||||
increasedAtTime: numbers.increasedAtTime,
|
||||
decreasedAtTime: numbers.decreasedAtTime,
|
||||
pendingImpactAmount: numbers.pendingImpactAmount,
|
||||
isLong: flags.isLong,
|
||||
pendingBorrowingFeesUsd: fees.borrowing.borrowingFeeUsd,
|
||||
fundingFeeAmount: fees.funding.fundingFeeAmount,
|
||||
@@ -331,6 +335,15 @@ export class Positions extends Module {
|
||||
}
|
||||
|
||||
private async getUserRefferalCode() {
|
||||
if (this.chainId === BOTANIX) {
|
||||
return {
|
||||
attachedOnChain: false,
|
||||
userReferralCode: undefined,
|
||||
userReferralCodeString: undefined,
|
||||
referralCodeForTxn: zeroHash,
|
||||
};
|
||||
}
|
||||
|
||||
const referralStorageAddress = getContract(this.chainId, "ReferralStorage");
|
||||
|
||||
const onChainCode = await this.sdk.executeMulticall({
|
||||
@@ -531,6 +544,7 @@ export class Positions extends Module {
|
||||
marketInfo.longToken.decimals,
|
||||
marketInfo.longToken.prices.minPrice
|
||||
)!;
|
||||
|
||||
const pendingClaimableFundingFeesShortUsd = convertToUsd(
|
||||
position.claimableShortTokenAmount,
|
||||
marketInfo.shortToken.decimals,
|
||||
@@ -544,17 +558,21 @@ export class Positions extends Module {
|
||||
pendingFundingFeesUsd,
|
||||
});
|
||||
|
||||
const closingPriceImpactDeltaUsd = getPriceImpactForPosition(
|
||||
marketInfo,
|
||||
position.sizeInUsd * -1n,
|
||||
position.isLong,
|
||||
{ fallbackToZero: true }
|
||||
);
|
||||
const closeAcceptablePriceInfo = marketInfo
|
||||
? getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: false,
|
||||
isLimit: false,
|
||||
isLong: position.isLong,
|
||||
indexPrice: getMarkPrice({ prices: indexToken.prices, isLong: position.isLong, isIncrease: false }),
|
||||
sizeDeltaUsd: position.sizeInUsd,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const positionFeeInfo = getPositionFee(
|
||||
marketInfo,
|
||||
position.sizeInUsd,
|
||||
closingPriceImpactDeltaUsd > 0,
|
||||
closeAcceptablePriceInfo?.balanceWasImproved ?? false,
|
||||
userReferralInfo,
|
||||
uiFeeFactor
|
||||
);
|
||||
@@ -583,6 +601,17 @@ export class Positions extends Module {
|
||||
const pnlPercentage =
|
||||
collateralUsd !== undefined && collateralUsd != 0n ? getBasisPoints(pnl, collateralUsd) : 0n;
|
||||
|
||||
const netPriceImapctValues =
|
||||
marketInfo && closeAcceptablePriceInfo
|
||||
? getNetPriceImpactDeltaUsdForDecrease({
|
||||
marketInfo,
|
||||
sizeInUsd: position.sizeInUsd,
|
||||
pendingImpactAmount: position.pendingImpactAmount,
|
||||
sizeDeltaUsd: position.sizeInUsd,
|
||||
priceImpactDeltaUsd: closeAcceptablePriceInfo.priceImpactDeltaUsd,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const netValue = getPositionNetValue({
|
||||
collateralUsd: collateralUsd,
|
||||
pnl,
|
||||
@@ -590,9 +619,20 @@ export class Positions extends Module {
|
||||
pendingFundingFeesUsd: pendingFundingFeesUsd,
|
||||
closingFeeUsd,
|
||||
uiFeeUsd,
|
||||
totalPendingImpactDeltaUsd: netPriceImapctValues?.totalImpactDeltaUsd ?? 0n,
|
||||
priceImpactDiffUsd: netPriceImapctValues?.priceImpactDiffUsd ?? 0n,
|
||||
});
|
||||
|
||||
const pnlAfterFees = getPositionPnlAfterFees({
|
||||
pnl,
|
||||
pendingBorrowingFeesUsd: position.pendingBorrowingFeesUsd,
|
||||
pendingFundingFeesUsd: pendingFundingFeesUsd,
|
||||
closingFeeUsd,
|
||||
uiFeeUsd,
|
||||
totalPendingImpactDeltaUsd: netPriceImapctValues?.totalImpactDeltaUsd ?? 0n,
|
||||
priceImpactDiffUsd: netPriceImapctValues?.priceImpactDiffUsd ?? 0n,
|
||||
});
|
||||
|
||||
const pnlAfterFees = pnl - totalPendingFeesUsd - closingFeeUsd - uiFeeUsd;
|
||||
const pnlAfterFeesPercentage =
|
||||
collateralUsd != 0n ? getBasisPoints(pnlAfterFees, collateralUsd + closingFeeUsd) : 0n;
|
||||
|
||||
@@ -623,6 +663,7 @@ export class Positions extends Module {
|
||||
sizeInTokens: position.sizeInTokens,
|
||||
collateralUsd,
|
||||
collateralAmount: position.collateralAmount,
|
||||
pendingImpactAmount: position.pendingImpactAmount,
|
||||
userReferralInfo,
|
||||
minCollateralUsd,
|
||||
pendingBorrowingFeesUsd: position.pendingBorrowingFeesUsd,
|
||||
@@ -658,6 +699,10 @@ export class Positions extends Module {
|
||||
pnlAfterFees,
|
||||
pnlAfterFeesPercentage,
|
||||
netValue,
|
||||
netPriceImapctDeltaUsd: netPriceImapctValues?.totalImpactDeltaUsd ?? 0n,
|
||||
priceImpactDiffUsd: netPriceImapctValues?.priceImpactDiffUsd ?? 0n,
|
||||
pendingImpactUsd: netPriceImapctValues?.proportionalPendingImpactDeltaUsd ?? 0n,
|
||||
closePriceImpactDeltaUsd: closeAcceptablePriceInfo?.priceImpactDeltaUsd ?? 0n,
|
||||
closingFeeUsd,
|
||||
uiFeeUsd,
|
||||
pendingFundingFeesUsd,
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import { getWrappedToken } from "../../configs/tokens.js";
|
||||
import {getWrappedToken} from "../../configs/tokens.js";
|
||||
import merge from "lodash/merge.js";
|
||||
import { MarketsInfoData } from "../../types/markets.js";
|
||||
import { OrderType } from "../../types/orders.js";
|
||||
import { Token, TokensData } from "../../types/tokens.js";
|
||||
import { PositionTradeAction, RawTradeAction, SwapTradeAction, TradeAction, TradeActionType } from "../../types/tradeHistory.js";
|
||||
import {MarketsInfoData} from "../../types/markets.js";
|
||||
import {OrderType} from "../../types/orders.js";
|
||||
import {TokensData} from "../../types/tokens.js";
|
||||
import {PositionTradeAction, TradeAction, TradeActionType} from "../../types/tradeHistory.js";
|
||||
import graphqlFetcher from "../../utils/graphqlFetcher.js";
|
||||
import { getByKey } from "../../utils/objects.js";
|
||||
import { isIncreaseOrderType, isLimitOrderType, isSwapOrderType, isTriggerDecreaseOrderType } from "../../utils/orders.js";
|
||||
import { buildFiltersBody, GraphQlFilters } from "../../utils/subgraph.js";
|
||||
import { getSwapPathOutputAddresses } from "../../utils/swap/swapStats.js";
|
||||
import { parseContractPrice } from "../../utils/tokens.js";
|
||||
import { Address, getAddress } from "viem";
|
||||
import { Module } from "../base.js";
|
||||
import { GmxSdk } from "../../index.js";
|
||||
import {
|
||||
isIncreaseOrderType,
|
||||
isLimitOrderType,
|
||||
isSwapOrderType,
|
||||
isTriggerDecreaseOrderType
|
||||
} from "../../utils/orders.js";
|
||||
import {buildFiltersBody, GraphQlFilters} from "../../utils/subgraph.js";
|
||||
import {getSwapPathOutputAddresses} from "../../utils/swap/swapStats.js";
|
||||
import {Address} from "viem";
|
||||
import {Module} from "../base.js";
|
||||
import {GmxSdk} from "../../index.js";
|
||||
import {TradeAction as SubsquidTradeAction} from "../../types/subsquid.js";
|
||||
import {createRawTradeActionTransformer} from "../../utils/tradeHistory.js";
|
||||
|
||||
|
||||
export type MarketFilterLongShortDirection = "long" | "short" | "swap" | "any";
|
||||
export type MarketFilterLongShortItemData = {
|
||||
@@ -100,15 +106,15 @@ export async function fetchTradeActions({
|
||||
marketsInfoData: MarketsInfoData | undefined;
|
||||
tokensData: TokensData | undefined;
|
||||
}): Promise<TradeAction[]> {
|
||||
const endpoint = sdk.config.subgraphUrl;
|
||||
const endpoint = sdk.config.subsquidUrl;
|
||||
const chainId = sdk.chainId;
|
||||
|
||||
if (!endpoint) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const skip = pageIndex * pageSize;
|
||||
const first = pageSize;
|
||||
const offset = pageIndex * pageSize;
|
||||
const limit = pageSize;
|
||||
|
||||
const nonSwapRelevantDefinedFiltersLowercased: MarketFilterLongShortItemData[] = marketsDirectionsFilter
|
||||
.filter((filter) => filter.direction !== "swap" && filter.marketAddress !== "any")
|
||||
@@ -135,16 +141,14 @@ export async function fetchTradeActions({
|
||||
const hasSwapRelevantDefinedMarkets = swapRelevantDefinedMarketsLowercased.length > 0;
|
||||
|
||||
const filtersStr = buildFiltersBody({
|
||||
and: [
|
||||
AND: [
|
||||
{
|
||||
account: forAllAccounts ? undefined : account!.toLowerCase(),
|
||||
transaction: {
|
||||
timestamp_gte: fromTxTimestamp,
|
||||
timestamp_lte: toTxTimestamp,
|
||||
},
|
||||
account_eq: forAllAccounts ? undefined : account,
|
||||
timestamp_gte: fromTxTimestamp,
|
||||
timestamp_lte: toTxTimestamp,
|
||||
},
|
||||
{
|
||||
or: !hasPureDirectionFilters
|
||||
OR: !hasPureDirectionFilters
|
||||
? undefined
|
||||
: pureDirectionFilters.map((filter) =>
|
||||
filter.direction === "swap"
|
||||
@@ -152,25 +156,25 @@ export async function fetchTradeActions({
|
||||
orderType_in: [OrderType.LimitSwap, OrderType.MarketSwap],
|
||||
}
|
||||
: {
|
||||
isLong: filter.direction === "long",
|
||||
isLong_eq: filter.direction === "long",
|
||||
orderType_not_in: [OrderType.LimitSwap, OrderType.MarketSwap],
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
or: [
|
||||
OR: [
|
||||
// For non-swap orders
|
||||
{
|
||||
and: !hasNonSwapRelevantDefinedMarkets
|
||||
AND: !hasNonSwapRelevantDefinedMarkets
|
||||
? undefined
|
||||
: [
|
||||
{
|
||||
orderType_not_in: [OrderType.LimitSwap, OrderType.MarketSwap],
|
||||
},
|
||||
{
|
||||
or: nonSwapRelevantDefinedFiltersLowercased.map((filter) => ({
|
||||
marketAddress: filter.marketAddress === "any" ? undefined : filter.marketAddress,
|
||||
isLong: filter.direction === "any" ? undefined : filter.direction === "long",
|
||||
OR: nonSwapRelevantDefinedFiltersLowercased.map((filter) => ({
|
||||
marketAddress_eq: filter.marketAddress === "any" ? undefined : filter.marketAddress,
|
||||
isLong_eq: filter.direction === "any" ? undefined : filter.direction === "long",
|
||||
// Collateral filtering is done outside of graphql on the client
|
||||
})),
|
||||
},
|
||||
@@ -178,14 +182,14 @@ export async function fetchTradeActions({
|
||||
},
|
||||
// For defined markets on swap orders
|
||||
{
|
||||
and: !hasSwapRelevantDefinedMarkets
|
||||
AND: !hasSwapRelevantDefinedMarkets
|
||||
? undefined
|
||||
: [
|
||||
{
|
||||
orderType_in: [OrderType.LimitSwap, OrderType.MarketSwap],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
OR: [
|
||||
// Source token is not in swap path so we add it to the or filter
|
||||
{
|
||||
marketAddress_in: swapRelevantDefinedMarketsLowercased,
|
||||
@@ -201,7 +205,7 @@ export async function fetchTradeActions({
|
||||
],
|
||||
},
|
||||
{
|
||||
or: orderEventCombinations?.map((combination) => {
|
||||
OR: orderEventCombinations?.map((combination) => {
|
||||
let sizeDeltaUsdCondition = {};
|
||||
|
||||
if (
|
||||
@@ -217,8 +221,8 @@ export async function fetchTradeActions({
|
||||
|
||||
return merge(
|
||||
{
|
||||
eventName: combination.eventName,
|
||||
orderType: combination.orderType,
|
||||
eventName_eq: combination.eventName,
|
||||
orderType_eq: combination.orderType,
|
||||
},
|
||||
sizeDeltaUsdCondition
|
||||
);
|
||||
@@ -227,7 +231,7 @@ export async function fetchTradeActions({
|
||||
{
|
||||
// We do not show create liquidation orders in the trade history, thus we filter it out
|
||||
// ... && not (liquidation && orderCreated) === ... && (not liquidation || not orderCreated)
|
||||
or: [{ orderType_not: OrderType.Liquidation }, { eventName_not: TradeActionType.OrderCreated }],
|
||||
OR: [{ orderType_not_eq: OrderType.Liquidation }, { eventName_not_eq: TradeActionType.OrderCreated }],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -236,10 +240,9 @@ export async function fetchTradeActions({
|
||||
|
||||
const query = `{
|
||||
tradeActions(
|
||||
skip: ${skip},
|
||||
first: ${first},
|
||||
orderBy: transaction__timestamp,
|
||||
orderDirection: desc,
|
||||
offset: ${offset},
|
||||
limit: ${limit},
|
||||
orderBy: transaction_timestamp_DESC,
|
||||
${whereClause}
|
||||
) {
|
||||
id
|
||||
@@ -279,6 +282,7 @@ export async function fetchTradeActions({
|
||||
|
||||
reason
|
||||
reasonBytes
|
||||
timestamp
|
||||
|
||||
transaction {
|
||||
timestamp
|
||||
@@ -287,7 +291,7 @@ export async function fetchTradeActions({
|
||||
}
|
||||
}`;
|
||||
|
||||
const result = await graphqlFetcher<{ tradeActions: RawTradeAction[] }>(endpoint, query);
|
||||
const result = await graphqlFetcher<{ tradeActions: SubsquidTradeAction[] }>(endpoint, query);
|
||||
|
||||
const rawTradeActions = result?.tradeActions || [];
|
||||
|
||||
@@ -374,150 +378,3 @@ export async function fetchTradeActions({
|
||||
|
||||
return tradeActions;
|
||||
}
|
||||
|
||||
function createRawTradeActionTransformer(
|
||||
marketsInfoData: MarketsInfoData,
|
||||
wrappedToken: Token,
|
||||
tokensData: TokensData
|
||||
): (
|
||||
value: RawTradeAction,
|
||||
index: number,
|
||||
array: RawTradeAction[]
|
||||
) => SwapTradeAction | PositionTradeAction | undefined {
|
||||
return (rawAction) => {
|
||||
const orderType = Number(rawAction.orderType);
|
||||
|
||||
if (isSwapOrderType(orderType)) {
|
||||
const initialCollateralTokenAddress = getAddress(rawAction.initialCollateralTokenAddress!);
|
||||
const swapPath = rawAction.swapPath!.map((address) => getAddress(address));
|
||||
|
||||
const swapPathOutputAddresses = getSwapPathOutputAddresses({
|
||||
marketsInfoData,
|
||||
swapPath,
|
||||
initialCollateralAddress: initialCollateralTokenAddress,
|
||||
wrappedNativeTokenAddress: wrappedToken.address,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
isIncrease: false,
|
||||
});
|
||||
|
||||
const initialCollateralToken = getByKey(tokensData, initialCollateralTokenAddress)!;
|
||||
const targetCollateralToken = getByKey(tokensData, swapPathOutputAddresses.outTokenAddress)!;
|
||||
|
||||
if (!initialCollateralToken || !targetCollateralToken) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const tradeAction: SwapTradeAction = {
|
||||
id: rawAction.id,
|
||||
eventName: rawAction.eventName,
|
||||
account: rawAction.account,
|
||||
swapPath,
|
||||
orderType,
|
||||
orderKey: rawAction.orderKey,
|
||||
initialCollateralTokenAddress: rawAction.initialCollateralTokenAddress!,
|
||||
initialCollateralDeltaAmount: bigNumberify(rawAction.initialCollateralDeltaAmount)!,
|
||||
minOutputAmount: bigNumberify(rawAction.minOutputAmount)!,
|
||||
executionAmountOut: rawAction.executionAmountOut ? bigNumberify(rawAction.executionAmountOut) : undefined,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
targetCollateralToken,
|
||||
initialCollateralToken,
|
||||
transaction: rawAction.transaction,
|
||||
reason: rawAction.reason,
|
||||
reasonBytes: rawAction.reasonBytes,
|
||||
};
|
||||
|
||||
return tradeAction;
|
||||
} else {
|
||||
const marketAddress = getAddress(rawAction.marketAddress!);
|
||||
const marketInfo = getByKey(marketsInfoData, marketAddress);
|
||||
const indexToken = marketInfo?.indexToken;
|
||||
const initialCollateralTokenAddress = getAddress(rawAction.initialCollateralTokenAddress!);
|
||||
const swapPath = rawAction.swapPath!.map((address) => getAddress(address));
|
||||
const swapPathOutputAddresses = getSwapPathOutputAddresses({
|
||||
marketsInfoData,
|
||||
swapPath,
|
||||
initialCollateralAddress: initialCollateralTokenAddress,
|
||||
wrappedNativeTokenAddress: wrappedToken.address,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
isIncrease: isIncreaseOrderType(rawAction.orderType),
|
||||
});
|
||||
const initialCollateralToken = getByKey(tokensData, initialCollateralTokenAddress);
|
||||
const targetCollateralToken = getByKey(tokensData, swapPathOutputAddresses.outTokenAddress);
|
||||
|
||||
if (!marketInfo || !indexToken || !initialCollateralToken || !targetCollateralToken) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const tradeAction: PositionTradeAction = {
|
||||
id: rawAction.id,
|
||||
eventName: rawAction.eventName,
|
||||
account: rawAction.account,
|
||||
marketAddress,
|
||||
marketInfo,
|
||||
indexToken,
|
||||
swapPath,
|
||||
initialCollateralTokenAddress,
|
||||
initialCollateralToken,
|
||||
targetCollateralToken,
|
||||
initialCollateralDeltaAmount: bigNumberify(rawAction.initialCollateralDeltaAmount)!,
|
||||
sizeDeltaUsd: bigNumberify(rawAction.sizeDeltaUsd)!,
|
||||
triggerPrice: rawAction.triggerPrice
|
||||
? parseContractPrice(bigNumberify(rawAction.triggerPrice)!, indexToken.decimals)
|
||||
: undefined,
|
||||
acceptablePrice: parseContractPrice(bigNumberify(rawAction.acceptablePrice)!, indexToken.decimals),
|
||||
executionPrice: rawAction.executionPrice
|
||||
? parseContractPrice(bigNumberify(rawAction.executionPrice)!, indexToken.decimals)
|
||||
: undefined,
|
||||
minOutputAmount: bigNumberify(rawAction.minOutputAmount)!,
|
||||
|
||||
collateralTokenPriceMax: rawAction.collateralTokenPriceMax
|
||||
? parseContractPrice(bigNumberify(rawAction.collateralTokenPriceMax)!, initialCollateralToken.decimals)
|
||||
: undefined,
|
||||
|
||||
collateralTokenPriceMin: rawAction.collateralTokenPriceMin
|
||||
? parseContractPrice(bigNumberify(rawAction.collateralTokenPriceMin)!, initialCollateralToken.decimals)
|
||||
: undefined,
|
||||
|
||||
indexTokenPriceMin: rawAction.indexTokenPriceMin
|
||||
? parseContractPrice(BigInt(rawAction.indexTokenPriceMin), indexToken.decimals)
|
||||
: undefined,
|
||||
indexTokenPriceMax: rawAction.indexTokenPriceMax
|
||||
? parseContractPrice(BigInt(rawAction.indexTokenPriceMax), indexToken.decimals)
|
||||
: undefined,
|
||||
|
||||
orderType,
|
||||
orderKey: rawAction.orderKey,
|
||||
isLong: rawAction.isLong!,
|
||||
pnlUsd: rawAction.pnlUsd ? BigInt(rawAction.pnlUsd) : undefined,
|
||||
basePnlUsd: rawAction.basePnlUsd ? BigInt(rawAction.basePnlUsd) : undefined,
|
||||
|
||||
priceImpactDiffUsd: rawAction.priceImpactDiffUsd ? BigInt(rawAction.priceImpactDiffUsd) : undefined,
|
||||
priceImpactUsd: rawAction.priceImpactUsd ? BigInt(rawAction.priceImpactUsd) : undefined,
|
||||
positionFeeAmount: rawAction.positionFeeAmount ? BigInt(rawAction.positionFeeAmount) : undefined,
|
||||
borrowingFeeAmount: rawAction.borrowingFeeAmount ? BigInt(rawAction.borrowingFeeAmount) : undefined,
|
||||
fundingFeeAmount: rawAction.fundingFeeAmount ? BigInt(rawAction.fundingFeeAmount) : undefined,
|
||||
|
||||
reason: rawAction.reason,
|
||||
reasonBytes: rawAction.reasonBytes,
|
||||
|
||||
transaction: rawAction.transaction,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
};
|
||||
|
||||
return tradeAction;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function bigNumberify(n?: bigint | string | null | undefined) {
|
||||
try {
|
||||
if (n === undefined) throw new Error("n is undefined");
|
||||
if (n === null) throw new Error("n is null");
|
||||
|
||||
return BigInt(n);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("bigNumberify error", e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ import {withRetry} from "viem";
|
||||
|
||||
import {
|
||||
EXECUTION_FEE_CONFIG_V2,
|
||||
GAS_LIMITS_STATIC_CONFIG,
|
||||
GAS_PRICE_PREMIUM_MAP,
|
||||
getChain,
|
||||
getViemChain,
|
||||
MAX_PRIORITY_FEE_PER_GAS_MAP
|
||||
} from "../../configs/chains.js";
|
||||
import {getContract} from "../../configs/contracts.js";
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
ESTIMATED_GAS_FEE_BASE_AMOUNT_V2_1,
|
||||
ESTIMATED_GAS_FEE_MULTIPLIER_FACTOR,
|
||||
ESTIMATED_GAS_FEE_PER_ORACLE_PRICE,
|
||||
GELATO_RELAY_FEE_MULTIPLIER_FACTOR_KEY,
|
||||
GLV_DEPOSIT_GAS_LIMIT,
|
||||
GLV_PER_MARKET_GAS_LIMIT,
|
||||
GLV_WITHDRAWAL_GAS_LIMIT,
|
||||
@@ -107,6 +109,10 @@ export class Utils extends Module {
|
||||
methodName: "getUint",
|
||||
params: [GLV_PER_MARKET_GAS_LIMIT],
|
||||
},
|
||||
gelatoRelayFeeMultiplierFactor: {
|
||||
methodName: "getUint",
|
||||
params: [GELATO_RELAY_FEE_MULTIPLIER_FACTOR_KEY],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -117,6 +123,8 @@ export class Utils extends Module {
|
||||
return BigInt(results[key].returnValues[0]);
|
||||
}
|
||||
|
||||
const staticGasLimits = GAS_LIMITS_STATIC_CONFIG[this.chainId];
|
||||
|
||||
return {
|
||||
depositToken: getBigInt("depositToken"),
|
||||
withdrawalMultiToken: getBigInt("withdrawalMultiToken"),
|
||||
@@ -131,10 +139,17 @@ export class Utils extends Module {
|
||||
glvDepositGasLimit: getBigInt("glvDepositGasLimit"),
|
||||
glvWithdrawalGasLimit: getBigInt("glvWithdrawalGasLimit"),
|
||||
glvPerMarketGasLimit: getBigInt("glvPerMarketGasLimit"),
|
||||
};
|
||||
createOrderGasLimit: staticGasLimits.createOrderGasLimit,
|
||||
updateOrderGasLimit: staticGasLimits.updateOrderGasLimit,
|
||||
cancelOrderGasLimit: staticGasLimits.cancelOrderGasLimit,
|
||||
tokenPermitGasLimit: staticGasLimits.tokenPermitGasLimit,
|
||||
gmxAccountCollateralGasLimit: staticGasLimits.gmxAccountCollateralGasLimit,
|
||||
gelatoRelayFeeMultiplierFactor: getBigInt("gelatoRelayFeeMultiplierFactor"),
|
||||
} satisfies GasLimitsConfig;
|
||||
});
|
||||
|
||||
this._gasLimits = gasLimits;
|
||||
|
||||
return gasLimits;
|
||||
}
|
||||
|
||||
@@ -154,10 +169,10 @@ export class Utils extends Module {
|
||||
|
||||
switch (tradeFeesType) {
|
||||
case "swap": {
|
||||
if (!swapAmounts || !swapAmounts.swapPathStats) return null;
|
||||
if (!swapAmounts?.swapStrategy.swapPathStats) return null;
|
||||
|
||||
return estimateExecuteSwapOrderGasLimit(gasLimits, {
|
||||
swapsCount: swapAmounts.swapPathStats.swapPath.length,
|
||||
swapsCount: swapAmounts.swapStrategy.swapPathStats?.swapPath.length,
|
||||
callbackGasLimit: 0n,
|
||||
});
|
||||
}
|
||||
@@ -165,7 +180,7 @@ export class Utils extends Module {
|
||||
if (!increaseAmounts) return null;
|
||||
|
||||
return estimateExecuteIncreaseOrderGasLimit(gasLimits, {
|
||||
swapsCount: increaseAmounts.swapPathStats?.swapPath.length,
|
||||
swapsCount: increaseAmounts.swapStrategy.swapPathStats?.swapPath.length,
|
||||
});
|
||||
}
|
||||
case "decrease": {
|
||||
@@ -229,7 +244,7 @@ export class Utils extends Module {
|
||||
const feeData = await withRetry(
|
||||
() =>
|
||||
this.sdk.publicClient.estimateFeesPerGas({
|
||||
chain: getChain(this.chainId),
|
||||
chain: getViemChain(this.chainId),
|
||||
type: "legacy",
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -1,4 +1,30 @@
|
||||
{
|
||||
"3637": {
|
||||
"0x6682BB60590a045A956541B1433f016Ed22E361d": {
|
||||
"optimalUsageFactorLong": "0xd7900b67e1e881ae7b0c8f93a08255e02561e815da97a3de875ca85cf4b40d1e",
|
||||
"optimalUsageFactorShort": "0x18c06265540d8e6ab5ba16677912db446fd09fb0615edc830ccc15dbc07fcd97",
|
||||
"baseBorrowingFactorLong": "0x9d72c9936d0741b7dd4b7a585a7cf418bd6ea5dae70c3c7caff29c67f7b16926",
|
||||
"baseBorrowingFactorShort": "0x11e7e75d485e3ff9f13227abf344a29d66b7f81819e896b0bf33c6d87d2c37b7",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xa42e5c54173d3f51736eb8cbe5f06c36084d30259b7e40d2c1fc70431300d7bd",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x861a85c0ae1bfc44aef16c01500ea32341ef8e72fc3103f2e4cac8b0899ab984"
|
||||
},
|
||||
"0x2f95a2529328E427d3204555F164B1102086690E": {
|
||||
"optimalUsageFactorLong": "0x78ca97fbabdfe99f666916132b2200ac92a2a87e4d693fed4b42415c7bb1f83f",
|
||||
"optimalUsageFactorShort": "0x3d54102e9622aa676179f1e51c9880980ca2c907a18e7fa021c4289c7bede161",
|
||||
"baseBorrowingFactorLong": "0x1f9c563f9dd77ab98efdf4d59a9b5288f8e5a68c6ca834d906a9f966c4f0eef3",
|
||||
"baseBorrowingFactorShort": "0x58ea485f2aa0ee5a370c5250a39ada3c2c994e7404158f45eb50f100cf53bec8",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x31ae298fedbf23a4328de09720d623dd431df614f39806867dd09b73601216d8",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x6daccc95dbd38aa12073d2b7d1439f9897d49cea49ee23a4245722cf76b162da"
|
||||
},
|
||||
"0x6bFDD025827F7CE130BcfC446927AEF34ae2a98d": {
|
||||
"optimalUsageFactorLong": "0xfa69cbf64ee47e4126c036bc70c223c711e39dba98ca9adb5d1114447692007c",
|
||||
"optimalUsageFactorShort": "0xf9c2f103bc3d763b06ac92154847f3915e85a4b03a982ec69effecb40c0ae20f",
|
||||
"baseBorrowingFactorLong": "0x4be52e9d009ab8bdbf6ad5524196e2d08a5e269af99218f8c7c7c9752093692f",
|
||||
"baseBorrowingFactorShort": "0x068ee704d21753b16c63dcee8d3fd81e98e5124de0401ee6f1e0c4ee98938a0d",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x11697e736a0b59f616867fbe856c1c44dd826a785c7155fd4c5657ac4a92a7f7",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x9e1a14e48896d7b69cbcd1632d3dd4061324a789f9be86617408fe12ec5700e6"
|
||||
}
|
||||
},
|
||||
"42161": {
|
||||
"0x47c031236e19d024b42f8AE6780E44A573170703": {
|
||||
"optimalUsageFactorLong": "0x81cc37ff3833298b6114d8317fd6c5996dc7a8ab53fefd3edb9f415ca4267824",
|
||||
@@ -647,6 +673,198 @@
|
||||
"baseBorrowingFactorShort": "0x4385c8e191a978b34824ae41b408ba588e261054e5945d0e99068ad10b4905ed",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x2914e46a2e627cc733a945a8eb44ab2ef758f9a6fc35821bec5e340d7e0239b4",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xe09312ebfcc03606ed101a23c5fe2e9883e431d2787ba65c5a0ae9526b58ce1b"
|
||||
},
|
||||
"0x4D3Eb91efd36C2b74181F34B111bc1E91a0d0cb4": {
|
||||
"optimalUsageFactorLong": "0x2e3f861626c4e7e577efc24e5c1bf68664f67cca423736be0c5cc5bfd2d51490",
|
||||
"optimalUsageFactorShort": "0x6140b20bd94e328307faca8fbb6fcead2c2104a2ef78c533383f36379298cbd2",
|
||||
"baseBorrowingFactorLong": "0xb1a5d5b89f2457ae28d85c819b41f35391063c1f32c19406bf0d5f3697d44af7",
|
||||
"baseBorrowingFactorShort": "0x6b2efc5c2a51a2cb3b50b9a474a578272ceac9dacd7f8a495246541658b004c4",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x0364a6542f02b0cf5de96a3f0a0566ee9ac777fdac6302691a0b1296c29edca6",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x825303f6ce78aa93bacadc5a8603f840a7e0f0bfdd480164fd9b0f3dfefaf00e"
|
||||
},
|
||||
"0x9e79146b3A022Af44E0708c6794F03Ef798381A5": {
|
||||
"optimalUsageFactorLong": "0x51f1c4be39fa03c2c9fdf3e3faf5acf64fdb3dd69a45e4c809014ea2bb49ee5e",
|
||||
"optimalUsageFactorShort": "0x2a0c242783a92cdcdead09777db7af8c8f4f4cec93e12816e68ccd4d7d8c83de",
|
||||
"baseBorrowingFactorLong": "0xd2648ee5a2446600ca9f877f02a536176854e9dc58f62e816ab1a9f196b5d419",
|
||||
"baseBorrowingFactorShort": "0x208583f32d8a947ce802ada215547db39c83f47b79694bab2a8553af1b011676",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x0dd5d847eb76fc4986137f91d409d500b8e9a3e05d0b721e902ed4fe6a42f0d4",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xa77bd37d33247f0d86c482b48208711317ee68817a21fc2976bb3e54f2265579"
|
||||
},
|
||||
"0x0e46941F9bfF8d0784BFfa3d0D7883CDb82D7aE7": {
|
||||
"optimalUsageFactorLong": "0x4c401d404a1c1269fb6aa7928ceb1935a9a6f4de7c7e64f214b8a266a662ce30",
|
||||
"optimalUsageFactorShort": "0xa230801e8b702d8b0e96c43f1f55a8b59290c4f8c279f1b7857dd05c580ce343",
|
||||
"baseBorrowingFactorLong": "0x5e5b2f3c0a995b771355f55d7717305c072e7e9590dfbd3974c16abfb106f264",
|
||||
"baseBorrowingFactorShort": "0x4e83186068e01b7963bf06a1db46129a3fc52c0aa2a6cede0f4f7e1cea418635",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x20a3640f97b96b9c0822d0dc61ee226a8334e7a5b24626b9a7502097af05a326",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x85ef58093c6eaed2811bc1bea92f4a159585f0b24ac6c4346d7d2df8022367b7"
|
||||
},
|
||||
"0x2523B89298908FEf4c5e5bd6F55F20926e22058f": {
|
||||
"optimalUsageFactorLong": "0xa2f32b1482558e0e227f8710d0d95e3109d262f1c202c36290eb7f237196378d",
|
||||
"optimalUsageFactorShort": "0x0bbb61bb560c7a37fa166a538e3bd121d97340e6c337b7b63305e6c970fe003e",
|
||||
"baseBorrowingFactorLong": "0x36448e380d2f15c1f9ebb52be2edf1465cd3bc094f51e820c4edb9739911bdfc",
|
||||
"baseBorrowingFactorShort": "0x3d10551cad9dcb1162fffb36dddb7a9bf732309b035c390903cb6691c5ed2c41",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x96c6512b5cf544d92602a6f131b09ce017fa399e19cb1077b5e401ccb288a81d",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x89006ab4ceebc6145f5520cd195fd1edc44b69eb84a3b6bc106a423954e3adfd"
|
||||
},
|
||||
"0x7c54D547FAD72f8AFbf6E5b04403A0168b654C6f": {
|
||||
"optimalUsageFactorLong": "0xff18362de8f0fda2252bc409478257cebdfa5ec4b871406f7a505da2c7fe1ab8",
|
||||
"optimalUsageFactorShort": "0x230f71281e768eebd4331bd719291624e543196517749b8d00dba269ce6be195",
|
||||
"baseBorrowingFactorLong": "0xaeef4efa75991b37580a3d0a298c6544ee23646fc1ad2d8c1f1cb423caeaa409",
|
||||
"baseBorrowingFactorShort": "0xe286e4d69b63827eefc1434086a543c85f9531c278e55f0603cf9b54e2c19637",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xb656f322337ee629cf453b0fa72ca684db89f8bdcfff5e333842f6e66f5ebbd8",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x853644d97900c5164a82701fde817c51a57d31dc222e136c1e2c7d5a948b11fd"
|
||||
},
|
||||
"0x39AC3C494950A4363D739201BA5A0861265C9ae5": {
|
||||
"optimalUsageFactorLong": "0x5be9aa175e2ec6bf9d9508099dc2452f179ea3c74374aaf69b245fadd1fee369",
|
||||
"optimalUsageFactorShort": "0xd2708f5731caffd02de0e20b01a2e089c23ae856702baa71dbc8183da6ebfb76",
|
||||
"baseBorrowingFactorLong": "0x0a4665f7256edc8fbc4dcb890994df31eeacc8d252f39a90f7f85842eeb81ca2",
|
||||
"baseBorrowingFactorShort": "0x9906550d5dc055a64080e0a74a6910eee2bf9e507bf50f7636ae653bc037ba4f",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x0eaf5f1d2e29a62d716f417ca4c84c379336f25ed6b72efef91b1192674b1cb4",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x90b85d18260e09eff7f3d7f4a3e907cbf476ff493d792f3f33f5926a68cfe584"
|
||||
},
|
||||
"0x4C0Bb704529Fa49A26bD854802d70206982c6f1B": {
|
||||
"optimalUsageFactorLong": "0x6132251ed7cf628c17d1a03b0a42435719110da3d14a57a8db2afe4389bff311",
|
||||
"optimalUsageFactorShort": "0x7efb109390c3e739eeefb62366461b031215a755a797d9a4eb24e228d3e4e9e0",
|
||||
"baseBorrowingFactorLong": "0x6cd904ae4fbfc7bd74ad666b5f8d0bd68e4a723e111e81f6547f0b1eed7bcf37",
|
||||
"baseBorrowingFactorShort": "0x3e5f962e6c670c6bc3f4ca5c95884c3aa47845efc94eecca34b6b7d7fa2a9b18",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xa985be98994cbd44fad76d2e48396d307f965b4478976156255e8b3e9281a1da",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xa0a5a75cba99e7ca32b5d5039093352f2d0c8002f767391f5c3da1e5eaecd324"
|
||||
},
|
||||
"0x8263bC3766a09f6dD4Bab04b4bf8D45F2B0973FF": {
|
||||
"optimalUsageFactorLong": "0xcf65fa5ca92f31afd04381260c53b010cc82d694441de260d266316133e277c6",
|
||||
"optimalUsageFactorShort": "0xdaa4ae4be02547f5cd1334b974921d1745b0e4602a0318dab8a3c40849527270",
|
||||
"baseBorrowingFactorLong": "0x6e563f6cb443ae4aa74a98c49a7894fd3eca7e3f82060c65296af449c4b84113",
|
||||
"baseBorrowingFactorShort": "0x138821850f50631349f9e3cc0c8217e6276f1f3b457535058614ac811f615eca",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x085985feb2950c1b3b6514f8a58b8c3e773fbc14766203c93391f49d0620fd41",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x92d93c3132ab787b6ad12fe4de15c8aa7c97979b8b6a89f957fa4d665d388c1b"
|
||||
},
|
||||
"0x40dAEAc02dCf6b3c51F9151f532C21DCEF2F7E63": {
|
||||
"optimalUsageFactorLong": "0x77ee9eabc7612beed8eed57a46e1faef4f44f5c3f217f3f1c5c4240dcd4b0625",
|
||||
"optimalUsageFactorShort": "0x5a32ca8b7f856170e4244c2dd3eca5765be5c943f4ec6e9e7e6cd6eb7a079722",
|
||||
"baseBorrowingFactorLong": "0x47055919ec19eb3179a0c3d99ebda26b229a92a42f898117cc07a477ea59c5f4",
|
||||
"baseBorrowingFactorShort": "0x183a9f2b4a1bd93688af66a5dada4d0444d8e0af19c05e23ce2de5799517876b",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x97a0f3257e4c53a716f2aec8482b5493785a94a8cf011e3bbebdafd76adb2d05",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x8216510d0019a2ffcff5be1f1dca67a27b6cb630db12580d3dd4df6748134d9a"
|
||||
},
|
||||
"0x672fEA44f4583DdaD620d60C1Ac31021F47558Cb": {
|
||||
"optimalUsageFactorLong": "0xd656b2ed477413b2c7a366b70df31f09e4109d308594488cb6989cdbc00328f2",
|
||||
"optimalUsageFactorShort": "0x00bad40a9c19db8daccf00701b36eee1a7d5decf375b0823786a176283b35f87",
|
||||
"baseBorrowingFactorLong": "0x2235c5b316dbc8b5a48a61ba913c4f60499c8e8fa243b9d7179a29403acd4390",
|
||||
"baseBorrowingFactorShort": "0xadc19abc5e3cdc7393f56e2c6371b32085af2ee78814ea6ebe9e217189a8a403",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xf6ec3dd73f1037a6aaea5c3897c01ff2c7d94dddcc3857a35edd9446442ba7e7",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x3551da5e79dea5838549ef9dc5016ac92391bc7e4c6b78b7f73eaa27d245a84c"
|
||||
},
|
||||
"0x3B7f4e4Cf2fa43df013d2B32673e6A01d29ab2Ac": {
|
||||
"optimalUsageFactorLong": "0xcfee8f8e2692cdcf11c4af2455157f4f18efa01fc6bbcc049e20ca6dacc43a08",
|
||||
"optimalUsageFactorShort": "0x203a038715927e0274514c0aa40ce6e024bf74b94f53c3965573e9b3b6295f5e",
|
||||
"baseBorrowingFactorLong": "0x58ee17d42d5a0ccc5af26f74eb115dbc916c2b5c20db2ea399569b76760c2a7b",
|
||||
"baseBorrowingFactorShort": "0x774688a2c8f874f84b8fd90662c2ceb45afbae4ece4456e7236ee46b3776fd9c",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xcf5c7c02346f62cef2ae4a64c78f732d560894bbdde290e0f685a8bb5b4d0954",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xa11f19bc8b03ece53927c2afb04c118fecb0cf5a778800a1f558c06b78605818"
|
||||
},
|
||||
"0xa29FfE4152B65A0347512Ae5c6A4Bbc7a3d6d51B": {
|
||||
"optimalUsageFactorLong": "0xfc5206fdd305807d0ccb94caad764a53a16ce56b164cafa76492b7388c7ccaa8",
|
||||
"optimalUsageFactorShort": "0x809ba85a9b39cb854ee5f2afc302cc9c388767a27517a2e64220e3838bbf6a91",
|
||||
"baseBorrowingFactorLong": "0x795cabccc1302668c7f3941836a98c704b1889e4e657c0e9822c0ca66191febf",
|
||||
"baseBorrowingFactorShort": "0x336c59129d8cf65f2848edfec9c8abac325cb9442d98d4f8dbd9808b78021e4e",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x644117c95e31e8d50d65b692d7650ab3b2158da280bc1ebee8070961d2d63f0c",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xb8508af8e288565d818d877a974d643efb7165d4c5bf610aaed87b8fcb143291"
|
||||
},
|
||||
"0x9f0849FB830679829d1FB759b11236D375D15C78": {
|
||||
"optimalUsageFactorLong": "0x16f1b35b207465f30a27af5a05c2cbc5950bbc596dc954208f16c2e72ef60d53",
|
||||
"optimalUsageFactorShort": "0x478c3e327487b2ce5ce0fa2c8ef51f52799b9d8be175b50a3fc7309bb0bb7642",
|
||||
"baseBorrowingFactorLong": "0xa118eca252d64e4deacf84e2915c28fc06046c9e0d357461730030ef8749973a",
|
||||
"baseBorrowingFactorShort": "0x39bc784879f3ef9de946a74c4d244136358aa9e71ff024535e8c3a3a427e8c0f",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xb9ad121fcf3c1b0db36e6a730321f002b19b8b3d3dc37dc5839f259ffd5b7b8a",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xcbf93efdbf556397736088feff0d4bc2b2f049622b72fb832cc7a3623f2f5bbd"
|
||||
},
|
||||
"0x41E3bC5B72384C8B26b559B7d16C2B81Fd36fbA2": {
|
||||
"optimalUsageFactorLong": "0x1049fa537be65f1669e1682815978c5643f4d8f1eb58c5be48cbc35c25470104",
|
||||
"optimalUsageFactorShort": "0x30cce3e5ab653df70bc715d62bfa6224debf9807317d1387cd2707375f8b0e8f",
|
||||
"baseBorrowingFactorLong": "0x23ed6320768672b71a9c233bb992d3476f86816d1f6f635f268d8d3f62de8e81",
|
||||
"baseBorrowingFactorShort": "0xdbadb6c2e4422e23c32b6d1d21ad6a9b1dbe314bc7b920b5871f45ccdb859f86",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x77a792e68ad9041d1fec21b18f12b0af5708ccd673ea8669c8ad5c1d996c97ad",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xa042c3817a731e8e3c7f7c0c27ac57ad64f1b3f96f5e8f2b61fc59e0dd5dda16"
|
||||
},
|
||||
"0x4024418592450E4d62faB15e2f833FC03A3447dc": {
|
||||
"optimalUsageFactorLong": "0x01cd43d4452a67736816f956f6c327899dcb3e0e21c55fc17c43f3e112fbdbc7",
|
||||
"optimalUsageFactorShort": "0xaf2294097cd7158722c20a8e715a3ae5ffd2fdf9a72437f3137d639aaf88ba5a",
|
||||
"baseBorrowingFactorLong": "0x4f3dcf36557dcf5acfa977979e818c7840b4edaff30feeffb4a9b3e723a7644c",
|
||||
"baseBorrowingFactorShort": "0xd87713e30b629d0a6f5fcbb7c6d4094fa205426156f8dfc9131c4ad171a9a39c",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xa9716f0fde044cbd378bf79669bba6e0d4a9b19c769df1b2f24bb99cad7de8af",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xddfa24b2645bead3760d8fd6545d5025c315df2a3c41f0906e567a2f79f9eec6"
|
||||
},
|
||||
"0x2a331e51a3D17211852d8625a1029898450e539B": {
|
||||
"optimalUsageFactorLong": "0x2a78441ba23708464d581d9426e221e0aa3a840dd9d13c766d70fc3bac22a10c",
|
||||
"optimalUsageFactorShort": "0x192b0a6717e8cc96105745bb4e0fbb543af27f1c82abed4ad3c733d6edc9ba96",
|
||||
"baseBorrowingFactorLong": "0x74a9054e0b13d192a76e38f6f6ecf312ab45bc316c19f775725de24d0b19706a",
|
||||
"baseBorrowingFactorShort": "0x4cdbd9179e7797cb3e65fb8bcc53e781b35235fe360c6f8b86b049a28533df23",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x0efc0133799b04a909a56ee3f5f92b5db33042d8569f2a1426b689e1a325ad52",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x4fe44701fa72e8210e4bfd3be170b656a61be4b7fe5ae96608c3fe596bfa1129"
|
||||
},
|
||||
"0x3f649eab7f4CE4945F125939C64429Be2C5d0cB4": {
|
||||
"optimalUsageFactorLong": "0x00bec875815865da5521b2ea0ee0616f66450231a98a55b6ee2b724b454532b4",
|
||||
"optimalUsageFactorShort": "0xacfca85903d763fb52fe1fcfae7f2ed8b2c9fe72280a31e979c0567d0a6a6efb",
|
||||
"baseBorrowingFactorLong": "0x9bc4457049d2f1b3b21b76e15d176270087a35a8e9b87e52ea76f4ef19576b69",
|
||||
"baseBorrowingFactorShort": "0x503e041bfa226bc8a98252380d4c18c1e69d0f6d60224bc1d9f2057cdfd9f6da",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x5405a5e90ab4c8c0829d48d8722fe746f93a9f5f7a427ad47af40cc0dc0afc8c",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x0fb399f08622627b531460308ec479e4b272d7074e54e3a1b09a72f67af12cf6"
|
||||
},
|
||||
"0xfaEaE570B07618D3F10360608E43c241181c4614": {
|
||||
"optimalUsageFactorLong": "0x840afffe8bb43184814016a015923dc44980d858e358aa503adda7b31c468f1c",
|
||||
"optimalUsageFactorShort": "0x4de43289ffe221b34411fec8755fb4f1c8d1d633f6092a93411d5cbc435f028b",
|
||||
"baseBorrowingFactorLong": "0x04bfe67edff208531e325f267f20d545cc448bdaf5d48e2603f5c90ed674ca5c",
|
||||
"baseBorrowingFactorShort": "0xef2cc5458599f65fc7218057d7d235ebbfb13278739c1e00a8c9e72a04cdb763",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x91551ab540048fa5892c9ea76f705ce83d782d9adc2fbbc3dbc0704f24d45136",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x39ae0e51cbb91af2399430ab64e4690dcd3c6a5693c849ea0a4b3a42b87617dd"
|
||||
},
|
||||
"0x6EeE8098dBC106aEde99763FA5F955A5bBc42C50": {
|
||||
"optimalUsageFactorLong": "0xc5d3ef57932d40b9ef196a4a57eeae3c1027a32ef7b6a34cf166069979fe4213",
|
||||
"optimalUsageFactorShort": "0x42f11677ada1c42fa727a2ea741dbb6f5b666389aa17ca02adac73c44d3d9a2f",
|
||||
"baseBorrowingFactorLong": "0x1778bc0fad1c076dc361d0f5202dd46515c98bc5436b8a59504340dd36793b21",
|
||||
"baseBorrowingFactorShort": "0x806893df271d661e281f8bc7fcc8f76b34873d4d2dbd5c744e172825170f2cf7",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x030d1ce870a32ceba468c3ad1799ba63042f03a68006edf75a9d57e11222edfb",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xecf06f84c969631a27bfd2ffef485a24b8ebfd9b96e5012177fd4e90c6404403"
|
||||
},
|
||||
"0xb3588455858a49D3244237CEe00880CcB84b91Dd": {
|
||||
"optimalUsageFactorLong": "0x94c4aa621db98cbda9d6162bb79826bbf4b96cd285eb90464ec1785e169674cb",
|
||||
"optimalUsageFactorShort": "0x80cf5d6b6ee1b3fd0397a7ee9dc2ebbafdf1ac30da22a880755edd7ebd8ec9d5",
|
||||
"baseBorrowingFactorLong": "0x6a3042d283f094247edd39893079e9235de817c338493feef0044e9b8b1dd09b",
|
||||
"baseBorrowingFactorShort": "0x9b9ff5b5a59d8b63d7e8861075c68050677c0302571e4e2e25b514f95b93128b",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x07a8457e82e785125d34722525f0e0e685316a513e5ee6f04b498087b8a275d2",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x24e05cd21e1f4f1d94567f773480d72250104265a10ad7e481d8cf73b9cc2e5a"
|
||||
},
|
||||
"0xF913B4748031EF569898ED91e5BA0d602bB93298": {
|
||||
"optimalUsageFactorLong": "0x4df8b45feea6d2963f1486e881aca59dcb247f56f3e04845bc08562668d7e3f6",
|
||||
"optimalUsageFactorShort": "0x1564077b0c2d200ac2167eaff02ffb59f62989c06694002b95ea4c486f68bf2e",
|
||||
"baseBorrowingFactorLong": "0x16a3c67f0ea673365b1c4576f7c65f04a31c670bc0b0cd5984899841983818a5",
|
||||
"baseBorrowingFactorShort": "0xbd0c22d6bbb01891a8d5f49e47458bc67a973cf226f93ebb2229a5143f205b26",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x42c7e1c1e8383280f2b7b6f41fef279b7de9d6ef844baeacbbc3ad1d67befd59",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x0cf0f56a26e557abcba53daa2391caf2ac97116f0289e58fb0b561a1aede0cbe"
|
||||
},
|
||||
"0x4De268aC68477f794C3eAC5A419Cbcffc2cD5e02": {
|
||||
"optimalUsageFactorLong": "0x86d3de0a019f225098a60cdb9d7e21ab24d699cc47e2c31db8d0a5f72b1572a3",
|
||||
"optimalUsageFactorShort": "0x3a604d2a245cee3e332f8c2af4931dc72f924b0d49ea8bd749e38b9c6fc3ae80",
|
||||
"baseBorrowingFactorLong": "0x9818632b65d01ae14408c0e4981045e091d5f425cc05e25eebda63a04377a520",
|
||||
"baseBorrowingFactorShort": "0xbbc7fda5e339cfdee4de6c34c72df7716f622b07f0f91a106dec58918397a91d",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x8d2a697a83b4b34e4ff24897e9339767ea6809e0cf6f4a0f72fd4e9c6bb7c76a",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x0d9606595ac223589c6c0e6e8b40475bafaa63e12da2980cb4819790d276926c"
|
||||
},
|
||||
"0x947C521E44f727219542B0f91a85182193c1D2ad": {
|
||||
"optimalUsageFactorLong": "0xf3e64603926be37b6b2562d4f3170766259becf591daea808b0bd9886327de90",
|
||||
"optimalUsageFactorShort": "0x929e03c53ef12a6d8478491778b82373ff4e986cd8f45a10446bf8ab82fedd1c",
|
||||
"baseBorrowingFactorLong": "0x951e8fcec3489d892830c6c56301630bc36f14483507ce1d439d5ddd15fe3308",
|
||||
"baseBorrowingFactorShort": "0x090b0040858c9c674a53d140621f9bff7f56a260b54a6de69bc1996ea3df566f",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xe88814f2fb3ab07c280f66bd31b9f4aa6540dcf530081f1b747166fd7a329bc9",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xbb3838087bd1b2f6534bcb7d72f2bf9bffb871ad3084d9cf413417bd9f332a3c"
|
||||
},
|
||||
"0x2347EbB8645Cc2EA0Ba92D1EC59704031F2fCCf4": {
|
||||
"optimalUsageFactorLong": "0x2c3053226bdfb226d42d53af6eeb4bbac1d17d56798e25a625b7b0a08042a981",
|
||||
"optimalUsageFactorShort": "0x4feddec8819871dd05aa7543a846a90bb1d2657a12665e807a49a13c20aec22e",
|
||||
"baseBorrowingFactorLong": "0x30f0541fc7801155864d3591e006af078f3c9765bfc8e05324be7da9d8b7146e",
|
||||
"baseBorrowingFactorShort": "0x53cfa8c89973d1f35bf07296010c6d6a3e4bbf0ffc6342c484eade343b08afff",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x2620c46b82428590d0078686fcde4289794adf54b38673056a5217e06cf0ad9a",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x6e33f746e16f3319f9369024021d1582b5cc5849d872deaddffbf6fe4bc4d6bf"
|
||||
}
|
||||
},
|
||||
"43113": {
|
||||
@@ -939,6 +1157,64 @@
|
||||
"baseBorrowingFactorShort": "0x3755d19ffde034f5b43c13d0b7904191836ca26a80930493bba489db125153f9",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x80a3a0af00d7c7d55e7bfffde6bc8ea69b652dafd94d7ebe4f20b2a4cdb7537b",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xe08a60ae243116c5d4e8f5c2df48367eb2edac52889485b82e216877ffb4ff34"
|
||||
},
|
||||
"0x94cE6F65188a92F297C7f0A5A7B3cAd9013450F8": {
|
||||
"optimalUsageFactorLong": "0xdad8117f7fee5bcf38f4a80a250f295a804aacddbe6d0aa0f54ed0505ee815f8",
|
||||
"optimalUsageFactorShort": "0xf6cdc9a2c99dfe219352a58f2333c48cc3b3c26c6dea227e6ff149fb94577358",
|
||||
"baseBorrowingFactorLong": "0x6af5259295536b9538c074343abc5a63533f2cf9f25b5fa179d75b636da84737",
|
||||
"baseBorrowingFactorShort": "0x448565c511e8f1589122ba03c1db3da1afa9d89a9036e7d9c55bada44d3d8f43",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xa9e22e9d3e4438cd0c056f6f9e39b103e741d9c87a30625643f02ad053731869",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x1ecf10a4f1f28b4e3c1f1fae6af7ea5a7e93d90844720b89b38509945c507896"
|
||||
},
|
||||
"0x1cb9932CE322877A2B86489BD1aA3C3CfF879F0d": {
|
||||
"optimalUsageFactorLong": "0xfeb1075a53052e6633557a43a3179d4f1e67a1bc65835090c0782a99157ff15f",
|
||||
"optimalUsageFactorShort": "0x07e0865031240e0e59fa57e199cf9128f507c871b33a59652362b5af659d430a",
|
||||
"baseBorrowingFactorLong": "0x574b6a64053b8229c5bb6979599ea3f5093eabdadbf85ac47b0ba855c87bc9bc",
|
||||
"baseBorrowingFactorShort": "0x61b2bf5ee8c0abafb81996ae64f534c17dc690a453b62cf2151e26e5ec2f8229",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xd42c10f47376a3705a0244fba83a2b52714b4742dc4e285b9b0cef38e28ab0cb",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x15d1b15dc1999636db607f963394239311b8b4119e830801d9bc28df041d3bbc"
|
||||
}
|
||||
},
|
||||
"421614": {
|
||||
"0x482Df3D320C964808579b585a8AC7Dd5D144eFaF": {
|
||||
"optimalUsageFactorLong": "0x6758150d36bd49f2cf7e45b455012350f4c1146c13bdbd4cdc28ea55cd71e316",
|
||||
"optimalUsageFactorShort": "0x9a7ec04e07d2316bd65469899746e1c285db5bbe3240ef4060e3c11ce8ae4df5",
|
||||
"baseBorrowingFactorLong": "0xb53527bebd470fb83050b3869b09b97e5ebb9fe3f180833281fe71ccaadb200e",
|
||||
"baseBorrowingFactorShort": "0xfcece4d6deca50446c64fa5fa5d2af6f6e49692abe4364f7f58013e80afa0dc4",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x3f884f571097eadcba55b27095cdc82ef3dcb2b79e0838e8ee71a01affed0816",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xb36cf9dd1ae5b776c21d7b0a6e469614d7067d23fa42751ab174a736afb70629"
|
||||
},
|
||||
"0xBb532Ab4923C23c2bfA455151B14fec177a34C0D": {
|
||||
"optimalUsageFactorLong": "0x75fd219b8629177fb7acf93eaa7fac5870e2cc069e203920a41b4a37aa010f9c",
|
||||
"optimalUsageFactorShort": "0xe454ed049bb948c915476a75b235a24c447372eb2cb7007200012a1052195448",
|
||||
"baseBorrowingFactorLong": "0xafe05d9f07f23a276e58ed97f1a88191d4316b3222c1f214bd3e99511a85c892",
|
||||
"baseBorrowingFactorShort": "0xcabe7245363c65f9bf212740bad39323939ea9d58f253cc03b17fd7f2ca1e83a",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xf32454317f5f8ee7728d4dea105693e9e39b19b4873097108c6aa10acc3f5dad",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x9d6bfae49b9a43ddd279d371ae2d2411e79cbe4d9cb9df90aa0a8b7b3251f62a"
|
||||
},
|
||||
"0xb6fC4C9eB02C35A134044526C62bb15014Ac0Bcc": {
|
||||
"optimalUsageFactorLong": "0x39d0409d01146b0ba49cfcc3c9b8acf01330e70a1f331c10ab5a1c62dd7d6019",
|
||||
"optimalUsageFactorShort": "0x4a0417fe45068725e2e3cb65912cc361ca6be12a1ea4cf49c72d3cdb9484a098",
|
||||
"baseBorrowingFactorLong": "0x543d708670c459c9f8d99df36fb9bb1439d70254316b5115ab20663c4882f1a1",
|
||||
"baseBorrowingFactorShort": "0x744984e2df298f85753485477cb5391581b19da90c81e982469f960b237f784b",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x9cd4468687a60514947b2540388be2b30e0460c34e0918f963f549c378a16cd9",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xf09c1ae2f99b6ee3757da419eff4dccc064a486e3fd0e9aa738b6ca32e76bb77"
|
||||
},
|
||||
"0x3A83246bDDD60c4e71c91c10D9A66Fd64399bBCf": {
|
||||
"optimalUsageFactorLong": "0xc51db96778409d633aee5ec25d8fecda981ed0771fe9ea24a6d8f5f6c98418e1",
|
||||
"optimalUsageFactorShort": "0x57dc47eb52a0a422319dd2f0e97b6df1b5d268611c38a0b822cb5b07317331d9",
|
||||
"baseBorrowingFactorLong": "0xfe52fceebf4b2604b07dd8e7e0c6fe439d7b7f1eea5616eb9cf271928a76d817",
|
||||
"baseBorrowingFactorShort": "0xc5c09d7b447503db6827286e552d6fa64e91ea3f8a3d1f71617ce492aabc6a53",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0x3ee19bd398d803e76c7bffbb1e7300dc675b8cea1e3b39b514164eead7a6b549",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0xb3a445394f1161006fc1f9159b741726c697158bd79724039c1d9f07033aff9c"
|
||||
},
|
||||
"0xAde9D177B9E060D2064ee9F798125e6539fDaA1c": {
|
||||
"optimalUsageFactorLong": "0x7136f26f09b6e9de850ea3cd43b4f6a60779ff34ba9ac83e30fef73389bf3ba1",
|
||||
"optimalUsageFactorShort": "0x5537de50a15619be6b974fec617f4282f8b82cc29c22479c91e6d8e85de313e7",
|
||||
"baseBorrowingFactorLong": "0x341ce2c61502e18c0a11d0889850a521bfd796e443b84c24aed2a5d1cb4ec3c4",
|
||||
"baseBorrowingFactorShort": "0x08ebdf8759d1b00beab7f018572ca0db3f029ac3f5608c11bd22a65c9e7540ff",
|
||||
"aboveOptimalUsageBorrowingFactorLong": "0xfa8a2b862611b3e908f294a3daad7a493fe2665e5a266b6297b59662b2d487a5",
|
||||
"aboveOptimalUsageBorrowingFactorShort": "0x56ee4e8daf9b4eb0d069222038c5e55ae54264aead84d9499839eef176e12c2a"
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,51 @@
|
||||
{
|
||||
"3637": {
|
||||
"0x6682BB60590a045A956541B1433f016Ed22E361d": {
|
||||
"longPoolAmount": "0x69a00ee30c22f748c1aa54b8f77a5b786c365e817497322cea18cba5e12f8446",
|
||||
"shortPoolAmount": "0x69a00ee30c22f748c1aa54b8f77a5b786c365e817497322cea18cba5e12f8446",
|
||||
"positionImpactPoolAmount": "0xe72b2da1beeaa2cde5f5861b60f1adb27ed581864c6e39ecb37f8794e03e174d",
|
||||
"swapImpactPoolAmountLong": "0x02603eac1d091c38978f864769361f985169348da32b431d4c0ee7c8559fddc1",
|
||||
"swapImpactPoolAmountShort": "0x02603eac1d091c38978f864769361f985169348da32b431d4c0ee7c8559fddc1",
|
||||
"longInterestUsingLongToken": "0x214be68b117ab3421e08bccb2521f805f76ae486dc92f1464abb3d7ed9ce907c",
|
||||
"longInterestUsingShortToken": "0x214be68b117ab3421e08bccb2521f805f76ae486dc92f1464abb3d7ed9ce907c",
|
||||
"shortInterestUsingLongToken": "0x44a1c0cc7e5e724918f78b1ae77c240bc1dec8098ddefd9fb15dfb4e06f58b39",
|
||||
"shortInterestUsingShortToken": "0x44a1c0cc7e5e724918f78b1ae77c240bc1dec8098ddefd9fb15dfb4e06f58b39",
|
||||
"longInterestInTokensUsingLongToken": "0xddc7e3aefe087c9ff970def36636c36cefa45ea75bf9d99c2747b921d46e3936",
|
||||
"longInterestInTokensUsingShortToken": "0xddc7e3aefe087c9ff970def36636c36cefa45ea75bf9d99c2747b921d46e3936",
|
||||
"shortInterestInTokensUsingLongToken": "0x85a20a5a323d16c5f5ecbc2d6352b4f47dc7921eef88e4cc987b3c8022e6a87f",
|
||||
"shortInterestInTokensUsingShortToken": "0x85a20a5a323d16c5f5ecbc2d6352b4f47dc7921eef88e4cc987b3c8022e6a87f"
|
||||
},
|
||||
"0x2f95a2529328E427d3204555F164B1102086690E": {
|
||||
"longPoolAmount": "0xecf9656e2b727f8d13f37e55d2400a942dccac9d6381148f77d902fcbe2cf7ee",
|
||||
"shortPoolAmount": "0xcfec2d18f35126352a2ce695309d775bbee4fafefe3413f7042939145a8b0573",
|
||||
"positionImpactPoolAmount": "0xf6f96ba2726dd8a85b2e9405309d1d848df3d96880d59321dcc218ef8d980668",
|
||||
"swapImpactPoolAmountLong": "0x357c18aaf9d45a361d90863904ade24e17fb77c64a416ece27086f31a0d8b054",
|
||||
"swapImpactPoolAmountShort": "0xbbb05a1d798d61588b2c44b94b16386d41137dbe373c187371d86d997a9147e1",
|
||||
"longInterestUsingLongToken": "0x129ec5ddb1853ff59fe335e75bd2fcfa6bbb9269fdc499282cf18e2f4d5964c9",
|
||||
"longInterestUsingShortToken": "0x3f654d7efcfe4f6cb7c83aa01e873c16e5af513cc4f87adf6eed255042a258fc",
|
||||
"shortInterestUsingLongToken": "0x1f410105f8be87f75b58b9cdee8d74667aced7496bb99a82f07f68db7ed1beb4",
|
||||
"shortInterestUsingShortToken": "0x51765c7340efd23bef0fef20734bbc293d17b324540a253b57bd0337038c7668",
|
||||
"longInterestInTokensUsingLongToken": "0x7f172a05ae40ac360e65ac522e8714499e4695d2250d219b96a6cf33fdbc083b",
|
||||
"longInterestInTokensUsingShortToken": "0xc34e82056451cf4f589cdb5609780092eef55a11937ecb6b97120c6425979ea4",
|
||||
"shortInterestInTokensUsingLongToken": "0xdfc2472d4b9b2c6f4b2754c1ade2bef5e4fe747aa61d86922f62b34d4f9b09bb",
|
||||
"shortInterestInTokensUsingShortToken": "0x3eff00ec29f463c99b6b378801ecb7b167366433326231573f74fa7ae20bbe20"
|
||||
},
|
||||
"0x6bFDD025827F7CE130BcfC446927AEF34ae2a98d": {
|
||||
"longPoolAmount": "0x3e705a690a4f6e12b648b46021d64b1a19979c3930bc633008e776bfdcffa768",
|
||||
"shortPoolAmount": "0x3e705a690a4f6e12b648b46021d64b1a19979c3930bc633008e776bfdcffa768",
|
||||
"positionImpactPoolAmount": "0x1be724cd8642f6c98c8e6633f9f575d86a98fece1cd51e45ed7069c050dd1d63",
|
||||
"swapImpactPoolAmountLong": "0xeb84c49e6ea3a599a47e95dda2e1d815088f3d9e477e2ddec50ee2f862e98a17",
|
||||
"swapImpactPoolAmountShort": "0xeb84c49e6ea3a599a47e95dda2e1d815088f3d9e477e2ddec50ee2f862e98a17",
|
||||
"longInterestUsingLongToken": "0x14569ffe0e8d775adefb50fc1d34370af1b37a000cafbb1f1ae069b594f9f0bd",
|
||||
"longInterestUsingShortToken": "0x14569ffe0e8d775adefb50fc1d34370af1b37a000cafbb1f1ae069b594f9f0bd",
|
||||
"shortInterestUsingLongToken": "0xe37270197048b507763f8de54b0cc4c02fe2d03bf112ddf44d60e119144de97f",
|
||||
"shortInterestUsingShortToken": "0xe37270197048b507763f8de54b0cc4c02fe2d03bf112ddf44d60e119144de97f",
|
||||
"longInterestInTokensUsingLongToken": "0x117853215d2155afd79c91f4586cd55c7cc7fa8885a5ae4cb9203c9df1e8f7e5",
|
||||
"longInterestInTokensUsingShortToken": "0x117853215d2155afd79c91f4586cd55c7cc7fa8885a5ae4cb9203c9df1e8f7e5",
|
||||
"shortInterestInTokensUsingLongToken": "0xe8a3c7877b603e90c025cd38a1f20d1b94f4f7a8ec7054e17b629218c7474c27",
|
||||
"shortInterestInTokensUsingShortToken": "0xe8a3c7877b603e90c025cd38a1f20d1b94f4f7a8ec7054e17b629218c7474c27"
|
||||
}
|
||||
},
|
||||
"42161": {
|
||||
"0x47c031236e19d024b42f8AE6780E44A573170703": {
|
||||
"longPoolAmount": "0xc97d6ba9e3cbf19c2bdb3eb671109e97cbbb0c4684ac5ea9b34fffbf3fff84fb",
|
||||
@@ -1214,6 +1261,366 @@
|
||||
"longInterestInTokensUsingShortToken": "0x6f542fdc6addee4e75891c4bfb8860c6232b10e35d2cf34d56a31f6e37a073a4",
|
||||
"shortInterestInTokensUsingLongToken": "0xbe1d49d8de9f15dd608c0ec6671808b8204838b14e8d31769b30489ee71e6e8e",
|
||||
"shortInterestInTokensUsingShortToken": "0xb19e3d111bcebca624cd2077e9c21d2f53f98e22648b3625c51cdcaf1ca09ddd"
|
||||
},
|
||||
"0x4D3Eb91efd36C2b74181F34B111bc1E91a0d0cb4": {
|
||||
"longPoolAmount": "0xcfdeeee75cef8de81f3e98bc2ee46390b1ebb489016a64e945f8df208913cd15",
|
||||
"shortPoolAmount": "0x819197d0e2f89533aaf9f8f682459b4a9632d54904ec9e5a550c76963ae99f8b",
|
||||
"positionImpactPoolAmount": "0x744ca86ca9666aad1339f04e710e921b3d1ebc7c56ed244640e5088fa98d6757",
|
||||
"swapImpactPoolAmountLong": "0x762b06f45ebc57521994d8ebf527c46abf5058816c792501f0bf625506e82d35",
|
||||
"swapImpactPoolAmountShort": "0x58950f4dfb0a69cfd67091901b12864103bb9cad22c497d86b4d4d258cf85687",
|
||||
"longInterestUsingLongToken": "0xa31015cdcb4172aaed407c2d933b627f7a8e46999aa67ae98973a8fe9b8c6a15",
|
||||
"longInterestUsingShortToken": "0x139a84e762d9799d19c51f6fb7241c93ea222cd11cfe52aae1306d8825cc6e42",
|
||||
"shortInterestUsingLongToken": "0x3d6d99fcb9adf2d5dcfe04612a0c670605275b61a237d5a077d2eb3044ad8f6a",
|
||||
"shortInterestUsingShortToken": "0xe0e0724df520d9ed9306b7611305b3d4ee8a7b7c48296c8a1bb7df9ad33a4807",
|
||||
"longInterestInTokensUsingLongToken": "0xe992d65208bb38ea6097352425d679b71a75429322dc6b4c872c9451cadd26a6",
|
||||
"longInterestInTokensUsingShortToken": "0xdf1bda8a83a0560547c25e3e4ca770baecbd6a61efb81b6d5e8e4e912f054329",
|
||||
"shortInterestInTokensUsingLongToken": "0x0d9c0848acc691d704ebb980a19c1661d77056a084d0147232952666d6d7780c",
|
||||
"shortInterestInTokensUsingShortToken": "0x41d05cc1bd601fe40bf8e5a838e9e832eb3bd6d9a56dabcf470e99d5e52c3de9"
|
||||
},
|
||||
"0x9e79146b3A022Af44E0708c6794F03Ef798381A5": {
|
||||
"longPoolAmount": "0x9c617050f77600ca856ef1119cbd36bb1eec68a511bb34b762bbaa699ef70793",
|
||||
"shortPoolAmount": "0x2d53f1549b165f9f8db0d7ec7e02680e970f29434a615215d9aaa894a19c2468",
|
||||
"positionImpactPoolAmount": "0x065cc9b8f43747f67442bc10b49e0ed840fdfa7a8d8e0702866a21749de3b62a",
|
||||
"swapImpactPoolAmountLong": "0xb1a0d964e70a12baf428183526200debe0c74f56af4ea665ea0b479f5575a75e",
|
||||
"swapImpactPoolAmountShort": "0x56f00e78bdb148aaead6242be6b03e4c9201ee4b80465bdc1cd19cab797536ca",
|
||||
"longInterestUsingLongToken": "0x008b836e3b01bb5b5c7fd81c366681c398c24bd81b5ad3cd44c272c98cb95a4a",
|
||||
"longInterestUsingShortToken": "0xc466b0c4230ff4b64b09c87515017bf0bfa42f419243f973c8860fe547d477e7",
|
||||
"shortInterestUsingLongToken": "0x04a5833e48beb387f1aff721fe09b0e0b277734e07c1c701b72f16751937d793",
|
||||
"shortInterestUsingShortToken": "0x09ef0af5dbe9d78c25fa7fe47bab4c53897b0221b49490023638af367d3ff665",
|
||||
"longInterestInTokensUsingLongToken": "0xf58d64f0e41f61a49a828be86024481a12e29015cdaa606bed07928b1838bcde",
|
||||
"longInterestInTokensUsingShortToken": "0x401b4f7fe4e249488787c5e8ed3d5b0b097ac08e418f45b7cb148b2c9fed9f43",
|
||||
"shortInterestInTokensUsingLongToken": "0xd30216795b710aa170af3198f18a117b7bd846a98051fdd9d73b9a80002223b9",
|
||||
"shortInterestInTokensUsingShortToken": "0xdfddd1e52c69cfcb2242fe606793a01faa8fa0bbbf2e482c89f600e74dd24f09"
|
||||
},
|
||||
"0x0e46941F9bfF8d0784BFfa3d0D7883CDb82D7aE7": {
|
||||
"longPoolAmount": "0x76339270fdf9fe51d24e487304213bdb0e24389b875d0e7d3f7398a8c92de8b5",
|
||||
"shortPoolAmount": "0xa1210429f9581af23981f66de5daca169404b4e37c8daca06dc62fe56a947cf4",
|
||||
"positionImpactPoolAmount": "0x89e555a84a9efa20e5ce28cb2a0df69557d180ab3da7cb1cc925162dd833cfc2",
|
||||
"swapImpactPoolAmountLong": "0xfbcb206ea4f7f1710bed958a5c148695f00afa1483f7f239f123720fe3d7c04e",
|
||||
"swapImpactPoolAmountShort": "0x86edc84a8c9ee6a09d745337c03d8005efd194acdddef85e36054d8313d36980",
|
||||
"longInterestUsingLongToken": "0xc99f3886c7d51b7d601686086b17ce369710a8d8af5c841a04a9eee87168fa68",
|
||||
"longInterestUsingShortToken": "0x839a0605a5833c4bf23d839f9753ad7b7f527d1d70fd49e229829e81d1b0e75b",
|
||||
"shortInterestUsingLongToken": "0xaf71d1be4d68c93f86dd1231424dc8978f28799e03e76aee5b3b1d8e219735d1",
|
||||
"shortInterestUsingShortToken": "0x77ea0d9da2ae89c287618474189f7d0cbd002043bf9e1524b3a20ed9097ec4cf",
|
||||
"longInterestInTokensUsingLongToken": "0xa9c8c0075d6b9f7ea3257f5868d91a1066be9c72cbfd79c3378398677fc454e9",
|
||||
"longInterestInTokensUsingShortToken": "0x990383f8aeebd168f8a25a52959bac05b31864a40946078e9c1821cc31997f17",
|
||||
"shortInterestInTokensUsingLongToken": "0x5f86375e18d3884992954c21ebe2191fc875d5d7514d830c967296d98ad22e03",
|
||||
"shortInterestInTokensUsingShortToken": "0xdef08d11b573ba8e3b6d919c724a60bbed677501313332e2d09bf3ef4e4a057f"
|
||||
},
|
||||
"0x2523B89298908FEf4c5e5bd6F55F20926e22058f": {
|
||||
"longPoolAmount": "0xc1cb4d3d87e1401e8b451df65d5bb41d628a71fce88be244b1be8a137ca5390a",
|
||||
"shortPoolAmount": "0x47e03c22f82ad390e58372207fd4c6d5a1685935568a343d2967fc263447ff4b",
|
||||
"positionImpactPoolAmount": "0x789583d3f45ef3e09b31e99fc1b3b47d1bdc3c179d4133b04e8f6a2810a171b4",
|
||||
"swapImpactPoolAmountLong": "0xc196aa2876aed0daa4a963857bf348cfc35acbfb7f5b3131c43cb63ddc104f58",
|
||||
"swapImpactPoolAmountShort": "0x37f0eabb9281ed4c4e87eb6c8b1a8264733d74deb3a2736908ee2e1d332bacb2",
|
||||
"longInterestUsingLongToken": "0x197bac785d03ea078e0b1fec22575baa8fdac1acd38fa15aefdfe092b2095477",
|
||||
"longInterestUsingShortToken": "0x28c25793551271bd5b21b8c86f31e16060dfe01e3ad6046c6f4be2bfbedd437c",
|
||||
"shortInterestUsingLongToken": "0xcb64f5ea4b0fffa761f1e828104bc0f49a7f887cf54731cec3634a4b7631427d",
|
||||
"shortInterestUsingShortToken": "0x585a446a024173ef0268da647ad0df5d5644b39235073e6d4e6da49f4c71deef",
|
||||
"longInterestInTokensUsingLongToken": "0x26e7ea7037b83a38bafbd2a65ca4a96371b56d6549ae8255a8aa820e2c5112e5",
|
||||
"longInterestInTokensUsingShortToken": "0x53cabdd1589982ca208ed4eccb26b9a4c84f02219b3097d7bfa348de853c1f15",
|
||||
"shortInterestInTokensUsingLongToken": "0xdd32f7b3d7add0fce0abaae3bef28acffc2e581605a60f6f272781352ddea05e",
|
||||
"shortInterestInTokensUsingShortToken": "0x713b5c0f4d0910279e8d3bad2e6d80f784c6d0ba3fb55ce6e0327937d4b81bc5"
|
||||
},
|
||||
"0x7c54D547FAD72f8AFbf6E5b04403A0168b654C6f": {
|
||||
"longPoolAmount": "0xead0643a2dc9e50fd5d68a68feee0b1bab6f98c5acc68e480bbec9ae7ee920f2",
|
||||
"shortPoolAmount": "0x3c6786065166f7f8a3829b4d978a706c91c622febcbfa56eb8308bd23fa1d352",
|
||||
"positionImpactPoolAmount": "0xd59fbf1f47b4fa2fb02b36548fae94bbf1e3a3d26fa5e0c5250066dc583c0839",
|
||||
"swapImpactPoolAmountLong": "0xbf213e14458ad58545aea523ec0105609069dad6b6b017eda8f0024daf63422b",
|
||||
"swapImpactPoolAmountShort": "0xf45fc23e8e69e28a9fa3a184fb43f255defdde09b840bfbca5192cda38b4f960",
|
||||
"longInterestUsingLongToken": "0x185834a73a6749224b3fcb782fc3ef073266aa5d77e39857125524964b2764b6",
|
||||
"longInterestUsingShortToken": "0xf2c5529f9011666344b81373a841fd14bb1b97f7562b761fd8b678302906eb4c",
|
||||
"shortInterestUsingLongToken": "0x33d0f93fc2126fe4d94c308eae266696b2b13adf7c0221d89f4e62020af79584",
|
||||
"shortInterestUsingShortToken": "0x466523fd838cf12da1d164b26cfb2eccec195bd29d8aecbf49cdd60ed06c56d8",
|
||||
"longInterestInTokensUsingLongToken": "0x5c1f450df618dc8dd4e3f46d6ddfa8f8e27a857ba2934ec5d4cec14182cd7b34",
|
||||
"longInterestInTokensUsingShortToken": "0xebd0844d77cf10b0a53213d880054e84a66dfd57853c583cbce83e124dc24a96",
|
||||
"shortInterestInTokensUsingLongToken": "0x09467669e73c2ebad9ae4ce2aa06ca52348ae5da2d25be4c35b608e4dff0e2bc",
|
||||
"shortInterestInTokensUsingShortToken": "0x4f4bacd66cdf5d93d6c55dd2c019d75653b63183bef9db11f98764fdd8cc60c7"
|
||||
},
|
||||
"0x39AC3C494950A4363D739201BA5A0861265C9ae5": {
|
||||
"longPoolAmount": "0x8b35db61cf1d679856d17037bff17b6d5909ef3d237ec5fddb6e9c45a2553c95",
|
||||
"shortPoolAmount": "0x74b22e46171010c4d6867a3420af82b00c739ec3bbeb295a4cf625bcdbfe07b5",
|
||||
"positionImpactPoolAmount": "0x9435975a828122df0431350d47a44d4700715d393c9d5f55a4fe7d1972f1d95f",
|
||||
"swapImpactPoolAmountLong": "0x9113211843bb2a4695cb03f9350950920a54e63b8a69010a2a9b440fc41d6ef8",
|
||||
"swapImpactPoolAmountShort": "0xa9a4df803ab35db2d17f39f87fd42b18a2830e58416f919391481f6d2e31a3c2",
|
||||
"longInterestUsingLongToken": "0x4e71590171ab7ef030cafac110a7385d65ce24f61c6c2edb04898654f04ec898",
|
||||
"longInterestUsingShortToken": "0x915ae07505869779440f15749260d59c6ea24880abd0d72c343b52d2b86b8f40",
|
||||
"shortInterestUsingLongToken": "0x829c7683ed83c5cff99e9e5313aff403cd26085ba4a43964d011d6bd4977efcd",
|
||||
"shortInterestUsingShortToken": "0xd193c75ba20a56756011040cfd14a59b30a569e75a1ac818c598210db4459c52",
|
||||
"longInterestInTokensUsingLongToken": "0x30d45feb138a7c16fa2eb74acaee67e95e1c60bc1e10c30301d3762b64883834",
|
||||
"longInterestInTokensUsingShortToken": "0x270eaf7ded63283fe668107dae6e74b2f65611f2ce8ac93f7587aa42690e726a",
|
||||
"shortInterestInTokensUsingLongToken": "0xba25770c0d2c19cb82136ca008f7f59e3f0a6273159a121852ef001e7fcf5cd4",
|
||||
"shortInterestInTokensUsingShortToken": "0x434a95fd250b7b15239714a1a92599a7706c946310e7cc999a66202318a3b9d5"
|
||||
},
|
||||
"0x4C0Bb704529Fa49A26bD854802d70206982c6f1B": {
|
||||
"longPoolAmount": "0xc996bc9829549ec431dc6686d166b850c834ca57d6d13eef76878eb034346f49",
|
||||
"shortPoolAmount": "0x73576063b41f5bfab75ba59775beae18756d9a05d1934205cd5c1c8ba136c37f",
|
||||
"positionImpactPoolAmount": "0x336c51bf0ded13ba7909d73214583e37c9b26efea4938aceda45c44e8a3daf11",
|
||||
"swapImpactPoolAmountLong": "0x82459c6efac2e94161ec6d0598601585325eef90f2af9881301f2278fee5099f",
|
||||
"swapImpactPoolAmountShort": "0x9f0dbc64c99da086cb864d7e695f4e7f266ca1440f08ec043ff6ed3665184d81",
|
||||
"longInterestUsingLongToken": "0x7994ea7561117141e087e7af8aa01cf4051b35e85120e52501d1e5796828d4b9",
|
||||
"longInterestUsingShortToken": "0xb5d3a077dd57f1759a6d816686fbf4107a01d451800b80f4ac2355fa60c2310f",
|
||||
"shortInterestUsingLongToken": "0xb28ff06b21c5927f46a4156fee083f0c7441fd5c53971d47344678e9ef9cf0ae",
|
||||
"shortInterestUsingShortToken": "0xf2729a78f904139a3e33a1278c11af17d3b46e5f5ca6223c397814f052e5d019",
|
||||
"longInterestInTokensUsingLongToken": "0xaeeeb61d7d9544320d5c9223f4d671475bfb227124129ec581bdd1c9427961b0",
|
||||
"longInterestInTokensUsingShortToken": "0x3f8f433c8c804ca8bc6c5c383e9201516559ee81f67de93db9ce8b2c2ee2403b",
|
||||
"shortInterestInTokensUsingLongToken": "0xf8b11af0ad2d1ab5f63791944792e3d3c96e5973b7f003dd86bd4314a25dcda9",
|
||||
"shortInterestInTokensUsingShortToken": "0x4ed2301ae42565af0bc559a51c13c60223d0159f349ccc0efc2f080d3a467298"
|
||||
},
|
||||
"0x8263bC3766a09f6dD4Bab04b4bf8D45F2B0973FF": {
|
||||
"longPoolAmount": "0x376d10c7c9e12d555cc72da9c11f290bb948dee849e7ac53c9919cb7ea326356",
|
||||
"shortPoolAmount": "0x0328aa07dddd94e5b175e0c18e2a038a87ef77dbe0f01598abfd5334e14f8e5e",
|
||||
"positionImpactPoolAmount": "0xec2057fa3283a4b58a58071d0fc709fbabe164669de94f39a32c0f9e6d8ad857",
|
||||
"swapImpactPoolAmountLong": "0xcad777762cbf8191fdc7a83fed0b6dc7267c3ec95deafbc3f278c78640f5def2",
|
||||
"swapImpactPoolAmountShort": "0x4ea39496d661599d66dd7c36f636e2beb1d9f22d494251079661ef97b260796b",
|
||||
"longInterestUsingLongToken": "0x60b2619176219b8e25afc32c0822b3ff9866f234f23171cd5c443e1307aadb83",
|
||||
"longInterestUsingShortToken": "0x2d9f098282c2b594ffa540647b1ac0c6209b17e64061586c5c89f70b7f1e561c",
|
||||
"shortInterestUsingLongToken": "0x46574cd73b54eb902ef9f6257cf28096088a744437a313e50ac6a3dcdde873ad",
|
||||
"shortInterestUsingShortToken": "0xa8bbf0769960efdfe8fcd0fb1dd38f6713bf76f3980f613aee0fb1c8fbcb5fa6",
|
||||
"longInterestInTokensUsingLongToken": "0x64f6ab661c3fb1f4782cee482f58fa61e4a8e8f100f407793406835061104b48",
|
||||
"longInterestInTokensUsingShortToken": "0x2aa02635cddc8d2c7be1cb924b3bb933eeb0fea6fa8a5a480180c0d0cb7d3aca",
|
||||
"shortInterestInTokensUsingLongToken": "0x82047a97d39c5c336f198c2c03598016c99e5e7c055d3507be800f23e516aef6",
|
||||
"shortInterestInTokensUsingShortToken": "0xe2de08baa4086bcdaa890f544b6144c2a3e1f7687a0724b6d9211851eb8851cb"
|
||||
},
|
||||
"0x40dAEAc02dCf6b3c51F9151f532C21DCEF2F7E63": {
|
||||
"longPoolAmount": "0x3ada05b4ab6a01c1e9d5d14a6623330ac706b167ef0a071c44caae0f2e86fdc5",
|
||||
"shortPoolAmount": "0x4549e636396f94e60a42c4e6cf6de9cc4641b422616da34afad3bb545252d55b",
|
||||
"positionImpactPoolAmount": "0x7ac4153f38da7f40234e4278155360da68d5d7ba448b8406a58a32e3984e1c84",
|
||||
"swapImpactPoolAmountLong": "0x23d98dcb90fa99c0c0afaf8da74219739891cb8110408cf62f0ff7ee9791b841",
|
||||
"swapImpactPoolAmountShort": "0x67689a5fe771bcce0255ea6af7497ebdf969b44a2925b0a7d57ab813a7d92528",
|
||||
"longInterestUsingLongToken": "0xb54834f5f22fd80d4bff9e5fad82cdc0812f9c92145c9213d61ec65e023f4a49",
|
||||
"longInterestUsingShortToken": "0x6b03ae70dddc4ea9ffac583d90730c4deb7aa91f2e05b0ef4496373135f501db",
|
||||
"shortInterestUsingLongToken": "0xb7825b979161efdf744c59e3a2f1ce9a946401f1fff467cdda4c79b4eeef1dee",
|
||||
"shortInterestUsingShortToken": "0x20d46f0c26c10020a4caa0b8508e679b5c0366ffa1957348f4af12e02e8ae627",
|
||||
"longInterestInTokensUsingLongToken": "0xb05b543fd027e0faeac576b85e5eb8dd16dbe9b0a287951e880462f74428dbe7",
|
||||
"longInterestInTokensUsingShortToken": "0x548e4ec925976c9573b2513f3e04ccb85992e7b2f9fcadef9d390972901249d2",
|
||||
"shortInterestInTokensUsingLongToken": "0x43eeca3a191fb5f9d6e028d433477afb80d8bc8be6d4736ca40f72100ed01760",
|
||||
"shortInterestInTokensUsingShortToken": "0x08fb093e1d5256360f715652095ebef083a166cb62c222f96d377cc7aee3e961"
|
||||
},
|
||||
"0x672fEA44f4583DdaD620d60C1Ac31021F47558Cb": {
|
||||
"longPoolAmount": "0xd88d77f52c0605af9087b8ac011f7ce71651f30489699ab476eaaf49fe3169be",
|
||||
"shortPoolAmount": "0xd88d77f52c0605af9087b8ac011f7ce71651f30489699ab476eaaf49fe3169be",
|
||||
"positionImpactPoolAmount": "0xc7f1c4797976d4aa2d9af5b9b4bac9af3820775e22a642a5f882d0040e30d7e4",
|
||||
"swapImpactPoolAmountLong": "0x10f2963890a8ae4e833e0f611420b6c41136c2ff83a764654cddfcb185ce39ec",
|
||||
"swapImpactPoolAmountShort": "0x10f2963890a8ae4e833e0f611420b6c41136c2ff83a764654cddfcb185ce39ec",
|
||||
"longInterestUsingLongToken": "0x5ebd54efa4eec2c74d89f7fdbbad947ed4ade7a369f87b940aa4c455a8140f8f",
|
||||
"longInterestUsingShortToken": "0x5ebd54efa4eec2c74d89f7fdbbad947ed4ade7a369f87b940aa4c455a8140f8f",
|
||||
"shortInterestUsingLongToken": "0x300b925f106354291a9776ac37a374aa7149da134418f5f8bdc2b59739fa7829",
|
||||
"shortInterestUsingShortToken": "0x300b925f106354291a9776ac37a374aa7149da134418f5f8bdc2b59739fa7829",
|
||||
"longInterestInTokensUsingLongToken": "0x8ff57a652cd892eade9757b138549656bc9f8dc00d9eeeb3fc4f9bd6f2cc2bd4",
|
||||
"longInterestInTokensUsingShortToken": "0x8ff57a652cd892eade9757b138549656bc9f8dc00d9eeeb3fc4f9bd6f2cc2bd4",
|
||||
"shortInterestInTokensUsingLongToken": "0x81b4fc3022e9cb293b10549fa7ff90ca91abec03ca725ded69a4b35c09858ddf",
|
||||
"shortInterestInTokensUsingShortToken": "0x81b4fc3022e9cb293b10549fa7ff90ca91abec03ca725ded69a4b35c09858ddf"
|
||||
},
|
||||
"0x3B7f4e4Cf2fa43df013d2B32673e6A01d29ab2Ac": {
|
||||
"longPoolAmount": "0x950f55352377af24d3a96bc2a3a38d1e5dfbf04c07d8364475feb8092ec75c64",
|
||||
"shortPoolAmount": "0x0704221c55eb30a2efec7b9d42ad18b41c308c26c638e85ce53d08cc694bb340",
|
||||
"positionImpactPoolAmount": "0x757e7478893be5bf67fc10551f9ed34f1403a3628a8ff3f99bafa17b3ea66b79",
|
||||
"swapImpactPoolAmountLong": "0x7d1c81974ef4c7afb3592f7ebd245358952ca21333678e9ed0685d30b02e8a0a",
|
||||
"swapImpactPoolAmountShort": "0x71ba227bbc9232cd200c2a208ce26cb2991da321664a31bbd5d410174d07eefa",
|
||||
"longInterestUsingLongToken": "0xffbd879ff459c888617ba08141ca99969c83ac9de54990cab294ce2edd9fc9bd",
|
||||
"longInterestUsingShortToken": "0xb86c0a56965a83b03c7f93e6612efc66d78eaffc027d8877b4edf23ad75fa4cc",
|
||||
"shortInterestUsingLongToken": "0x769be22d39b7606b33a7734d76e3b9416cba72fff936b26a9ddb7875b4497dc3",
|
||||
"shortInterestUsingShortToken": "0x1746986fd30dbb8fb1939fe11604f11eeb57acbc85b1a76b5c78551798afdea7",
|
||||
"longInterestInTokensUsingLongToken": "0x19507cd2da36e6ca0e96b043c6b0e853e477cd1aab3ee7f74db45f918910549d",
|
||||
"longInterestInTokensUsingShortToken": "0x8d9c0ab0775b9405e5f54458dcbe28bfa2a49a07e39253b6d9735a77ba8a6c16",
|
||||
"shortInterestInTokensUsingLongToken": "0x10863b6d78d3d1746d0bb8de8b7e3df021ddcdc1008d6c1ede3c3bfb30f08199",
|
||||
"shortInterestInTokensUsingShortToken": "0x98724366736175a92826c63d94d611bc2ea3a63f24714d71c2c106a24a7548ea"
|
||||
},
|
||||
"0xa29FfE4152B65A0347512Ae5c6A4Bbc7a3d6d51B": {
|
||||
"longPoolAmount": "0x6693ae65f8c7bbbdb4dafdc6e2d2a4824b589b7c7454b411517e795feae42bc8",
|
||||
"shortPoolAmount": "0x29057b0a5f9faf83d5b250aa95a44056aafe0c87f6ebbed9ff5bede52e4b2d7e",
|
||||
"positionImpactPoolAmount": "0x2882e4b9bb2ad8ea954630b4ecb75982cf32cba38bac0acaf3db59d61735bee3",
|
||||
"swapImpactPoolAmountLong": "0xd496578938511700e33179b3c23bd8fed3ce421ad8800f4dfd11e3363ba2dddb",
|
||||
"swapImpactPoolAmountShort": "0x3308ca648709f2b017c80f87483d2f87e1f55bc67d0a7de075d50ef01af25a13",
|
||||
"longInterestUsingLongToken": "0xd5f0aacccbc59df807fe217610dff270e934ce85b5629a8bd93f723389fd628c",
|
||||
"longInterestUsingShortToken": "0x250a4ce3cc83fa6179cdff4c755cd1434fa1d2ca4fbc8dd0db896098ce0fd94a",
|
||||
"shortInterestUsingLongToken": "0x977c23ba871ab8a9572a25e627de71e8665f2ff6f3bcd6fb8aa1c033382ec0a1",
|
||||
"shortInterestUsingShortToken": "0xce032220c9bf7365cd2b408a5f6bb2df31f6220585ceaaffbecb714a182cba81",
|
||||
"longInterestInTokensUsingLongToken": "0xbb9538095139b99b8d1a471e5298108f9879203fdc9f88e302b98edbc810f377",
|
||||
"longInterestInTokensUsingShortToken": "0xaf453df5666a65e4ccc0f2a16fe04f10e5c7c44b0c24d8ebfdd1da10e0b57e1e",
|
||||
"shortInterestInTokensUsingLongToken": "0x168d1abf6abf13f235b7fbc88ac6a316e280110fb243fa96ba0d1d39184a8863",
|
||||
"shortInterestInTokensUsingShortToken": "0xfd8853a8fc94ff0f74ae1b99c4dd162d6a5673bf3a2b1d7cea2217cfecd8ad2a"
|
||||
},
|
||||
"0x9f0849FB830679829d1FB759b11236D375D15C78": {
|
||||
"longPoolAmount": "0x21b6ff319e87d9484b5155577154f1e7eb2a02719b2c78877e1b878bd628aab6",
|
||||
"shortPoolAmount": "0xcbcc6f24046ab5a5cb181328fc5279797aa888128db0943ca97d5e29cd07589c",
|
||||
"positionImpactPoolAmount": "0x7bc771e3bd2c1e339894d8cc0e339f3bd29571b1f81dc4aaab752d9d5c4565d8",
|
||||
"swapImpactPoolAmountLong": "0x9b8e8803dce19ab635bdcbd739151269bdcfe50181bdded95378e3fd86b32060",
|
||||
"swapImpactPoolAmountShort": "0x55f2af971ae258c2ac5ae48e016329ea7e3d41558f1171bc082f9de3c822dff3",
|
||||
"longInterestUsingLongToken": "0xe7339c70b7e5f38ed83fde6e7db586b5472612110751aa45da936bdbac49d176",
|
||||
"longInterestUsingShortToken": "0x8be7905da5f947c317e5183fdcb4ccfa7dc100dd7b612f5c7ba9ecba45220914",
|
||||
"shortInterestUsingLongToken": "0x35cff8db41f43cd082d5ec9e12c8e41e96e62da8ad6e9f62c24ab649ae2de828",
|
||||
"shortInterestUsingShortToken": "0xd933217c4dd384699dac11292fa70a208607ffbf9a63beb7bae9d66b1b7080b8",
|
||||
"longInterestInTokensUsingLongToken": "0x3953a9515c3bf950fa114c7de0c76524b7dce1188855ed5e1858ff72a757b4f3",
|
||||
"longInterestInTokensUsingShortToken": "0x0b102716f7adc9208283d13cf4d33943256dda36d1ee07862e50753261911492",
|
||||
"shortInterestInTokensUsingLongToken": "0x6a9fe892379ef84c1da651fb1b30387a0d01a1569c5351d4a18984b34bfd1f62",
|
||||
"shortInterestInTokensUsingShortToken": "0x84181252090dc10bfd3e2e1e50d08518e7699461a3fa413f0231e14bdacb16fd"
|
||||
},
|
||||
"0x41E3bC5B72384C8B26b559B7d16C2B81Fd36fbA2": {
|
||||
"longPoolAmount": "0xd6ed59f255e08bef3cf9e68aa634540a79f8cf8c24a8f1afbbdc50d71fdd5b73",
|
||||
"shortPoolAmount": "0x8de9f8593e9eeb6e5362d0d1c895805b3d1e668310efc235dcaa2d2c404c788f",
|
||||
"positionImpactPoolAmount": "0x7222de6cc3da940aca8ee2060a44d3de7e70ecd226f81f9a1b2ca69d6a6a2644",
|
||||
"swapImpactPoolAmountLong": "0x52336ec1e9c00866cbede2fbe31e97ff00baf2a6e3a26ba3620216a96c74e1cc",
|
||||
"swapImpactPoolAmountShort": "0x127deefe4568977525b74d0b273ed1d87786f0d42a695653794bbd73ff876f49",
|
||||
"longInterestUsingLongToken": "0xfa0eeda66aa4975b3a93cf07412f2cfeee9834076ff9c1e85f60f00a4c523462",
|
||||
"longInterestUsingShortToken": "0x08a35ff52c7b661c5dcb38f52398a128d4fc1aeae9c5c45ff08e5cba6e01d174",
|
||||
"shortInterestUsingLongToken": "0x7db9b4dee13ca7279a42d464d3031dea20b5b677f20cdf5b20710a82324945ec",
|
||||
"shortInterestUsingShortToken": "0x9c2a5fee2968fb07453d28eb67579914466110b820d4b96d9a48e7adcce43578",
|
||||
"longInterestInTokensUsingLongToken": "0xb5e89a6d37702072e196e5441e96699d88f453415a13db6e8d80ef6a25222c12",
|
||||
"longInterestInTokensUsingShortToken": "0xc76b943c3379d44c6d5a908c2c3fa46f9ec10174347bf7dd1658d2c1f027e60c",
|
||||
"shortInterestInTokensUsingLongToken": "0xc8b8935b9902170550dfb96508f2ea66040dfedd45158b208a858d20acbd929f",
|
||||
"shortInterestInTokensUsingShortToken": "0xfc8615b99a334d4bb6cc031e1094a9fe778c53077112dc1618c605b0b3e84dc7"
|
||||
},
|
||||
"0x4024418592450E4d62faB15e2f833FC03A3447dc": {
|
||||
"longPoolAmount": "0x83f34cc222753541fc5a71c11281066b32f516795bf4a8ef8e72ff7e0c54e436",
|
||||
"shortPoolAmount": "0x66fbafb6e6d8e08c11832bf31814caa8f6a95c301c3619bbeaa8835108e8a378",
|
||||
"positionImpactPoolAmount": "0x2541d06e4691a422b923bd6675ec85a5ce82eda37f4ec522b1c6b9256841dfe3",
|
||||
"swapImpactPoolAmountLong": "0xa085cf24fc0f0f4bd0bb963935565be9a367b5e8d90e154692b8d6c01f849f5b",
|
||||
"swapImpactPoolAmountShort": "0x4277b357f8145ba122dfc509d9615e7ffbd88f9c6ae1a647edbece5beab18b14",
|
||||
"longInterestUsingLongToken": "0x5e70ba3d2b3949a2b2ea13fd5b984b161752fe5617e02f777f2687a92dbe0c6e",
|
||||
"longInterestUsingShortToken": "0x7f8d92fae1514a43bde14520c2b62f05cdcb3c516f9c057aae527e5caff0650c",
|
||||
"shortInterestUsingLongToken": "0xb1d21632141d96aac72c4267fa7ab91127f3f3b4f6a239bd98025ff4d4904c25",
|
||||
"shortInterestUsingShortToken": "0xed6b6a776eb923184cdef0888b7c6131065dc62e9fb3e8485852626b9f830557",
|
||||
"longInterestInTokensUsingLongToken": "0xb8e1a132e4523d7ad7c86d630a12c9c166aaecc01293303a4ed54b43c6ace833",
|
||||
"longInterestInTokensUsingShortToken": "0xdd7f9cbbea3932ac56d5821e688b06257fdcad3e617f5f3a4a998019c7182e5e",
|
||||
"shortInterestInTokensUsingLongToken": "0x1e790e78ad84d7aa79f44402cd60cf352af29c5b6eead4c92244d7be1d538d1d",
|
||||
"shortInterestInTokensUsingShortToken": "0x8c0fdb55a8f1c62ce6e80c0563160e8c1c3e4b96d0a26ec7ab862ed027c264f5"
|
||||
},
|
||||
"0x2a331e51a3D17211852d8625a1029898450e539B": {
|
||||
"longPoolAmount": "0x659509d7fa0fda2d8e744e0c9644d219df684fbc50598b1d1b15d88b9313c681",
|
||||
"shortPoolAmount": "0x3e0c8ce5fb9cf02bb8556946902167b4841b475adc549fc5fb5c041b674bff9b",
|
||||
"positionImpactPoolAmount": "0x65c4c70da8cdc0fb0b45eb00c226f85deee481283ac2cd33f39c872fd3db5938",
|
||||
"swapImpactPoolAmountLong": "0xf72fa15da10d167ca27c544b595ecd700ea8329b10ed2631e9383b7b4b820922",
|
||||
"swapImpactPoolAmountShort": "0xae355549c57a687fd96229c3e5f02d2886c7d25ae243088d950cd86968e9a9ff",
|
||||
"longInterestUsingLongToken": "0xd46023d8348b8b885875e18a02e1d5736f685867ec4e8be5ee445cfa37d580f1",
|
||||
"longInterestUsingShortToken": "0x91ecc8e169c4a8f0b52cfdec15975121c4ca08e4719de7644915427222e11f7f",
|
||||
"shortInterestUsingLongToken": "0x02cf20750719c9ca3489788be07d62221679996956fda294cecf59948d182ed5",
|
||||
"shortInterestUsingShortToken": "0x7dcee765554639c9fc9ea5dbbd16dc17a2b17adadceda176a42ebe1416bc6ed9",
|
||||
"longInterestInTokensUsingLongToken": "0x10bbf01caf11a412eabf5f96f17e554bcaacf1bf851ceee9820a1be78c477aae",
|
||||
"longInterestInTokensUsingShortToken": "0x2bbf2a4970d56ecea00c8bcb796e89404bfd59aa8a97f1c088f19f00fd12ba31",
|
||||
"shortInterestInTokensUsingLongToken": "0x8ce7591973fe243cc0099e82295c51dd26024e0820aca410607fdd5a1427f1a2",
|
||||
"shortInterestInTokensUsingShortToken": "0xaf9d5fb1cefaf0a08e43a88456228b8e30e67fe43f6355db440585ec197ce6dd"
|
||||
},
|
||||
"0x3f649eab7f4CE4945F125939C64429Be2C5d0cB4": {
|
||||
"longPoolAmount": "0x50021591a0db3ce445862a6deccd582a8a89f7e7b8a395ff7c3e6a9c1641ebc0",
|
||||
"shortPoolAmount": "0x46e840b7349f19041d03c15334d9d36f598f934565c0c1d5cb7cc1444aa7af26",
|
||||
"positionImpactPoolAmount": "0xea29851583407889ceed629935a7725d49c1b1cdf8f7d9f36950c9fe0bec3d5c",
|
||||
"swapImpactPoolAmountLong": "0x55fd263f5a7b559af2a67b50ca6a156e8cfedb40660fb5f9c0683fed5b016a70",
|
||||
"swapImpactPoolAmountShort": "0x019bbdc9a4d0bf4d6fb72ba20350b1ae78d68261409062264dac47730129a7a2",
|
||||
"longInterestUsingLongToken": "0x2678021537f42a4dfbd6a3a9abbba466e9112f7d00a98920c853179e45080b49",
|
||||
"longInterestUsingShortToken": "0x254166ee412fb56fe6bf12ff0351026a135dfbb3a63a7d5e768c8d660a8ee14f",
|
||||
"shortInterestUsingLongToken": "0xa55c83874b02c061524f33b53fe646548ce37b83a4d9c8375087d3bf745062ef",
|
||||
"shortInterestUsingShortToken": "0x6dcbf98ae30f2d205771f8aaa0e84afd62175ad41cc6a6035fe5b6ad17cb3c97",
|
||||
"longInterestInTokensUsingLongToken": "0x4f56026fa3fd35ae7036b41c2a967c921ff837db320c1cce338da05b0528ddea",
|
||||
"longInterestInTokensUsingShortToken": "0x00e69b7bf298b90c16ef69ff36db4b035a598bff7b80f69269d4ea8683d8fcb1",
|
||||
"shortInterestInTokensUsingLongToken": "0xdaa2bc65d530f5cb53b34c1a342f0b0c1560ba589d2e9fea3bd3c63a839ecdd1",
|
||||
"shortInterestInTokensUsingShortToken": "0x01f14b9baf4d510252960662eed0c373507faa13b8141454255241fdf8435afc"
|
||||
},
|
||||
"0xfaEaE570B07618D3F10360608E43c241181c4614": {
|
||||
"longPoolAmount": "0x8a72f9dd5241aae4fec3f462150eb6ec079c0e57a268744249ad38644efa68ef",
|
||||
"shortPoolAmount": "0xcbe25ccc91ef8a53882bdf1d5f942cd8badd8d59c09e5b8617a21fea976603e0",
|
||||
"positionImpactPoolAmount": "0x9d2316aa3880c4d911fdd7cd8fc31d7f1c6cb1c89a0d816ba9cabc134f0d064f",
|
||||
"swapImpactPoolAmountLong": "0x64907bf4a3b9aeee952aa2f4fd03ad6775d536ebbcfa2d22e1ae1e35a2d338b0",
|
||||
"swapImpactPoolAmountShort": "0x959ad5a4dba9ac953420a0735929bd9f1e4f4a814aea3eea834fc06c88384afe",
|
||||
"longInterestUsingLongToken": "0x06a39814acd5d9756fc552e09ae57737aa3c62d174aa5e651ca74f5fbb8212cd",
|
||||
"longInterestUsingShortToken": "0x7ed444d60e663737325d75baf64a9d272ef5ca5b136f30144d475d45c8a7f774",
|
||||
"shortInterestUsingLongToken": "0x2826923e2508ed051f1d376cd7fd11fad83feb6c865a428231720640d2df7110",
|
||||
"shortInterestUsingShortToken": "0xb31048797c81f2c66f83b6ac15411f2570c7140c71cbdfc993cdae66963fef68",
|
||||
"longInterestInTokensUsingLongToken": "0x432027648cd2b1542d9bfb30e3979578b1e29eef0e0d3b4f88807ac37fe33513",
|
||||
"longInterestInTokensUsingShortToken": "0xad49f2d31a82ad8b9d6a80434d1729841da6477f672703ef3cc46d7fed32163a",
|
||||
"shortInterestInTokensUsingLongToken": "0xd173bc3d0d10312c88d35bf8eb1cdea9f71e7aeb48fa8b97fcb3e7b70debb76f",
|
||||
"shortInterestInTokensUsingShortToken": "0xf08346748fcaf05878849d20375b4bdde817abb3a030c259ceb5607d4d2db468"
|
||||
},
|
||||
"0x6EeE8098dBC106aEde99763FA5F955A5bBc42C50": {
|
||||
"longPoolAmount": "0xf0f68ff6a59536f53d75d1290668f43d86036aa93021b8c1a54f743eec140ae0",
|
||||
"shortPoolAmount": "0x12876f72e651deac6f33082e4d16bb817d98550d5cc9243b5b4a25fab6401315",
|
||||
"positionImpactPoolAmount": "0x17d4b7dbc22006be9840cb37fec14b70790cb6f69d4507706c64186827bb4c58",
|
||||
"swapImpactPoolAmountLong": "0xf6fa1917141d60ec14934a75f2d63ff3ed0e1f38688c8a421b1ab995d811ba56",
|
||||
"swapImpactPoolAmountShort": "0xfe036fe6d6a67d305e8927c485d1b78f4e8b05b913e0e72c092414b5e684e413",
|
||||
"longInterestUsingLongToken": "0x617c6f662ef4c095f73e9bfd59b61dc1c16030db39cc5d8e5d53150b622d8133",
|
||||
"longInterestUsingShortToken": "0x1abbb5e44eedad51541b1a193c0c032a38ccf2b41e5830a6e408600a5224080d",
|
||||
"shortInterestUsingLongToken": "0xbdb508d5b3e8f585c7b62dcf8325189eb23b47ca22096a063a97dfc732c9bf10",
|
||||
"shortInterestUsingShortToken": "0x64b1331a2bb3e6cd07c41c58906e6e124a222c1823746b2b4ae88c0c1cba2db5",
|
||||
"longInterestInTokensUsingLongToken": "0xd2e95ba95918bffa388956782ae752bc84c80f006821d0d25b6aea7a3b0673a9",
|
||||
"longInterestInTokensUsingShortToken": "0xdeb4a1cb7576524fdd7aca3983e6fa086ccdaed271bb8901b921e8aefd2d0290",
|
||||
"shortInterestInTokensUsingLongToken": "0x279f4f7193060e6d7a03cae98cb3600919f4c62f9bc45455c763fc9fd4036393",
|
||||
"shortInterestInTokensUsingShortToken": "0x3a5b89ac8075a9b0600d0bfbbc8750dc5240297f942646d4665e4ebaa27f99d6"
|
||||
},
|
||||
"0xb3588455858a49D3244237CEe00880CcB84b91Dd": {
|
||||
"longPoolAmount": "0xe2371c5c929d4777222ab27f74abce5bb121bc461c7348dbb986ea3c3a8ac782",
|
||||
"shortPoolAmount": "0xe37dbaf4f42147b19722527f418f97a33a7f0abf9f35d768ea661cf3d04bf49f",
|
||||
"positionImpactPoolAmount": "0xb88ced6c6baba2c8d4d00a2095efe33f80462e5e97f12ac5b00c4caa645859d5",
|
||||
"swapImpactPoolAmountLong": "0xc3c16377eab524ae40cb98b1c081a1c7ef5d047315ef6d61835b92e97c67aa27",
|
||||
"swapImpactPoolAmountShort": "0x0d7dc09d659d2ac0466d0098192e57a1c861a0e213fd164516029af3e11a9ee8",
|
||||
"longInterestUsingLongToken": "0x9fa688dfb0e144d7b35c644d214a32dac98ddd6df7b16a23b30cfb574694ef84",
|
||||
"longInterestUsingShortToken": "0xf61885cc87995d2294e91c634de592010e1367e64bec74eba8124fb995b42f68",
|
||||
"shortInterestUsingLongToken": "0x4c26636aad88b1f13cf67eea34dabffa123741bd428950f810a4748b0ff60ab7",
|
||||
"shortInterestUsingShortToken": "0xb09abf74d298edf201e156aa3cd71aed0dd5ac4a2ac5fe964ab50cbfe157bc90",
|
||||
"longInterestInTokensUsingLongToken": "0xffbe460b595251eb74d5e5d6f91ef9431b216faf56bd466cf118f4017c1210c0",
|
||||
"longInterestInTokensUsingShortToken": "0x87b8fbd9d598471aa3ea5053c9840d7f6496057bf367d550291b429725d88d91",
|
||||
"shortInterestInTokensUsingLongToken": "0x0304eeddd5d87227f90e9dd22b3ad39573da0ed1d07c0ae5d2b3a0ad9b14ca19",
|
||||
"shortInterestInTokensUsingShortToken": "0x0a28d88d02c6da0f1e9da244a7db2b1f9a8c231a341025ba72f8ed7d3979abe8"
|
||||
},
|
||||
"0xF913B4748031EF569898ED91e5BA0d602bB93298": {
|
||||
"longPoolAmount": "0x4e32b073b1d891267faa911b2026d27398e783fe47ac5897f0ac3e65a77b180e",
|
||||
"shortPoolAmount": "0x74beee2ee892cd19cdd4c49e4e87ed9ef91932777f7510cb08afa76955f5e56f",
|
||||
"positionImpactPoolAmount": "0xbad08b4bdf3ca9496bf7be36238681eb5ade243558ae504cd5f578c7942d0455",
|
||||
"swapImpactPoolAmountLong": "0x93d1a8df53a9fb5e5792da0d0941aa43d89b778f728559eef47c8b4ccad6ae22",
|
||||
"swapImpactPoolAmountShort": "0xb54532f7ba14c372e33175bc4b211d4f17afaef58151c88b619de877f1a2d38f",
|
||||
"longInterestUsingLongToken": "0x2619523773cac98ea0e5199dca5c8f9d17708a21f0d18e230932effbade1c8c7",
|
||||
"longInterestUsingShortToken": "0x80ab066711c75e89aafe87550f1925d75dcc1065348b18a36cb5fe501bacad69",
|
||||
"shortInterestUsingLongToken": "0xf4a4e80263f30771406ab23258fb5e0f3cc5dfbaa1b4b7a33dd9b81d94c2bd95",
|
||||
"shortInterestUsingShortToken": "0x6e905caaca22092a177156b3462bc3151b8c587ad4ea3582548cbc7195f89ad5",
|
||||
"longInterestInTokensUsingLongToken": "0x561f5a9761274c3ac1667aec675f97a263115b837cdd55c1c2c8d44af40d9cba",
|
||||
"longInterestInTokensUsingShortToken": "0x5fa74582bd92383fde22c777688c0686e7af27efd082e47955f2707f94787d9e",
|
||||
"shortInterestInTokensUsingLongToken": "0x0d97214c2dc1a5e2b6bcf67a3c80f2359a0f3dea82b1c140f12a4cc052dbf903",
|
||||
"shortInterestInTokensUsingShortToken": "0x328e1d94076bf2a92e9cd803136b955f233accb8d5754a2fe5344b4a6e4fb358"
|
||||
},
|
||||
"0x4De268aC68477f794C3eAC5A419Cbcffc2cD5e02": {
|
||||
"longPoolAmount": "0xe28547c0c17bd91a25c645d777adc9219889531845d68d5d2955f5a9e7adf388",
|
||||
"shortPoolAmount": "0xe9ecedc339bb8d14e1d68eb288c81c1b22fb3e6901b0421cf39379a59b731850",
|
||||
"positionImpactPoolAmount": "0x97bfc9130f5af068d0994759abc5cefc6a318a01cac69c23a3e5ad371fd2c4db",
|
||||
"swapImpactPoolAmountLong": "0xa72ffb68b7795fc2811a2bb09816ce651232d3b0b82ebf53ff16e10cb6df845e",
|
||||
"swapImpactPoolAmountShort": "0x50ee7507f04693208d277e1df709052311339f8b0dc88614fdfaed21cbca341b",
|
||||
"longInterestUsingLongToken": "0x407192382563274212987e487eae9da0913fb07a67cb172c90f6c0ba8fb0396f",
|
||||
"longInterestUsingShortToken": "0x46e53daf34ea673270d48e3378485694a42cc89330e4a249a06b7eda50ee2d41",
|
||||
"shortInterestUsingLongToken": "0x2da73ffa3d816951ae5ac1483405e0214bfc4d923637a80916a1228721251316",
|
||||
"shortInterestUsingShortToken": "0xd5939b0acf91f8de52396a2ce2b022ec942f519e0420c3fe4506f5dd77c872b8",
|
||||
"longInterestInTokensUsingLongToken": "0x11c80459a0f4d665e2697d461b89e4a3b3e5ed4190127f59035a5164529ef129",
|
||||
"longInterestInTokensUsingShortToken": "0xac7b7a5b15fd2f40becd35de5403b5825297c9fe3e9591293608426db2a5f857",
|
||||
"shortInterestInTokensUsingLongToken": "0xbe2e062d70098fedc078ce344d49096edb0c2d483efb8b40a073df2d3bec1675",
|
||||
"shortInterestInTokensUsingShortToken": "0x7148ff630c4aedff55c32c9d296df445e408dc02e19c62c578906a9f2589508b"
|
||||
},
|
||||
"0x947C521E44f727219542B0f91a85182193c1D2ad": {
|
||||
"longPoolAmount": "0x4f8ca797a73bd7a6e0e5764371def4943c729d59d0c0c2425df547412012b2ae",
|
||||
"shortPoolAmount": "0x50e613546ea75d92cca3aee5c5935d34a16bf100ab7db93f34b6005249d59e22",
|
||||
"positionImpactPoolAmount": "0xf415f967aa02cc464e3570e521e26084591233246a30ff64cdbe477bc5617619",
|
||||
"swapImpactPoolAmountLong": "0x64abd8a5fd4efce5e402f51bcf0820d4ea2ada8027eb690fb413ed348a000bbf",
|
||||
"swapImpactPoolAmountShort": "0x8dbe415bc61935945bd9b52baf0715c6fb445634124660fc8c01946c05b93762",
|
||||
"longInterestUsingLongToken": "0xfb3c77df1fcefa269177768c1b9fcf9fddaf0ba8a99f382b0536ca76fc67e2dc",
|
||||
"longInterestUsingShortToken": "0xe544cac798429b4da6359e02136f7330d29f4ec4b5797f0652e86bf047775a12",
|
||||
"shortInterestUsingLongToken": "0x595d76c42178ed7d18884f651b21e8d1d10280166f208dcfbba93146e0126f58",
|
||||
"shortInterestUsingShortToken": "0x0e1e76c1bec83587626de9f55c180513ad9c6276d83d6c7d7fcc8228d0165f21",
|
||||
"longInterestInTokensUsingLongToken": "0x1d40144557c68977be40c3609c4cd3011615c82baedc60d1faceeee0fe58d03c",
|
||||
"longInterestInTokensUsingShortToken": "0xd42c20c92ee8add244311eecce0bb9aee2e34509a642fd45cb091646c0e3136d",
|
||||
"shortInterestInTokensUsingLongToken": "0x5685c9c3cedc76f7f4eb9a550a783ba3d1a51e0b83624559df6f18ecd2395abe",
|
||||
"shortInterestInTokensUsingShortToken": "0xa029faf66a0c6d3862e25e5f180756e6552e04c2c95193785e0848191c045ded"
|
||||
},
|
||||
"0x2347EbB8645Cc2EA0Ba92D1EC59704031F2fCCf4": {
|
||||
"longPoolAmount": "0xe6b0f43e7615e46a90205e63d63e43687c7e578e404fca321711f427f7f0c8b1",
|
||||
"shortPoolAmount": "0x34aaea7b406618de19385f819b0ab9caa7debb9713c44c14e495efb4f73d6a73",
|
||||
"positionImpactPoolAmount": "0xfdd324af6053708576054f97c7a56b0be5d67603503335d7f9f182705b94e70e",
|
||||
"swapImpactPoolAmountLong": "0x5452823907a822ac7742924415616dfe23a07a931ceaa50fd28b9470067f0bfe",
|
||||
"swapImpactPoolAmountShort": "0x377e578ba634d7cc4b1e874e640c005c3f80ba45d59060e4ea1ab0115905c743",
|
||||
"longInterestUsingLongToken": "0xe2fc113ed7c4e1413282c85d324137ab9c9e9b067954c71c313acf36b05c498c",
|
||||
"longInterestUsingShortToken": "0x69d46c6d0f72f52d4c6e9a31dde0d1280d82b349b42dfa7c3708c43213524c93",
|
||||
"shortInterestUsingLongToken": "0xfefdadf4b69a4dc1789be717ca1db60bedf0435c3e1c4b27f9f670a44ef01d6f",
|
||||
"shortInterestUsingShortToken": "0x0eeca8196e7d5b7d6321eb72ee60d9716e0eedaefdd54c1a1a671bf3e734647c",
|
||||
"longInterestInTokensUsingLongToken": "0x0e36a6eda64ca5fac6eae3ef799ffcbfbd61c737850cb101f76da735137ba816",
|
||||
"longInterestInTokensUsingShortToken": "0x1ff50c1b1d847765805fc064151fd0dd0cc5a70e3ccae18692296216c566dba4",
|
||||
"shortInterestInTokensUsingLongToken": "0x84f2e6f5d63becd425968f24d9eafa3e629fa6c1e802a36531fdf7942131fcf1",
|
||||
"shortInterestInTokensUsingShortToken": "0xf2974ad3b0355d5e21d9f0bb876e1362ed876460902b71d2127c86c885f96a00"
|
||||
}
|
||||
},
|
||||
"43113": {
|
||||
@@ -1758,6 +2165,113 @@
|
||||
"longInterestInTokensUsingShortToken": "0xaf01e4f4a7b0884f217695d374acc70a5904db6d460bc5c5d31591557300f689",
|
||||
"shortInterestInTokensUsingLongToken": "0x562b9a09cca894aa54f6d4b17a72fe7e9c4ddc4686516dcfd15a597f3eec7ee3",
|
||||
"shortInterestInTokensUsingShortToken": "0xc563cc005593b275e1c8e42f993a0b8df38c5d1634f6867ece778bea7be36823"
|
||||
},
|
||||
"0x94cE6F65188a92F297C7f0A5A7B3cAd9013450F8": {
|
||||
"longPoolAmount": "0x0cfc5b6a91f7d200cae8e17385f5c1fd9b500ba6919cbf2e8cec407444011762",
|
||||
"shortPoolAmount": "0xb6914e1bc1f98f7ccb5bd8c45a6f66837aaa99b2dd99cce7445612f34d61159b",
|
||||
"positionImpactPoolAmount": "0xdee53ff0bba09c25e3b7b789306f2f0748efb7192065f1e8f7048e202dd961a6",
|
||||
"swapImpactPoolAmountLong": "0x80a076262b69e244f998a5e5223c9be6a98454c49f5035e917e980745aedb3c1",
|
||||
"swapImpactPoolAmountShort": "0x0ef33099e30c494475f3538713b9d743ef7d10110336e09a076dba073c1d50c2",
|
||||
"longInterestUsingLongToken": "0xb7bd39bc06702cd6a32bb1f032526ef3eccbfe167f07f613fe092b0407dfbc2f",
|
||||
"longInterestUsingShortToken": "0xa4f7b789a5352e3e7647a57749c1c429c988f01e0418f452cff7ffac89a1ab31",
|
||||
"shortInterestUsingLongToken": "0xc535618ec842c32b50e86466a8d22286ad738227af317ce4d50dcb098780b650",
|
||||
"shortInterestUsingShortToken": "0x4de2b3fa042fd5b54999ab452e6fb1080a078f2ccf6e19c933ad1414defb3730",
|
||||
"longInterestInTokensUsingLongToken": "0x3c9e0baac56e2818c4a3eb98924f340b2f65d5fec4afe3238d2744be64c62406",
|
||||
"longInterestInTokensUsingShortToken": "0xd0ce682e37dcac3e21bfb60546f612310ddc6d55cde3010a03f39c8254c58c9e",
|
||||
"shortInterestInTokensUsingLongToken": "0xe4a0a086a57e5ebf93c02996173518a25df8280b48bfaab22ddad851d5d5f279",
|
||||
"shortInterestInTokensUsingShortToken": "0x459afc07ae83faa216fe9bcbbb75a032a0557d866842476547540f230077ab76"
|
||||
},
|
||||
"0x1cb9932CE322877A2B86489BD1aA3C3CfF879F0d": {
|
||||
"longPoolAmount": "0xad56a0742868c3f4d39bdabc6eb90751687b8783cd4d1c87a656b972ac94f362",
|
||||
"shortPoolAmount": "0xc4860b276e11628fdf9568c1ecafb4c1f1696e77ff9f2107f2d0a25d8674c189",
|
||||
"positionImpactPoolAmount": "0x1419731b8a479a3688df057e0a4033c443e675e518b5591180bb313dc11ce678",
|
||||
"swapImpactPoolAmountLong": "0xdd20e2c64c6f0a1239498b6ccca9663021d35f8c343865f706597a54bd0cf7b1",
|
||||
"swapImpactPoolAmountShort": "0x7acdba86bcd54a661ee090115379fb5e819a82e6fd137e6ff6cd53379e31581f",
|
||||
"longInterestUsingLongToken": "0x55163a490779a711bfdd9608942dd00fcc82b5a5bca21f5e5651d1e0cd2073d1",
|
||||
"longInterestUsingShortToken": "0xe9e15ba9bf85cb3daf420725890f38a8022d9d4014eff9623827a8a3a1db27b9",
|
||||
"shortInterestUsingLongToken": "0x0d91379d8f3eb0c60e304b5ba7800655ecacc3ca8deddd3688e7d2a0f0931fd9",
|
||||
"shortInterestUsingShortToken": "0x95e8052b45df9ba1e967e650cf45f7109e29effb3b2ad0ad0ad99cfe247fe4f1",
|
||||
"longInterestInTokensUsingLongToken": "0x767e1771b41d53e64006f73ada7340e3cf1785f9b217b47b76dd71b53f5c6055",
|
||||
"longInterestInTokensUsingShortToken": "0xe66cbf40dbb64071b803a8739120264576e6de7d7eebf7c3f75a12a97040d222",
|
||||
"shortInterestInTokensUsingLongToken": "0x6e32e6700f16a1a53fe8c811c5b3e7ceee4b7c7552816bebe96ebca708c87e25",
|
||||
"shortInterestInTokensUsingShortToken": "0xd710836d5908bc3e16f18b7bd2f7f2930256fba5067f84b467f2aad0818aa49a"
|
||||
}
|
||||
},
|
||||
"421614": {
|
||||
"0x482Df3D320C964808579b585a8AC7Dd5D144eFaF": {
|
||||
"longPoolAmount": "0x1ec0396d749853b2fbba67bf4a9b63ee6c10acc4d2f436febe09f1d1f22b0c5e",
|
||||
"shortPoolAmount": "0x966ce6d85e013c191a28ae5d018091e0977856d1e46c38f400d171c0f167c61b",
|
||||
"positionImpactPoolAmount": "0x59846886db6aea850e805c57f34fb2e011dc0ef1e7adb646d2658abc07f0bf63",
|
||||
"swapImpactPoolAmountLong": "0x57ab3e5d4bf6949f6a528441273a53ab9cd656f343e5d061998cc53065caaabd",
|
||||
"swapImpactPoolAmountShort": "0x6a381d4f3adda0215422faf57cdfc1b16d416a29aa2ff331b4bffc91cd2d0eed",
|
||||
"longInterestUsingLongToken": "0xacda5af0a8de33f1e16ed19c34f899475e2fedff3c74d823744e5dd827e438f5",
|
||||
"longInterestUsingShortToken": "0x4cfb492a8112307cbada8ea447a0ce6a4c11ff2bfade59018ce0ce5c15db6a9b",
|
||||
"shortInterestUsingLongToken": "0x21bbed12abc8ea55003797eea6889821b5edaab8a7797e6bc32a3bf049fe1735",
|
||||
"shortInterestUsingShortToken": "0x817a1463ac1219c0e9b4a0d860e8930a6d46db91ce0d8fabb16500f5f84abb3e",
|
||||
"longInterestInTokensUsingLongToken": "0x100d64c96e9cac0c854f64f9e0df2ef84e0f9345953235c46550f90c0c6b5687",
|
||||
"longInterestInTokensUsingShortToken": "0x189399d250ad271f1a21e2c2f65a9f35cf1ab0f1c43ed0801362c29b65db6e10",
|
||||
"shortInterestInTokensUsingLongToken": "0x9c1e5c3cd21ef1109f76ec647ab04d40fb29f66b5d5f297fcfa8e83c8905ba15",
|
||||
"shortInterestInTokensUsingShortToken": "0xac1c8c21f4f015b06b1f3a276029a141d3c18d6023c2eea7e202f0551ef6b014"
|
||||
},
|
||||
"0xBb532Ab4923C23c2bfA455151B14fec177a34C0D": {
|
||||
"longPoolAmount": "0xf8f5984b5f6823422477171a767c309ef3e66bb03fd9250c2338c923b3f826cd",
|
||||
"shortPoolAmount": "0x17fe969fc101cde65c93cdea179927ea235c4dd02cf6fc4393c2f87c804bf155",
|
||||
"positionImpactPoolAmount": "0x9126d9e193e88aa45b54fbc6cd8065097741e70da912e43ddff7b31a3e1b604a",
|
||||
"swapImpactPoolAmountLong": "0x9d47d3c9bfe2bdc467ff67de884bfad5d4de00e1ca466c99be1a1092a7af2141",
|
||||
"swapImpactPoolAmountShort": "0x5c43473b1082f43b6f9aa5249b7eaab0c4ff65a516bc4fb261b32ff93bd8174f",
|
||||
"longInterestUsingLongToken": "0x169e6b04a9e8678b2b9f7946ece753dade93057f3e9843349f0fe5a7005da237",
|
||||
"longInterestUsingShortToken": "0x3e70f2ab88230fd47e9404a701ef9424e4f20b387eeb9de44c0bb86eaf94a2b2",
|
||||
"shortInterestUsingLongToken": "0x2916e4265d709d2ef21afe08b5ab9f1561d7f26987208b0a8cbfacdc5dadd8bc",
|
||||
"shortInterestUsingShortToken": "0x91a2fce531bf3417c5b52a2a5339e04ee0e1ded24d2d861577c21c988645e0cd",
|
||||
"longInterestInTokensUsingLongToken": "0x66ba10da6bc8084a3a5cc0852a07b2672ff1c5b5c95e63de444f085af32532a2",
|
||||
"longInterestInTokensUsingShortToken": "0xc1180ae0f4a1033944ec547d3b0ef99e10049c319db9d3a46258dfd6badb8ea4",
|
||||
"shortInterestInTokensUsingLongToken": "0xe17033f35824563158cc320f75d6c5555cc82b4e2042604c4cdb4830ef3c9881",
|
||||
"shortInterestInTokensUsingShortToken": "0x58e77d188c1a5575aade08d268601c46aec9fa6947a7f5c834bc2c00d02510b2"
|
||||
},
|
||||
"0xb6fC4C9eB02C35A134044526C62bb15014Ac0Bcc": {
|
||||
"longPoolAmount": "0x5181afcd124d09ecf3cc0183366419590e613f89f0baeb6f2a254b864d1ded10",
|
||||
"shortPoolAmount": "0x343aa8429e73bd4ea04046b08e044979ef44e502b642f8d1ac4205d3a16cdba1",
|
||||
"positionImpactPoolAmount": "0xa4a2162d0d1924d188b6da3e89cc4666698d0673a3ce0d117ac4060d971bd40a",
|
||||
"swapImpactPoolAmountLong": "0x07f51927e52071245c00905daeb9a3f626b50f5d7a776c594476ce5cf2fe8194",
|
||||
"swapImpactPoolAmountShort": "0xd3c393266534b2c089d9a134566dc3808e40936c9b429f6be41f3828397d9cd3",
|
||||
"longInterestUsingLongToken": "0xc85cc31027778d0ea13196668b57ceb45c1ea78917381d411d06fe8a62ab8073",
|
||||
"longInterestUsingShortToken": "0x8b5a79f28c17506e7caf37ae1e02516067eb1f42a1562a2bc9f3799fd7b159c8",
|
||||
"shortInterestUsingLongToken": "0xe2a3d5cb489f8168c7fb25429bd5bc3864cff0081f5bed90f87a0928a4d6a2ae",
|
||||
"shortInterestUsingShortToken": "0xc15572223fa93a63c1306166390d39186b2e13b66203bb8da04f48c05239083e",
|
||||
"longInterestInTokensUsingLongToken": "0x7c8cce0e9bfec8d13e6feff6341af5931b3732a9a55a7f25473a88ca4bbbfacf",
|
||||
"longInterestInTokensUsingShortToken": "0x0b1320cb3d7324a5c2e8e18bb798f8ccfa1acc8e56604c57c1bf4155c73f7079",
|
||||
"shortInterestInTokensUsingLongToken": "0x6be2375335308fffff2d8e9ff62b96acde6d1cffb4ddb1813ebff64cda059f7d",
|
||||
"shortInterestInTokensUsingShortToken": "0x6189501bde268b7f87ea49dc02723f702c95061a6e339774b74bef3341ade66e"
|
||||
},
|
||||
"0x3A83246bDDD60c4e71c91c10D9A66Fd64399bBCf": {
|
||||
"longPoolAmount": "0xa2075faf68c9726bb08bb027b98fcecd4f0f380983553b6aed4a7bf993d02ea2",
|
||||
"shortPoolAmount": "0x414c60762f70eea230bcd80009a752335b653c02037cd66deb7c36dbf3498b81",
|
||||
"positionImpactPoolAmount": "0xe084c2f70684f43aa96742d584cfb131021f8128e5828c29b7b3845423ccbdad",
|
||||
"swapImpactPoolAmountLong": "0x7fb1713aaf6cb5808aff7a90a6af51774c89f8b65f52236d5c9fa0b1d53c66ca",
|
||||
"swapImpactPoolAmountShort": "0x14b77dc07e765ab968065f1a550ad0a5aeec121d1fc90812ae88079db53883b5",
|
||||
"longInterestUsingLongToken": "0x5b3ff89f6dc478e77a3ebecd74a52b7f110bf3f31913ffb4b738e010405ff256",
|
||||
"longInterestUsingShortToken": "0xddad805de02d7bbc3f074197f1c0f8a28ac3452b129d3ccfb977044433ca1407",
|
||||
"shortInterestUsingLongToken": "0x79c7f949bed9c76895450148d13cbd2943e77e7eaa5319a13813eae044a06c17",
|
||||
"shortInterestUsingShortToken": "0x2755569cb50b991dde964096fa69cba032d8b7585f3a08b96682f1802b1b290d",
|
||||
"longInterestInTokensUsingLongToken": "0x8e57e5f724ac170095bdbd8ba1578908e9889266226882bcef5829bc6d2b6c6c",
|
||||
"longInterestInTokensUsingShortToken": "0x71bb78048905f09a82839779926db8aff078392c82b239b5b1551d323e7a50bf",
|
||||
"shortInterestInTokensUsingLongToken": "0x2dad3d52c3bfd9b175fd4422536a36576d5a7b719800cf33f82c2891345fc168",
|
||||
"shortInterestInTokensUsingShortToken": "0x2102a3bee36639520696b3d223cd869c7b21925f61e77331cf32335c4dae4362"
|
||||
},
|
||||
"0xAde9D177B9E060D2064ee9F798125e6539fDaA1c": {
|
||||
"longPoolAmount": "0x50d0f68f6a4fecbc901e4a8082d9317f682e4b2f5a87c11e368abf51c37ae5a6",
|
||||
"shortPoolAmount": "0xfacfa0c2d3c3056986b1c7ef2727d911129f7d4169bf668f9570e8998bd2ad08",
|
||||
"positionImpactPoolAmount": "0xa28e40173205226b450ee3f66e740ac1e123b81eed4198899746b45993480b0b",
|
||||
"swapImpactPoolAmountLong": "0xe80d5b6847cd87925fc341da9e3982e056ef5308ee6735242877c46b36630f33",
|
||||
"swapImpactPoolAmountShort": "0xe0f8ac8f75912883b35bd29b9ccdc044d9b58db259776e40024578264ba5e245",
|
||||
"longInterestUsingLongToken": "0xaead92698f3316bb52e70fd394f1944f87bf576e5ffb2b4a4a1d0c7fdcf8f8b6",
|
||||
"longInterestUsingShortToken": "0x99a8e43d4a6c016b51f0562ca4ff9e5b1fe08e7027c4dd4683ced9ae2f0a4470",
|
||||
"shortInterestUsingLongToken": "0xc81070e2b6a9277ea42887805edb202fdbd227668ea67024d66f69439358c671",
|
||||
"shortInterestUsingShortToken": "0xa0980940eb86bfbe3a5a9a1c03c397b15064c09dba8e4cff7d6add6671f9ce51",
|
||||
"longInterestInTokensUsingLongToken": "0x4087fbc85522f334dc81f3fb0c97490d2bf309131eb503ea550960057037dc3a",
|
||||
"longInterestInTokensUsingShortToken": "0xf61da26292dbce9b746fcc8438f02fab8e8a28a0d610791c191d64038ece27fc",
|
||||
"shortInterestInTokensUsingLongToken": "0x4e9ca5ca185f24d2f633d7bc31c3203b976717299efa1596032b0ffab3e8ff32",
|
||||
"shortInterestInTokensUsingShortToken": "0xfa0bb36ec3ca4732de1401392424dff4ce738063ac80315a014d3fffc4f7bd60"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,16 @@
|
||||
* Json files in this directory are prebuild by scripts from the `scripts/prebuild` directory.
|
||||
* No need to edit them manually, use `yarn run prebuild` command instead.
|
||||
*/
|
||||
import hashedMarketValuesKeysJson from "./hashedMarketValuesKeys.json" with { type: "json" };
|
||||
import hashedMarketConfigKeysJson from "./hashedMarketConfigKeys.json" with { type: "json" };
|
||||
import hashedKinkModelMarketRatesKeys from "./hashedKinkModelMarketRatesKeys.json" with { type: "json" };
|
||||
|
||||
|
||||
import {
|
||||
KinkModelMarketRateMulticallRequestConfig,
|
||||
MarketConfigMulticallRequestConfig,
|
||||
MarketValuesMulticallRequestConfig,
|
||||
KinkModelMarketRateMulticallRequestConfig,
|
||||
MarketConfigMulticallRequestConfig,
|
||||
MarketValuesMulticallRequestConfig
|
||||
} from "../modules/markets/types.js";
|
||||
import hashedKinkModelMarketRatesKeys from "./hashedKinkModelMarketRatesKeys.json" with {type: "json"};
|
||||
import hashedMarketConfigKeysJson from "./hashedMarketConfigKeys.json" with {type: "json"};
|
||||
import hashedMarketValuesKeysJson from "./hashedMarketValuesKeys.json" with {type: "json"};
|
||||
|
||||
type HashedMarketValuesKeys = Omit<
|
||||
Record<keyof MarketValuesMulticallRequestConfig[`${string}-dataStore`]["calls"], string>,
|
||||
@@ -22,7 +24,9 @@ const HASHED_MARKET_VALUES_KEYS: {
|
||||
};
|
||||
} = hashedMarketValuesKeysJson;
|
||||
|
||||
type HashedMarketConfigKeys = Record<keyof MarketConfigMulticallRequestConfig[`${string}-dataStore`]["calls"], string>;
|
||||
type HashedMarketConfigKeys = Partial<
|
||||
Record<keyof MarketConfigMulticallRequestConfig[`${string}-dataStore`]["calls"], string>
|
||||
>;
|
||||
|
||||
const HASHED_MARKET_CONFIG_KEYS: {
|
||||
[chainId: number]: {
|
||||
@@ -41,4 +45,4 @@ const HASHED_KINK_MODEL_MARKET_RATES_KEYS: {
|
||||
};
|
||||
} = hashedKinkModelMarketRatesKeys;
|
||||
|
||||
export { HASHED_MARKET_VALUES_KEYS, HASHED_MARKET_CONFIG_KEYS, HASHED_KINK_MODEL_MARKET_RATES_KEYS };
|
||||
export { HASHED_KINK_MODEL_MARKET_RATES_KEYS, HASHED_MARKET_CONFIG_KEYS, HASHED_MARKET_VALUES_KEYS };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ExternalSwapAggregator } from "./trade.js";
|
||||
import { Token } from "./tokens.js";
|
||||
import {ExternalSwapAggregator} from "./trade.js";
|
||||
import {Token} from "./tokens.js";
|
||||
|
||||
export type ExecutionFee = {
|
||||
feeUsd: bigint;
|
||||
@@ -39,7 +39,18 @@ export type GasLimitsConfig = {
|
||||
estimatedGasFeeBaseAmount: bigint;
|
||||
estimatedGasFeePerOraclePrice: bigint;
|
||||
estimatedFeeMultiplierFactor: bigint;
|
||||
gelatoRelayFeeMultiplierFactor: bigint;
|
||||
glvDepositGasLimit: bigint;
|
||||
glvWithdrawalGasLimit: bigint;
|
||||
glvPerMarketGasLimit: bigint;
|
||||
createOrderGasLimit: bigint;
|
||||
updateOrderGasLimit: bigint;
|
||||
cancelOrderGasLimit: bigint;
|
||||
tokenPermitGasLimit: bigint;
|
||||
gmxAccountCollateralGasLimit: bigint;
|
||||
};
|
||||
|
||||
export type L1ExpressOrderGasReference = {
|
||||
gasLimit: bigint;
|
||||
sizeOfData: bigint;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TokenData } from "./tokens";
|
||||
import {TokenData} from "./tokens";
|
||||
|
||||
export type PnlFactorType = "FOR_DEPOSITS" | "FOR_WITHDRAWALS" | "FOR_TRADERS";
|
||||
|
||||
@@ -120,6 +120,14 @@ export type MarketInfo = Market &
|
||||
virtualMarketId: string;
|
||||
virtualLongTokenId: string;
|
||||
virtualShortTokenId: string;
|
||||
|
||||
atomicSwapFeeFactor: bigint;
|
||||
swapFeeFactorForBalanceWasImproved: bigint;
|
||||
swapFeeFactorForBalanceWasNotImproved: bigint;
|
||||
positionFeeFactorForBalanceWasImproved: bigint;
|
||||
positionFeeFactorForBalanceWasNotImproved: bigint;
|
||||
|
||||
minCollateralFactorForLiquidation: bigint;
|
||||
};
|
||||
|
||||
export type MarketsData = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MarketInfo } from "./markets";
|
||||
import { TokenData, TokensRatio, TokensRatioAndSlippage } from "./tokens";
|
||||
import { SwapPathStats, TriggerThresholdType } from "./trade";
|
||||
import {MarketInfo} from "./markets";
|
||||
import {TokenData, TokensRatio, TokensRatioAndSlippage} from "./tokens";
|
||||
import {SwapPathStats, TriggerThresholdType} from "./trade";
|
||||
|
||||
export enum OrderType {
|
||||
// the order will be cancelled if the minOutputAmount cannot be fulfilled
|
||||
@@ -25,6 +25,10 @@ export enum OrderType {
|
||||
StopIncrease = 8,
|
||||
}
|
||||
|
||||
export type SwapOrderType = OrderType.MarketSwap | OrderType.LimitSwap;
|
||||
export type IncreaseOrderType = OrderType.MarketIncrease | OrderType.LimitIncrease | OrderType.StopIncrease;
|
||||
export type DecreaseOrderType = OrderType.MarketDecrease | OrderType.LimitDecrease | OrderType.StopLossDecrease;
|
||||
|
||||
export enum SwapPricingType {
|
||||
TwoStep = 0,
|
||||
Shift = 1,
|
||||
@@ -59,11 +63,15 @@ export type Order = {
|
||||
orderType: OrderType;
|
||||
shouldUnwrapNativeToken: boolean;
|
||||
autoCancel: boolean;
|
||||
data: string;
|
||||
data: string[];
|
||||
uiFeeReceiver: string;
|
||||
validFromTime: bigint;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export type SwapOrderInfo = Order & {
|
||||
isSwap: true;
|
||||
isTwap: false;
|
||||
swapPathStats?: SwapPathStats;
|
||||
triggerRatio?: TokensRatio | TokensRatioAndSlippage;
|
||||
initialCollateralToken: TokenData;
|
||||
@@ -71,6 +79,8 @@ export type SwapOrderInfo = Order & {
|
||||
};
|
||||
|
||||
export type PositionOrderInfo = Order & {
|
||||
isSwap: false;
|
||||
isTwap: false;
|
||||
marketInfo: MarketInfo;
|
||||
swapPathStats?: SwapPathStats;
|
||||
indexToken: TokenData;
|
||||
@@ -78,10 +88,17 @@ export type PositionOrderInfo = Order & {
|
||||
targetCollateralToken: TokenData;
|
||||
acceptablePrice: bigint;
|
||||
triggerPrice: bigint;
|
||||
triggerThresholdType: TriggerThresholdType;
|
||||
triggerThresholdType: TriggerThresholdType | undefined;
|
||||
};
|
||||
|
||||
export type OrderInfo = SwapOrderInfo | PositionOrderInfo;
|
||||
export type TwapOrderInfo<T extends PositionOrderInfo | SwapOrderInfo = PositionOrderInfo | SwapOrderInfo> = Omit<
|
||||
T,
|
||||
"isTwap"
|
||||
> & {
|
||||
isSwap: T extends SwapOrderInfo ? true : false;
|
||||
} & TwapOrderParams<T>;
|
||||
|
||||
export type OrderInfo = SwapOrderInfo | PositionOrderInfo | TwapOrderInfo;
|
||||
|
||||
export type OrdersData = {
|
||||
[orderKey: string]: Order;
|
||||
@@ -92,3 +109,20 @@ export type OrdersInfoData = {
|
||||
};
|
||||
|
||||
export type OrderTxnType = "create" | "update" | "cancel";
|
||||
|
||||
type SingleOrderParams = {
|
||||
key: string;
|
||||
isTwap: false;
|
||||
orderType: OrderType;
|
||||
};
|
||||
|
||||
type TwapOrderParams<T extends SingleOrderParams = SingleOrderParams> = {
|
||||
key: string;
|
||||
isTwap: true;
|
||||
orders: T[];
|
||||
orderType: OrderType;
|
||||
twapId: string;
|
||||
numberOfParts: number;
|
||||
};
|
||||
|
||||
export type OrderParams = SingleOrderParams | TwapOrderParams;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Market, MarketInfo } from "./markets";
|
||||
import { TokenData } from "./tokens";
|
||||
import {Market, MarketInfo} from "./markets";
|
||||
import {TokenData} from "./tokens";
|
||||
|
||||
export type Position = {
|
||||
key: string;
|
||||
@@ -22,6 +22,23 @@ export type Position = {
|
||||
positionFeeAmount: bigint;
|
||||
traderDiscountAmount: bigint;
|
||||
uiFeeAmount: bigint;
|
||||
pendingImpactAmount: bigint;
|
||||
/**
|
||||
* Not implemented in parsing
|
||||
*/
|
||||
borrowingFactor?: bigint;
|
||||
/**
|
||||
* Not implemented in parsing
|
||||
*/
|
||||
fundingFeeAmountPerSize?: bigint;
|
||||
/**
|
||||
* Not implemented in parsing
|
||||
*/
|
||||
longTokenClaimableFundingAmountPerSize?: bigint;
|
||||
/**
|
||||
* Not implemented in parsing
|
||||
*/
|
||||
shortTokenClaimableFundingAmountPerSize?: bigint;
|
||||
data: string;
|
||||
};
|
||||
|
||||
@@ -46,6 +63,10 @@ export type PositionInfo = Position & {
|
||||
pnlPercentage: bigint;
|
||||
pnlAfterFees: bigint;
|
||||
pnlAfterFeesPercentage: bigint;
|
||||
netPriceImapctDeltaUsd: bigint;
|
||||
priceImpactDiffUsd: bigint;
|
||||
pendingImpactUsd: bigint;
|
||||
closePriceImpactDeltaUsd: bigint;
|
||||
leverage: bigint | undefined;
|
||||
leverageWithPnl: bigint | undefined;
|
||||
netValue: bigint;
|
||||
@@ -55,6 +76,8 @@ export type PositionInfo = Position & {
|
||||
pendingClaimableFundingFeesUsd: bigint;
|
||||
};
|
||||
|
||||
export type PositionInfoLoaded = PositionInfo & { marketInfo: MarketInfo };
|
||||
|
||||
export type PositionsData = {
|
||||
[positionKey: string]: Position;
|
||||
};
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
import type { PublicClient, WalletClient } from "viem";
|
||||
import type { Token } from "./tokens";
|
||||
import type { MarketSdkConfig } from "./markets";
|
||||
import type {PublicClient, WalletClient} from "viem";
|
||||
import type {Token} from "./tokens";
|
||||
import type {MarketSdkConfig} from "./markets";
|
||||
import {ContractsChainId} from "../configs/chains";
|
||||
|
||||
export interface GmxSdkConfig {
|
||||
/** Chain ID */
|
||||
chainId: number;
|
||||
/** Account's address */
|
||||
account?: string;
|
||||
/** GMX Oracle URL */
|
||||
oracleUrl: string;
|
||||
/** Blockhain RPC URL */
|
||||
rpcUrl: string;
|
||||
/** GMX Subsquid URL */
|
||||
subsquidUrl: string;
|
||||
/** GMX Subgraph Synthetics Stats URL */
|
||||
subgraphUrl: string;
|
||||
/** Chain ID */
|
||||
chainId: ContractsChainId;
|
||||
/** Account's address */
|
||||
account?: string;
|
||||
/** GMX Oracle URL */
|
||||
oracleUrl: string;
|
||||
/** Blockhain RPC URL */
|
||||
rpcUrl: string;
|
||||
/** GMX Subsquid URL */
|
||||
subsquidUrl: string;
|
||||
|
||||
/** Custom viem's public and private client */
|
||||
publicClient?: PublicClient;
|
||||
walletClient?: WalletClient;
|
||||
/** Custom viem's public and private client */
|
||||
publicClient?: PublicClient;
|
||||
walletClient?: WalletClient;
|
||||
|
||||
/** Tokens override configurations */
|
||||
tokens?: Record<string, Partial<Token>>;
|
||||
/** Markets override configurations */
|
||||
markets?: Record<string, Partial<MarketSdkConfig>>;
|
||||
/** Tokens override configurations */
|
||||
tokens?: Record<string, Partial<Token>>;
|
||||
/** Markets override configurations */
|
||||
markets?: Record<string, Partial<MarketSdkConfig>>;
|
||||
|
||||
settings?: {
|
||||
uiFeeReceiverAccount?: string;
|
||||
};
|
||||
settings?: {
|
||||
uiFeeReceiverAccount?: string;
|
||||
};
|
||||
}
|
||||
|
||||
9106
src/Managing.Web3Proxy/src/generated/gmxsdk/types/subsquid.ts
Normal file
9106
src/Managing.Web3Proxy/src/generated/gmxsdk/types/subsquid.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
import {ExternalSwapQuote, SwapPathStats} from "./trade";
|
||||
|
||||
type BaseSwapStrategy = {
|
||||
amountIn: bigint;
|
||||
amountOut: bigint;
|
||||
usdIn: bigint;
|
||||
usdOut: bigint;
|
||||
priceIn: bigint;
|
||||
priceOut: bigint;
|
||||
feesUsd: bigint;
|
||||
};
|
||||
|
||||
export type NoSwapStrategy = BaseSwapStrategy & {
|
||||
type: "noSwap";
|
||||
externalSwapQuote: undefined;
|
||||
swapPathStats: undefined;
|
||||
};
|
||||
|
||||
export type ExternalSwapStrategy = BaseSwapStrategy & {
|
||||
type: "externalSwap";
|
||||
externalSwapQuote: ExternalSwapQuote;
|
||||
swapPathStats: undefined;
|
||||
};
|
||||
|
||||
export type InternalSwapStrategy = BaseSwapStrategy & {
|
||||
type: "internalSwap";
|
||||
swapPathStats: SwapPathStats;
|
||||
externalSwapQuote: undefined;
|
||||
};
|
||||
|
||||
export type CombinedSwapStrategy = BaseSwapStrategy & {
|
||||
type: "combinedSwap";
|
||||
externalSwapQuote: ExternalSwapQuote;
|
||||
swapPathStats: SwapPathStats;
|
||||
};
|
||||
|
||||
export type SwapStrategyForIncreaseOrders =
|
||||
| NoSwapStrategy
|
||||
| ExternalSwapStrategy
|
||||
| InternalSwapStrategy
|
||||
| CombinedSwapStrategy;
|
||||
|
||||
export type SwapStrategyForSwapOrders = NoSwapStrategy | InternalSwapStrategy | CombinedSwapStrategy;
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
export type ERC20Address = string & { __brand: "ERC20Address" };
|
||||
export type NativeTokenSupportedAddress = string & { __brand: "NativeTokenSupportedAddress" };
|
||||
|
||||
export type TokenAddressTypesMap = {
|
||||
wrapped: ERC20Address;
|
||||
native: NativeTokenSupportedAddress;
|
||||
};
|
||||
|
||||
export type ContractPrice = bigint & { __brand: "contractPrice" };
|
||||
|
||||
export type TokenCategory = "meme" | "layer1" | "layer2" | "defi";
|
||||
|
||||
export type Token = {
|
||||
@@ -19,6 +29,9 @@ export type Token = {
|
||||
reservesUrl?: string;
|
||||
imageUrl?: string;
|
||||
categories?: TokenCategory[];
|
||||
isPermitSupported?: boolean;
|
||||
isPermitDisabled?: boolean;
|
||||
contractVersion?: string;
|
||||
|
||||
isUsdg?: boolean;
|
||||
isNative?: boolean;
|
||||
@@ -31,6 +44,7 @@ export type Token = {
|
||||
isV1Available?: boolean;
|
||||
isPlatformToken?: boolean;
|
||||
isPlatformTradingToken?: boolean;
|
||||
isStaking?: boolean;
|
||||
shouldResetAllowance?: boolean;
|
||||
};
|
||||
|
||||
@@ -80,6 +94,28 @@ export type TokenInfo = Token & {
|
||||
maxLongCapacity?: bigint;
|
||||
};
|
||||
|
||||
export type SignedTokenPermit = {
|
||||
// account address
|
||||
owner: string;
|
||||
// spender contract address
|
||||
spender: string;
|
||||
// amount
|
||||
value: bigint;
|
||||
// validity period of the permit
|
||||
deadline: bigint;
|
||||
// ECDSA signature components
|
||||
v: number;
|
||||
r: string;
|
||||
s: string;
|
||||
// token address
|
||||
token: string;
|
||||
onchainParams: {
|
||||
name: string;
|
||||
version: string;
|
||||
nonce: bigint;
|
||||
};
|
||||
};
|
||||
|
||||
export type InfoTokens = {
|
||||
[key: string]: TokenInfo;
|
||||
};
|
||||
@@ -91,8 +127,16 @@ export type TokenPrices = {
|
||||
|
||||
export type TokenData = Token & {
|
||||
prices: TokenPrices;
|
||||
isGmxAccount?: boolean;
|
||||
walletBalance?: bigint;
|
||||
gmxAccountBalance?: bigint;
|
||||
/**
|
||||
* If isGmxAccount is true, then this is the gmx account balance
|
||||
* If isGmxAccount is false, then this is the wallet balance
|
||||
*/
|
||||
balance?: bigint;
|
||||
totalSupply?: bigint;
|
||||
hasPriceFeedProvider?: boolean;
|
||||
};
|
||||
|
||||
export type TokensRatio = {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ExternalSwapFeeItem, FeeItem, SwapFeeItem } from "./fees.js";
|
||||
import { DecreasePositionSwapType, OrderType } from "./orders.js";
|
||||
import { MarketInfo } from "./markets.js";
|
||||
import {ExternalSwapFeeItem, FeeItem, SwapFeeItem} from "./fees.js";
|
||||
import {DecreasePositionSwapType, OrderType} from "./orders.js";
|
||||
import {TokensData} from "./tokens.js";
|
||||
import {SwapStrategyForIncreaseOrders} from "./swapStrategy.js";
|
||||
|
||||
export enum TradeType {
|
||||
Long = "Long",
|
||||
Short = "Short",
|
||||
@@ -12,6 +14,7 @@ export enum TradeMode {
|
||||
Limit = "Limit",
|
||||
StopMarket = "StopMarket",
|
||||
Trigger = "Trigger",
|
||||
Twap = "TWAP",
|
||||
}
|
||||
|
||||
export enum TriggerThresholdType {
|
||||
@@ -33,6 +36,7 @@ export type TradeFlags = {
|
||||
isTrigger: boolean;
|
||||
isMarket: boolean;
|
||||
isLimit: boolean;
|
||||
isTwap: boolean;
|
||||
};
|
||||
|
||||
export type SwapAmounts = {
|
||||
@@ -42,7 +46,7 @@ export type SwapAmounts = {
|
||||
usdOut: bigint;
|
||||
priceIn: bigint;
|
||||
priceOut: bigint;
|
||||
swapPathStats: SwapPathStats | undefined;
|
||||
swapStrategy: SwapStrategyForIncreaseOrders;
|
||||
minOutputAmount: bigint;
|
||||
uiFeeUsd?: bigint;
|
||||
};
|
||||
@@ -54,8 +58,7 @@ export type IncreasePositionAmounts = {
|
||||
collateralDeltaAmount: bigint;
|
||||
collateralDeltaUsd: bigint;
|
||||
|
||||
swapPathStats: SwapPathStats | undefined;
|
||||
externalSwapQuote: ExternalSwapQuote | undefined;
|
||||
swapStrategy: SwapStrategyForIncreaseOrders;
|
||||
indexTokenAmount: bigint;
|
||||
|
||||
sizeDeltaUsd: bigint;
|
||||
@@ -71,6 +74,7 @@ export type IncreasePositionAmounts = {
|
||||
triggerThresholdType?: TriggerThresholdType;
|
||||
acceptablePrice: bigint;
|
||||
acceptablePriceDeltaBps: bigint;
|
||||
recommendedAcceptablePriceDeltaBps: bigint;
|
||||
|
||||
positionFeeUsd: bigint;
|
||||
uiFeeUsd: bigint;
|
||||
@@ -107,8 +111,11 @@ export type DecreasePositionAmounts = {
|
||||
borrowingFeeUsd: bigint;
|
||||
fundingFeeUsd: bigint;
|
||||
swapProfitFeeUsd: bigint;
|
||||
positionPriceImpactDeltaUsd: bigint;
|
||||
proportionalPendingImpactDeltaUsd: bigint;
|
||||
closePriceImpactDeltaUsd: bigint;
|
||||
totalPendingImpactDeltaUsd: bigint;
|
||||
priceImpactDiffUsd: bigint;
|
||||
balanceWasImproved: boolean;
|
||||
payedRemainingCollateralAmount: bigint;
|
||||
|
||||
payedOutputUsd: bigint;
|
||||
@@ -159,6 +166,8 @@ export type NextPositionValues = {
|
||||
nextPnlPercentage?: bigint;
|
||||
nextEntryPrice?: bigint;
|
||||
remainingCollateralFeesUsd?: bigint;
|
||||
nextPendingImpactDeltaUsd?: bigint;
|
||||
potentialPriceImpactDiffUsd?: bigint;
|
||||
};
|
||||
|
||||
export type SwapStats = {
|
||||
@@ -194,10 +203,13 @@ export type SwapPathStats = {
|
||||
|
||||
export type MarketEdge = {
|
||||
marketAddress: string;
|
||||
marketInfo: MarketInfo;
|
||||
// from token
|
||||
/**
|
||||
* Token Address
|
||||
*/
|
||||
from: string;
|
||||
// to token
|
||||
/**
|
||||
* Token Address
|
||||
*/
|
||||
to: string;
|
||||
};
|
||||
|
||||
@@ -207,9 +219,11 @@ export type SwapRoute = {
|
||||
liquidity: bigint;
|
||||
};
|
||||
|
||||
export type MarketsGraph = {
|
||||
abjacencyList: { [token: string]: MarketEdge[] };
|
||||
edges: MarketEdge[];
|
||||
type TokenAddress = string;
|
||||
export type SwapPaths = {
|
||||
[from: TokenAddress]: {
|
||||
[to: TokenAddress]: TokenAddress[][];
|
||||
};
|
||||
};
|
||||
|
||||
export type SwapEstimator = (
|
||||
@@ -219,38 +233,42 @@ export type SwapEstimator = (
|
||||
usdOut: bigint;
|
||||
};
|
||||
|
||||
export type FindSwapPath = (usdIn: bigint, opts: { order?: ("liquidity" | "length")[] }) => SwapPathStats | undefined;
|
||||
export type NaiveSwapEstimator = (
|
||||
e: MarketEdge,
|
||||
usdIn: bigint
|
||||
) => {
|
||||
/**
|
||||
* 1.1 means output is 10% greater than input
|
||||
* 0.9 means output is 10% less than input
|
||||
*/
|
||||
swapYield: number;
|
||||
};
|
||||
|
||||
export type NaiveNetworkEstimator = (
|
||||
usdIn: bigint,
|
||||
swapCount: number
|
||||
) => {
|
||||
networkYield: number;
|
||||
usdOut: bigint;
|
||||
};
|
||||
|
||||
export type MarketEdgeLiquidityGetter = (e: MarketEdge) => bigint;
|
||||
|
||||
export type SwapOptimizationOrderArray = ("liquidity" | "length")[];
|
||||
export type FindSwapPath = (usdIn: bigint, opts?: { order?: SwapOptimizationOrderArray }) => SwapPathStats | undefined;
|
||||
|
||||
export type TradeFeesType = "swap" | "increase" | "decrease" | "edit";
|
||||
|
||||
export enum ExternalSwapAggregator {
|
||||
OpenOcean = "openOcean",
|
||||
BotanixStaking = "botanixStaking",
|
||||
}
|
||||
|
||||
export type ExternalSwapOutput = {
|
||||
aggregator: ExternalSwapAggregator;
|
||||
inTokenAddress: string;
|
||||
outTokenAddress: string;
|
||||
amountIn: bigint;
|
||||
amountOut: bigint;
|
||||
usdIn: bigint | undefined;
|
||||
usdOut: bigint | undefined;
|
||||
priceIn: bigint | undefined;
|
||||
priceOut: bigint | undefined;
|
||||
feesUsd: bigint | undefined;
|
||||
needSpenderApproval?: boolean;
|
||||
txnData: {
|
||||
to: string;
|
||||
data: string;
|
||||
value: bigint;
|
||||
estimatedGas: bigint;
|
||||
};
|
||||
};
|
||||
|
||||
export type ExternalSwapQuote = {
|
||||
aggregator: ExternalSwapAggregator;
|
||||
inTokenAddress: string;
|
||||
outTokenAddress: string;
|
||||
receiver: string;
|
||||
amountIn: bigint;
|
||||
amountOut: bigint;
|
||||
usdIn: bigint;
|
||||
@@ -264,9 +282,24 @@ export type ExternalSwapQuote = {
|
||||
data: string;
|
||||
value: bigint;
|
||||
estimatedGas: bigint;
|
||||
estimatedExecutionFee: bigint;
|
||||
};
|
||||
};
|
||||
|
||||
export type ExternalSwapPath = {
|
||||
aggregator: ExternalSwapAggregator;
|
||||
inTokenAddress: string;
|
||||
outTokenAddress: string;
|
||||
};
|
||||
|
||||
export type ExternalSwapQuoteParams = {
|
||||
chainId: number;
|
||||
receiverAddress: string;
|
||||
gasPrice: bigint | undefined;
|
||||
tokensData: TokensData | undefined;
|
||||
botanixStakingAssetsPerShare: bigint | undefined;
|
||||
};
|
||||
|
||||
export type ExternalSwapCalculationStrategy = "byFromValue" | "leverageBySize";
|
||||
|
||||
export type ExternalSwapInputs = {
|
||||
@@ -287,9 +320,14 @@ export type TradeFees = {
|
||||
swapFees?: SwapFeeItem[];
|
||||
positionFee?: FeeItem;
|
||||
swapPriceImpact?: FeeItem;
|
||||
positionPriceImpact?: FeeItem;
|
||||
priceImpactDiff?: FeeItem;
|
||||
positionCollateralPriceImpact?: FeeItem;
|
||||
proportionalPendingImpact?: FeeItem;
|
||||
increasePositionPriceImpact?: FeeItem;
|
||||
decreasePositionPriceImpact?: FeeItem;
|
||||
totalPendingImpact?: FeeItem;
|
||||
priceImpactDiff?: FeeItem;
|
||||
positionNetPriceImpact?: FeeItem;
|
||||
collateralNetPriceImpact?: FeeItem;
|
||||
collateralPriceImpactDiff?: FeeItem;
|
||||
positionFeeFactor?: bigint;
|
||||
borrowFee?: FeeItem;
|
||||
@@ -316,4 +354,5 @@ export type TradeSearchParams = {
|
||||
pool?: string;
|
||||
collateral?: string;
|
||||
market?: string;
|
||||
chainId?: string;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { BytesLike } from "./common";
|
||||
import { MarketInfo } from "./markets";
|
||||
import { OrderType } from "./orders";
|
||||
import { TokenData } from "./tokens";
|
||||
import {MarketInfo} from "./markets";
|
||||
import {OrderType} from "./orders";
|
||||
import {TokenData} from "./tokens";
|
||||
|
||||
export enum TradeActionType {
|
||||
OrderCreated = "OrderCreated",
|
||||
@@ -11,54 +10,9 @@ export enum TradeActionType {
|
||||
OrderFrozen = "OrderFrozen",
|
||||
}
|
||||
|
||||
export type RawTradeAction = {
|
||||
id: string;
|
||||
eventName: TradeActionType;
|
||||
|
||||
account: string;
|
||||
marketAddress?: string;
|
||||
swapPath?: string[];
|
||||
initialCollateralTokenAddress?: string;
|
||||
|
||||
initialCollateralDeltaAmount?: string;
|
||||
sizeDeltaUsd?: string;
|
||||
triggerPrice?: string;
|
||||
acceptablePrice?: string;
|
||||
executionPrice?: string;
|
||||
minOutputAmount?: string;
|
||||
executionAmountOut?: string;
|
||||
|
||||
priceImpactDiffUsd?: string;
|
||||
priceImpactUsd?: string;
|
||||
positionFeeAmount?: string;
|
||||
borrowingFeeAmount?: string;
|
||||
fundingFeeAmount?: string;
|
||||
liquidationFeeAmount?: string;
|
||||
pnlUsd?: string;
|
||||
basePnlUsd?: string;
|
||||
|
||||
collateralTokenPriceMax?: string;
|
||||
collateralTokenPriceMin?: string;
|
||||
|
||||
indexTokenPriceMin?: string;
|
||||
indexTokenPriceMax?: string;
|
||||
|
||||
orderType: OrderType;
|
||||
orderKey: string;
|
||||
isLong?: boolean;
|
||||
shouldUnwrapNativeToken?: boolean;
|
||||
|
||||
reason?: string;
|
||||
reasonBytes?: BytesLike;
|
||||
|
||||
transaction: {
|
||||
timestamp: number;
|
||||
hash: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type PositionTradeAction = {
|
||||
id: string;
|
||||
srcChainId?: number;
|
||||
eventName: TradeActionType;
|
||||
marketInfo: MarketInfo;
|
||||
marketAddress: string;
|
||||
@@ -91,16 +45,23 @@ export type PositionTradeAction = {
|
||||
reason?: string;
|
||||
reasonBytes?: string | Uint8Array;
|
||||
shouldUnwrapNativeToken: boolean;
|
||||
totalImpactUsd?: bigint;
|
||||
liquidationFeeAmount?: bigint;
|
||||
|
||||
twapParams:
|
||||
| {
|
||||
twapGroupId: string;
|
||||
numberOfParts: number;
|
||||
}
|
||||
| undefined;
|
||||
timestamp: number;
|
||||
transaction: {
|
||||
timestamp: number;
|
||||
hash: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type SwapTradeAction = {
|
||||
id: string;
|
||||
srcChainId?: number;
|
||||
account: string;
|
||||
eventName: TradeActionType;
|
||||
initialCollateralTokenAddress: string;
|
||||
@@ -113,12 +74,17 @@ export type SwapTradeAction = {
|
||||
executionAmountOut?: bigint;
|
||||
orderType: OrderType;
|
||||
orderKey: string;
|
||||
|
||||
reason?: string;
|
||||
reasonBytes?: string | Uint8Array;
|
||||
twapParams:
|
||||
| {
|
||||
twapGroupId: string;
|
||||
numberOfParts: number;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
timestamp: number;
|
||||
transaction: {
|
||||
timestamp: number;
|
||||
hash: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -113,6 +113,8 @@ describe("getPositionNetValue", () => {
|
||||
closingFeeUsd: 5n,
|
||||
uiFeeUsd: 20n,
|
||||
pnl: 200n,
|
||||
totalPendingImpactDeltaUsd: 10n,
|
||||
priceImpactDiffUsd: 15n,
|
||||
});
|
||||
// netValue = 1000n - (10n+15n) -5n -20n + 200n = 1000n -25n -5n -20n +200n=1150n
|
||||
expect(result).toBe(1150n);
|
||||
@@ -168,6 +170,7 @@ describe("getLiquidationPrice", () => {
|
||||
minCollateralUsd: 100n,
|
||||
isLong: true,
|
||||
userReferralInfo: undefined,
|
||||
pendingImpactAmount: 0n,
|
||||
})
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
@@ -183,6 +186,7 @@ describe("getLiquidationPrice", () => {
|
||||
minCollateralUsd: 100n,
|
||||
isLong: true,
|
||||
userReferralInfo: undefined,
|
||||
pendingImpactAmount: 0n,
|
||||
})
|
||||
).toBeUndefined();
|
||||
});
|
||||
@@ -207,6 +211,7 @@ describe("getLiquidationPrice", () => {
|
||||
minCollateralUsd: 200n,
|
||||
isLong: true,
|
||||
userReferralInfo: undefined,
|
||||
pendingImpactAmount: 0n,
|
||||
});
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -2,8 +2,14 @@ export const bigMath = {
|
||||
abs(x: bigint) {
|
||||
return x < 0n ? -x : x;
|
||||
},
|
||||
mulDiv(x: bigint, y: bigint, z: bigint) {
|
||||
return (x * y) / z;
|
||||
mulDiv(x: bigint, y: bigint, z: bigint, roundUpMagnitude = false) {
|
||||
const result = (x * y) / z;
|
||||
|
||||
if (roundUpMagnitude && this.mulmod(x, y, z) > 0n) {
|
||||
return result + 1n;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
max(max: bigint, ...rest: bigint[]) {
|
||||
return rest.reduce((currentMax, val) => (currentMax < val ? val : currentMax), max);
|
||||
@@ -30,4 +36,10 @@ export const bigMath = {
|
||||
divRound(x: bigint, y: bigint) {
|
||||
return x / y + ((x % y) * 2n > y ? 1n : 0n);
|
||||
},
|
||||
divRoundUp(x: bigint, y: bigint) {
|
||||
return (x + y - 1n) / y;
|
||||
},
|
||||
mulmod(x: bigint, y: bigint, m: bigint): bigint {
|
||||
return (x * y) % m;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Abi, Address, encodeFunctionData, PublicClient, withRetry } from "viem";
|
||||
import { getPrivyClient, getChainName } from '../../../plugins/custom/privy.js';
|
||||
import {Abi, Address, encodeFunctionData, PublicClient, withRetry} from "viem";
|
||||
import {getChainName, getPrivyClient} from '../../../plugins/custom/privy.js';
|
||||
|
||||
import type { GmxSdk } from "../index.js";
|
||||
import { bigMath } from "./bigmath.js";
|
||||
import { GAS_PRICE_BUFFER_MAP, GAS_PRICE_PREMIUM_MAP, MAX_FEE_PER_GAS_MAP, MAX_PRIORITY_FEE_PER_GAS_MAP } from "../configs/chains.js";
|
||||
|
||||
import { getChain } from "../configs/chains.js";
|
||||
import { BASIS_POINTS_DIVISOR_BIGINT } from "../configs/factors.js";
|
||||
import type {GmxSdk} from "../index.js";
|
||||
import {bigMath} from "./bigmath.js";
|
||||
import {
|
||||
GAS_PRICE_BUFFER_MAP,
|
||||
GAS_PRICE_PREMIUM_MAP,
|
||||
getViemChain,
|
||||
MAX_FEE_PER_GAS_MAP,
|
||||
MAX_PRIORITY_FEE_PER_GAS_MAP
|
||||
} from "../configs/chains.js";
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT} from "../configs/factors.js";
|
||||
|
||||
export async function getGasPrice(client: PublicClient, chainId: number) {
|
||||
let maxFeePerGas = MAX_FEE_PER_GAS_MAP[chainId];
|
||||
@@ -16,11 +20,11 @@ export async function getGasPrice(client: PublicClient, chainId: number) {
|
||||
() =>
|
||||
client.estimateFeesPerGas({
|
||||
type: "legacy",
|
||||
chain: getChain(chainId),
|
||||
chain: getViemChain(chainId),
|
||||
}),
|
||||
{
|
||||
delay: 200,
|
||||
retryCount: 2,
|
||||
retryCount: 1,
|
||||
shouldRetry: ({ error }) => {
|
||||
const isInvalidBlockError = error?.message?.includes("invalid value for value.hash");
|
||||
|
||||
@@ -189,7 +193,9 @@ export async function callContract(
|
||||
}
|
||||
};
|
||||
|
||||
console.log('param', param)
|
||||
console.log('Address', sdk.config.account)
|
||||
console.log('Method', method)
|
||||
console.log('Params', params)
|
||||
|
||||
const response = await privy.walletApi.ethereum.sendTransaction(param as any);
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import type {GasLimitsConfig} from "../../types/fees.js";
|
||||
|
||||
export function getNaiveEstimatedGasBySwapCount(singleSwap: GasLimitsConfig["singleSwap"], swapsCount: number): bigint {
|
||||
const swapsCountBigint = BigInt(swapsCount);
|
||||
|
||||
return singleSwap * swapsCountBigint;
|
||||
}
|
||||
@@ -1,18 +1,30 @@
|
||||
import { HIGH_PRICE_IMPACT_BPS } from "../../configs/factors.js";
|
||||
import { FeeItem } from "../../types/fees.js";
|
||||
import { MarketInfo } from "../../types/markets.js";
|
||||
import { SwapStats } from "../../types/trade.js";
|
||||
import { bigMath } from "../bigmath.js";
|
||||
import { applyFactor, getBasisPoints, PRECISION } from "../numbers.js";
|
||||
import {HIGH_PRICE_IMPACT_BPS} from "../../configs/factors.js";
|
||||
import {FeeItem} from "../../types/fees.js";
|
||||
import {MarketInfo} from "../../types/markets.js";
|
||||
import {SwapStats} from "../../types/trade.js";
|
||||
import {bigMath} from "../bigmath.js";
|
||||
import {applyFactor, getBasisPoints, PRECISION} from "../numbers.js";
|
||||
|
||||
export * from "./estimateOraclePriceCount.js";
|
||||
export * from "./executionFee.js";
|
||||
export * from "./priceImpact.js";
|
||||
|
||||
export function getSwapFee(marketInfo: MarketInfo, swapAmount: bigint, forPositiveImpact: boolean) {
|
||||
const factor = forPositiveImpact
|
||||
? marketInfo.swapFeeFactorForPositiveImpact
|
||||
: marketInfo.swapFeeFactorForNegativeImpact;
|
||||
|
||||
export function getSwapFee(
|
||||
marketInfo: MarketInfo,
|
||||
swapAmount: bigint,
|
||||
balanceWasImproved: boolean,
|
||||
isAtomicSwap: boolean
|
||||
) {
|
||||
let factor: bigint;
|
||||
|
||||
if (isAtomicSwap) {
|
||||
factor = BigInt(marketInfo.atomicSwapFeeFactor ?? 0);
|
||||
} else {
|
||||
factor = BigInt(balanceWasImproved
|
||||
? (marketInfo.swapFeeFactorForBalanceWasImproved ?? 0)
|
||||
: (marketInfo.swapFeeFactorForBalanceWasNotImproved ?? 0));
|
||||
}
|
||||
|
||||
return applyFactor(swapAmount, factor);
|
||||
}
|
||||
@@ -20,13 +32,13 @@ export function getSwapFee(marketInfo: MarketInfo, swapAmount: bigint, forPositi
|
||||
export function getPositionFee(
|
||||
marketInfo: MarketInfo,
|
||||
sizeDeltaUsd: bigint,
|
||||
forPositiveImpact: boolean,
|
||||
balanceWasImproved: boolean,
|
||||
referralInfo: { totalRebateFactor: bigint; discountFactor: bigint } | undefined,
|
||||
uiFeeFactor?: bigint
|
||||
) {
|
||||
const factor = forPositiveImpact
|
||||
? marketInfo.positionFeeFactorForPositiveImpact
|
||||
: marketInfo.positionFeeFactorForNegativeImpact;
|
||||
const factor = BigInt(balanceWasImproved
|
||||
? (marketInfo.positionFeeFactorForBalanceWasImproved ?? 0)
|
||||
: (marketInfo.positionFeeFactorForBalanceWasNotImproved ?? 0));
|
||||
|
||||
let positionFeeUsd = applyFactor(sizeDeltaUsd, factor);
|
||||
const uiFeeUsd = applyFactor(sizeDeltaUsd, uiFeeFactor ?? 0n);
|
||||
@@ -58,7 +70,7 @@ export function getFundingFactorPerPeriod(marketInfo: MarketInfo, isLong: boolea
|
||||
|
||||
let fundingForPayingSide = 0n;
|
||||
if (payingInterestUsd !== 0n) {
|
||||
fundingForPayingSide = bigMath.mulDiv(fundingFactorPerSecond, largerInterestUsd, payingInterestUsd);
|
||||
fundingForPayingSide = bigMath.mulDiv(BigInt(fundingFactorPerSecond ?? 0), largerInterestUsd, payingInterestUsd);
|
||||
}
|
||||
let fundingForReceivingSide = 0n;
|
||||
if (receivingInterestUsd !== 0n) {
|
||||
@@ -86,9 +98,9 @@ export function getFundingFeeRateUsd(
|
||||
}
|
||||
|
||||
export function getBorrowingFactorPerPeriod(marketInfo: MarketInfo, isLong: boolean, periodInSeconds: number) {
|
||||
const factorPerSecond = isLong
|
||||
? marketInfo.borrowingFactorPerSecondForLongs
|
||||
: marketInfo.borrowingFactorPerSecondForShorts;
|
||||
const factorPerSecond = BigInt(isLong
|
||||
? (marketInfo.borrowingFactorPerSecondForLongs ?? 0)
|
||||
: (marketInfo.borrowingFactorPerSecondForShorts ?? 0));
|
||||
|
||||
return factorPerSecond * BigInt(periodInSeconds || 1);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { bigNumberify } from "../../modules/trades/trades.js";
|
||||
import { MarketInfo } from "../../types/markets.js";
|
||||
import { TokenData } from "../../types/tokens.js";
|
||||
import { bigMath } from "../bigmath.js";
|
||||
import { getTokenPoolType } from "../markets.js";
|
||||
import { applyFactor, expandDecimals, getBasisPoints, roundUpMagnitudeDivision } from "../numbers.js";
|
||||
import { convertToTokenAmount, convertToUsd, getMidPrice } from "../tokens.js";
|
||||
import {bigNumberify} from "../tradeHistory.js";
|
||||
import {MarketInfo} from "../../types/markets.js";
|
||||
import {TokenData} from "../../types/tokens.js";
|
||||
import {bigMath} from "../bigmath.js";
|
||||
import {getTokenPoolType} from "../markets.js";
|
||||
import {applyFactor, expandDecimals, getBasisPoints, roundUpMagnitudeDivision} from "../numbers.js";
|
||||
import {convertToTokenAmount, convertToUsd, getMidPrice} from "../tokens.js";
|
||||
|
||||
export function getPriceImpactByAcceptablePrice(p: {
|
||||
sizeDeltaUsd: bigint;
|
||||
@@ -69,38 +69,72 @@ export function getCappedPositionImpactUsd(
|
||||
marketInfo: MarketInfo,
|
||||
sizeDeltaUsd: bigint,
|
||||
isLong: boolean,
|
||||
opts: { fallbackToZero?: boolean } = {}
|
||||
isIncrease: boolean,
|
||||
opts: { fallbackToZero?: boolean; shouldCapNegativeImpact?: boolean } = {}
|
||||
) {
|
||||
const priceImpactDeltaUsd = getPriceImpactForPosition(marketInfo, sizeDeltaUsd, isLong, opts);
|
||||
sizeDeltaUsd = isIncrease ? sizeDeltaUsd : sizeDeltaUsd * -1n;
|
||||
|
||||
if (priceImpactDeltaUsd < 0) {
|
||||
return priceImpactDeltaUsd;
|
||||
const { priceImpactDeltaUsd, balanceWasImproved } = getPriceImpactForPosition(marketInfo, sizeDeltaUsd, isLong, opts);
|
||||
|
||||
if (priceImpactDeltaUsd < 0 && !opts.shouldCapNegativeImpact) {
|
||||
return { priceImpactDeltaUsd, balanceWasImproved };
|
||||
}
|
||||
|
||||
const cappedImpactUsd = capPositionImpactUsdByMaxPriceImpactFactor(marketInfo, sizeDeltaUsd, priceImpactDeltaUsd);
|
||||
|
||||
return {
|
||||
priceImpactDeltaUsd: cappedImpactUsd,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
export function capPositionImpactUsdByMaxImpactPool(marketInfo: MarketInfo, positionImpactDeltaUsd: bigint) {
|
||||
if (positionImpactDeltaUsd < 0) {
|
||||
return positionImpactDeltaUsd;
|
||||
}
|
||||
|
||||
const { indexToken } = marketInfo;
|
||||
|
||||
const impactPoolAmount = marketInfo?.positionImpactPoolAmount;
|
||||
|
||||
const impactPoolAmount = marketInfo.positionImpactPoolAmount;
|
||||
const maxPriceImpactUsdBasedOnImpactPool = convertToUsd(
|
||||
impactPoolAmount,
|
||||
indexToken.decimals,
|
||||
indexToken.prices.minPrice
|
||||
)!;
|
||||
|
||||
let cappedImpactUsd = priceImpactDeltaUsd;
|
||||
|
||||
if (cappedImpactUsd > maxPriceImpactUsdBasedOnImpactPool) {
|
||||
cappedImpactUsd = maxPriceImpactUsdBasedOnImpactPool;
|
||||
if (positionImpactDeltaUsd > maxPriceImpactUsdBasedOnImpactPool) {
|
||||
positionImpactDeltaUsd = maxPriceImpactUsdBasedOnImpactPool;
|
||||
}
|
||||
|
||||
const maxPriceImpactFactor = marketInfo.maxPositionImpactFactorPositive;
|
||||
const maxPriceImpactUsdBasedOnMaxPriceImpactFactor = applyFactor(bigMath.abs(sizeDeltaUsd), maxPriceImpactFactor);
|
||||
return positionImpactDeltaUsd;
|
||||
}
|
||||
|
||||
if (cappedImpactUsd > maxPriceImpactUsdBasedOnMaxPriceImpactFactor) {
|
||||
cappedImpactUsd = maxPriceImpactUsdBasedOnMaxPriceImpactFactor;
|
||||
export function capPositionImpactUsdByMaxPriceImpactFactor(
|
||||
marketInfo: MarketInfo,
|
||||
sizeDeltaUsd: bigint,
|
||||
positionImpactDeltaUsd: bigint
|
||||
) {
|
||||
const { maxPositiveImpactFactor, maxNegativeImpactFactor } = getMaxPositionImpactFactors(marketInfo);
|
||||
|
||||
const maxPriceImapctFactor = positionImpactDeltaUsd > 0 ? maxPositiveImpactFactor : maxNegativeImpactFactor;
|
||||
|
||||
const maxPriceImpactUsdBasedOnMaxPriceImpactFactor = applyFactor(bigMath.abs(sizeDeltaUsd), maxPriceImapctFactor);
|
||||
|
||||
if (bigMath.abs(positionImpactDeltaUsd) > maxPriceImpactUsdBasedOnMaxPriceImpactFactor) {
|
||||
positionImpactDeltaUsd = maxPriceImpactUsdBasedOnMaxPriceImpactFactor * (positionImpactDeltaUsd > 0 ? 1n : -1n);
|
||||
}
|
||||
|
||||
return cappedImpactUsd;
|
||||
return positionImpactDeltaUsd;
|
||||
}
|
||||
|
||||
export function getMaxPositionImpactFactors(marketInfo: MarketInfo) {
|
||||
let maxPositiveImpactFactor = marketInfo.maxPositionImpactFactorPositive;
|
||||
const maxNegativeImpactFactor = marketInfo.maxPositionImpactFactorNegative;
|
||||
|
||||
if (maxPositiveImpactFactor > maxNegativeImpactFactor) {
|
||||
maxPositiveImpactFactor = maxNegativeImpactFactor;
|
||||
}
|
||||
|
||||
return { maxPositiveImpactFactor, maxNegativeImpactFactor };
|
||||
}
|
||||
|
||||
export function getPriceImpactForPosition(
|
||||
@@ -118,7 +152,7 @@ export function getPriceImpactForPosition(
|
||||
isLong: isLong!,
|
||||
});
|
||||
|
||||
const priceImpactUsd = getPriceImpactUsd({
|
||||
const { priceImpactDeltaUsd, balanceWasImproved } = getPriceImpactUsd({
|
||||
currentLongUsd,
|
||||
currentShortUsd,
|
||||
nextLongUsd,
|
||||
@@ -129,12 +163,18 @@ export function getPriceImpactForPosition(
|
||||
fallbackToZero: opts.fallbackToZero,
|
||||
});
|
||||
|
||||
if (priceImpactUsd > 0) {
|
||||
return priceImpactUsd;
|
||||
if (priceImpactDeltaUsd > 0) {
|
||||
return {
|
||||
priceImpactDeltaUsd,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
if (bigMath.abs(marketInfo.virtualInventoryForPositions) <= 0) {
|
||||
return priceImpactUsd;
|
||||
return {
|
||||
priceImpactDeltaUsd,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
const virtualInventoryParams = getNextOpenInterestForVirtualInventory({
|
||||
@@ -143,7 +183,7 @@ export function getPriceImpactForPosition(
|
||||
isLong: isLong!,
|
||||
});
|
||||
|
||||
const priceImpactUsdForVirtualInventory = getPriceImpactUsd({
|
||||
const { priceImpactDeltaUsd: priceImpactUsdForVirtualInventory } = getPriceImpactUsd({
|
||||
currentLongUsd: virtualInventoryParams.currentLongUsd,
|
||||
currentShortUsd: virtualInventoryParams.currentShortUsd,
|
||||
nextLongUsd: virtualInventoryParams.nextLongUsd,
|
||||
@@ -154,7 +194,38 @@ export function getPriceImpactForPosition(
|
||||
fallbackToZero: opts.fallbackToZero,
|
||||
});
|
||||
|
||||
return priceImpactUsdForVirtualInventory < priceImpactUsd! ? priceImpactUsdForVirtualInventory : priceImpactUsd;
|
||||
return {
|
||||
priceImpactDeltaUsd:
|
||||
priceImpactUsdForVirtualInventory < priceImpactDeltaUsd!
|
||||
? priceImpactUsdForVirtualInventory
|
||||
: priceImpactDeltaUsd!,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
export function getProportionalPendingImpactValues({
|
||||
sizeInUsd,
|
||||
pendingImpactAmount,
|
||||
sizeDeltaUsd,
|
||||
indexToken,
|
||||
}: {
|
||||
sizeInUsd: bigint;
|
||||
pendingImpactAmount: bigint;
|
||||
sizeDeltaUsd: bigint;
|
||||
indexToken: TokenData;
|
||||
}) {
|
||||
const proportionalPendingImpactDeltaAmount =
|
||||
sizeDeltaUsd !== 0n && sizeInUsd !== 0n
|
||||
? bigMath.mulDiv(pendingImpactAmount, sizeDeltaUsd, sizeInUsd, pendingImpactAmount < 0n)
|
||||
: 0n;
|
||||
|
||||
const proportionalPendingImpactDeltaUsd = convertToUsd(
|
||||
proportionalPendingImpactDeltaAmount,
|
||||
indexToken.decimals,
|
||||
proportionalPendingImpactDeltaAmount > 0 ? indexToken.prices.minPrice : indexToken.prices.maxPrice
|
||||
)!;
|
||||
|
||||
return { proportionalPendingImpactDeltaAmount, proportionalPendingImpactDeltaUsd };
|
||||
}
|
||||
|
||||
export function getPriceImpactForSwap(
|
||||
@@ -190,7 +261,7 @@ export function getPriceImpactForSwap(
|
||||
shortDeltaUsd,
|
||||
});
|
||||
|
||||
const priceImpactUsd = getPriceImpactUsd({
|
||||
const { priceImpactDeltaUsd, balanceWasImproved } = getPriceImpactUsd({
|
||||
currentLongUsd: longPoolUsd,
|
||||
currentShortUsd: shortPoolUsd,
|
||||
nextLongUsd: nextLongPoolUsd,
|
||||
@@ -201,15 +272,21 @@ export function getPriceImpactForSwap(
|
||||
fallbackToZero: opts.fallbackToZero,
|
||||
});
|
||||
|
||||
if (priceImpactUsd > 0) {
|
||||
return priceImpactUsd;
|
||||
if (priceImpactDeltaUsd > 0) {
|
||||
return {
|
||||
priceImpactDeltaUsd,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
const virtualInventoryLong = marketInfo.virtualPoolAmountForLongToken;
|
||||
const virtualInventoryShort = marketInfo.virtualPoolAmountForShortToken;
|
||||
|
||||
if (virtualInventoryLong <= 0 || virtualInventoryShort <= 0) {
|
||||
return priceImpactUsd;
|
||||
return {
|
||||
priceImpactDeltaUsd,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
const virtualInventoryParams = getNextPoolAmountsParams({
|
||||
@@ -222,7 +299,7 @@ export function getPriceImpactForSwap(
|
||||
shortDeltaUsd,
|
||||
});
|
||||
|
||||
const priceImpactUsdForVirtualInventory = getPriceImpactUsd({
|
||||
const { priceImpactDeltaUsd: priceImpactUsdForVirtualInventory } = getPriceImpactUsd({
|
||||
currentLongUsd: virtualInventoryParams.longPoolUsd,
|
||||
currentShortUsd: virtualInventoryParams.shortPoolUsd,
|
||||
nextLongUsd: virtualInventoryParams.nextLongPoolUsd,
|
||||
@@ -233,7 +310,13 @@ export function getPriceImpactForSwap(
|
||||
fallbackToZero: opts.fallbackToZero,
|
||||
});
|
||||
|
||||
return priceImpactUsdForVirtualInventory < priceImpactUsd! ? priceImpactUsdForVirtualInventory : priceImpactUsd;
|
||||
return {
|
||||
priceImpactDeltaUsd:
|
||||
priceImpactUsdForVirtualInventory < priceImpactDeltaUsd!
|
||||
? priceImpactUsdForVirtualInventory
|
||||
: priceImpactDeltaUsd!,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
function getNextOpenInterestForVirtualInventory(p: { virtualInventory: bigint; usdDelta: bigint; isLong: boolean }) {
|
||||
@@ -335,7 +418,10 @@ export function getPriceImpactUsd(p: {
|
||||
|
||||
if (nextLongUsd < 0 || nextShortUsd < 0) {
|
||||
if (p.fallbackToZero) {
|
||||
return 0n;
|
||||
return {
|
||||
priceImpactDeltaUsd: 0n,
|
||||
balanceWasImproved: false,
|
||||
};
|
||||
} else {
|
||||
throw new Error("Negative pool amount");
|
||||
}
|
||||
@@ -346,13 +432,14 @@ export function getPriceImpactUsd(p: {
|
||||
|
||||
const isSameSideRebalance = p.currentLongUsd < p.currentShortUsd === nextLongUsd < nextShortUsd;
|
||||
|
||||
let impactUsd: bigint;
|
||||
let priceImpactDeltaUsd: bigint;
|
||||
|
||||
const balanceWasImproved = nextDiff < currentDiff;
|
||||
if (isSameSideRebalance) {
|
||||
const hasPositiveImpact = nextDiff < currentDiff;
|
||||
const factor = hasPositiveImpact ? p.factorPositive : p.factorNegative;
|
||||
|
||||
impactUsd = calculateImpactForSameSideRebalance({
|
||||
priceImpactDeltaUsd = calculateImpactForSameSideRebalance({
|
||||
currentDiff,
|
||||
nextDiff,
|
||||
hasPositiveImpact,
|
||||
@@ -360,7 +447,7 @@ export function getPriceImpactUsd(p: {
|
||||
exponentFactor: p.exponentFactor,
|
||||
});
|
||||
} else {
|
||||
impactUsd = calculateImpactForCrossoverRebalance({
|
||||
priceImpactDeltaUsd = calculateImpactForCrossoverRebalance({
|
||||
currentDiff,
|
||||
nextDiff,
|
||||
factorPositive: p.factorPositive,
|
||||
@@ -369,7 +456,10 @@ export function getPriceImpactUsd(p: {
|
||||
});
|
||||
}
|
||||
|
||||
return impactUsd;
|
||||
return {
|
||||
priceImpactDeltaUsd,
|
||||
balanceWasImproved,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {encodeAbiParameters, keccak256} from "viem";
|
||||
import {encodeAbiParameters, keccak256, stringToBytes} from "viem";
|
||||
import {LRUCache} from "./LruCache.js";
|
||||
|
||||
export const ZERO_DATA = "0x";
|
||||
|
||||
const dataCache = new LRUCache<string>(10_000);
|
||||
|
||||
export function hashData(dataTypes, dataValues) {
|
||||
export function hashData(dataTypes: string[], dataValues: (string | number | bigint | boolean)[]) {
|
||||
const key = JSON.stringify({ dataTypes, dataValues }, (_, val) => (typeof val === "bigint" ? String(val) : val));
|
||||
|
||||
if (dataCache.has(key)) {
|
||||
@@ -12,7 +14,7 @@ export function hashData(dataTypes, dataValues) {
|
||||
|
||||
// Convert dataTypes from array of strings to array of objects with 'type' property
|
||||
const abiParameters = dataTypes.map((type) => ({ type }));
|
||||
const bytes = encodeAbiParameters(abiParameters, dataValues);
|
||||
const bytes = encodeAbiParameters(abiParameters, dataValues as any);
|
||||
const hash = keccak256(bytes);
|
||||
|
||||
dataCache.set(key, hash);
|
||||
@@ -40,7 +42,8 @@ export function hashDataMap<
|
||||
): {
|
||||
[K in keyof R]: string;
|
||||
} {
|
||||
const result = {};
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
for (const key of Object.keys(map)) {
|
||||
if (!map[key]) {
|
||||
continue;
|
||||
@@ -55,3 +58,7 @@ export function hashDataMap<
|
||||
[K in keyof R]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function keccakString(string: string) {
|
||||
return keccak256(stringToBytes(string));
|
||||
}
|
||||
|
||||
@@ -1,44 +1,50 @@
|
||||
import {hashDataMap} from "./hash.js";
|
||||
|
||||
import {
|
||||
BORROWING_EXPONENT_FACTOR_KEY,
|
||||
BORROWING_FACTOR_KEY,
|
||||
FUNDING_DECREASE_FACTOR_PER_SECOND,
|
||||
FUNDING_EXPONENT_FACTOR_KEY,
|
||||
FUNDING_FACTOR_KEY,
|
||||
FUNDING_INCREASE_FACTOR_PER_SECOND,
|
||||
IS_MARKET_DISABLED_KEY,
|
||||
MAX_FUNDING_FACTOR_PER_SECOND,
|
||||
MAX_OPEN_INTEREST_KEY,
|
||||
MAX_PNL_FACTOR_FOR_TRADERS_KEY,
|
||||
MAX_PNL_FACTOR_KEY,
|
||||
MAX_POOL_AMOUNT_KEY,
|
||||
MAX_POOL_USD_FOR_DEPOSIT_KEY,
|
||||
MAX_POSITION_IMPACT_FACTOR_FOR_LIQUIDATIONS_KEY,
|
||||
MAX_POSITION_IMPACT_FACTOR_KEY,
|
||||
MIN_COLLATERAL_FACTOR_FOR_OPEN_INTEREST_MULTIPLIER_KEY,
|
||||
MIN_COLLATERAL_FACTOR_KEY,
|
||||
MIN_FUNDING_FACTOR_PER_SECOND,
|
||||
MIN_POSITION_IMPACT_POOL_AMOUNT_KEY,
|
||||
OPEN_INTEREST_IN_TOKENS_KEY,
|
||||
OPEN_INTEREST_KEY,
|
||||
OPEN_INTEREST_RESERVE_FACTOR_KEY,
|
||||
POOL_AMOUNT_ADJUSTMENT_KEY,
|
||||
POOL_AMOUNT_KEY,
|
||||
POSITION_FEE_FACTOR_KEY,
|
||||
POSITION_IMPACT_EXPONENT_FACTOR_KEY,
|
||||
POSITION_IMPACT_FACTOR_KEY,
|
||||
POSITION_IMPACT_POOL_AMOUNT_KEY,
|
||||
POSITION_IMPACT_POOL_DISTRIBUTION_RATE_KEY,
|
||||
RESERVE_FACTOR_KEY,
|
||||
SWAP_FEE_FACTOR_KEY,
|
||||
SWAP_IMPACT_EXPONENT_FACTOR_KEY,
|
||||
SWAP_IMPACT_FACTOR_KEY,
|
||||
SWAP_IMPACT_POOL_AMOUNT_KEY,
|
||||
THRESHOLD_FOR_DECREASE_FUNDING,
|
||||
THRESHOLD_FOR_STABLE_FUNDING,
|
||||
VIRTUAL_MARKET_ID_KEY,
|
||||
VIRTUAL_TOKEN_ID_KEY,
|
||||
ATOMIC_SWAP_FEE_FACTOR_KEY,
|
||||
BORROWING_EXPONENT_FACTOR_KEY,
|
||||
BORROWING_FACTOR_KEY,
|
||||
FUNDING_DECREASE_FACTOR_PER_SECOND,
|
||||
FUNDING_EXPONENT_FACTOR_KEY,
|
||||
FUNDING_FACTOR_KEY,
|
||||
FUNDING_INCREASE_FACTOR_PER_SECOND,
|
||||
IS_MARKET_DISABLED_KEY,
|
||||
LENT_POSITION_IMPACT_POOL_AMOUNT_KEY,
|
||||
MAX_FUNDING_FACTOR_PER_SECOND,
|
||||
MAX_LENDABLE_IMPACT_FACTOR_FOR_WITHDRAWALS_KEY,
|
||||
MAX_LENDABLE_IMPACT_FACTOR_KEY,
|
||||
MAX_LENDABLE_IMPACT_USD_KEY,
|
||||
MAX_OPEN_INTEREST_KEY,
|
||||
MAX_PNL_FACTOR_FOR_TRADERS_KEY,
|
||||
MAX_PNL_FACTOR_KEY,
|
||||
MAX_POOL_AMOUNT_KEY,
|
||||
MAX_POOL_USD_FOR_DEPOSIT_KEY,
|
||||
MAX_POSITION_IMPACT_FACTOR_FOR_LIQUIDATIONS_KEY,
|
||||
MAX_POSITION_IMPACT_FACTOR_KEY,
|
||||
MIN_COLLATERAL_FACTOR_FOR_LIQUIDATION_KEY,
|
||||
MIN_COLLATERAL_FACTOR_FOR_OPEN_INTEREST_MULTIPLIER_KEY,
|
||||
MIN_COLLATERAL_FACTOR_KEY,
|
||||
MIN_FUNDING_FACTOR_PER_SECOND,
|
||||
MIN_POSITION_IMPACT_POOL_AMOUNT_KEY,
|
||||
OPEN_INTEREST_IN_TOKENS_KEY,
|
||||
OPEN_INTEREST_KEY,
|
||||
OPEN_INTEREST_RESERVE_FACTOR_KEY,
|
||||
POOL_AMOUNT_ADJUSTMENT_KEY,
|
||||
POOL_AMOUNT_KEY,
|
||||
POSITION_FEE_FACTOR_KEY,
|
||||
POSITION_IMPACT_EXPONENT_FACTOR_KEY,
|
||||
POSITION_IMPACT_FACTOR_KEY,
|
||||
POSITION_IMPACT_POOL_AMOUNT_KEY,
|
||||
POSITION_IMPACT_POOL_DISTRIBUTION_RATE_KEY,
|
||||
RESERVE_FACTOR_KEY,
|
||||
SWAP_FEE_FACTOR_KEY,
|
||||
SWAP_IMPACT_EXPONENT_FACTOR_KEY,
|
||||
SWAP_IMPACT_FACTOR_KEY,
|
||||
SWAP_IMPACT_POOL_AMOUNT_KEY,
|
||||
THRESHOLD_FOR_DECREASE_FUNDING,
|
||||
THRESHOLD_FOR_STABLE_FUNDING,
|
||||
VIRTUAL_MARKET_ID_KEY,
|
||||
VIRTUAL_TOKEN_ID_KEY,
|
||||
} from "../configs/dataStore.js";
|
||||
import {MarketConfig} from "../configs/markets.js";
|
||||
|
||||
@@ -161,6 +167,14 @@ export function hashMarketConfigKeys(market: MarketConfig) {
|
||||
["bytes32", "bytes32", "address", "bool"],
|
||||
[MAX_PNL_FACTOR_KEY, MAX_PNL_FACTOR_FOR_TRADERS_KEY, marketAddress, false],
|
||||
],
|
||||
positionFeeFactorForBalanceWasImproved: [
|
||||
["bytes32", "address", "bool"],
|
||||
[POSITION_FEE_FACTOR_KEY, marketAddress, true],
|
||||
],
|
||||
positionFeeFactorForBalanceWasNotImproved: [
|
||||
["bytes32", "address", "bool"],
|
||||
[POSITION_FEE_FACTOR_KEY, marketAddress, false],
|
||||
],
|
||||
positionFeeFactorForPositiveImpact: [
|
||||
["bytes32", "address", "bool"],
|
||||
[POSITION_FEE_FACTOR_KEY, marketAddress, true],
|
||||
@@ -189,10 +203,30 @@ export function hashMarketConfigKeys(market: MarketConfig) {
|
||||
["bytes32", "address"],
|
||||
[MAX_POSITION_IMPACT_FACTOR_FOR_LIQUIDATIONS_KEY, marketAddress],
|
||||
],
|
||||
maxLendableImpactFactor: [
|
||||
["bytes32", "address"],
|
||||
[MAX_LENDABLE_IMPACT_FACTOR_KEY, marketAddress],
|
||||
],
|
||||
maxLendableImpactFactorForWithdrawals: [
|
||||
["bytes32", "address"],
|
||||
[MAX_LENDABLE_IMPACT_FACTOR_FOR_WITHDRAWALS_KEY, marketAddress],
|
||||
],
|
||||
maxLendableImpactUsd: [
|
||||
["bytes32", "address"],
|
||||
[MAX_LENDABLE_IMPACT_USD_KEY, marketAddress],
|
||||
],
|
||||
lentPositionImpactPoolAmount: [
|
||||
["bytes32", "address"],
|
||||
[LENT_POSITION_IMPACT_POOL_AMOUNT_KEY, marketAddress],
|
||||
],
|
||||
minCollateralFactor: [
|
||||
["bytes32", "address"],
|
||||
[MIN_COLLATERAL_FACTOR_KEY, marketAddress],
|
||||
],
|
||||
minCollateralFactorForLiquidation: [
|
||||
["bytes32", "address"],
|
||||
[MIN_COLLATERAL_FACTOR_FOR_LIQUIDATION_KEY, marketAddress],
|
||||
],
|
||||
minCollateralFactorForOpenInterestLong: [
|
||||
["bytes32", "address", "bool"],
|
||||
[MIN_COLLATERAL_FACTOR_FOR_OPEN_INTEREST_MULTIPLIER_KEY, marketAddress, true],
|
||||
@@ -205,6 +239,14 @@ export function hashMarketConfigKeys(market: MarketConfig) {
|
||||
["bytes32", "address"],
|
||||
[POSITION_IMPACT_EXPONENT_FACTOR_KEY, marketAddress],
|
||||
],
|
||||
swapFeeFactorForBalanceWasImproved: [
|
||||
["bytes32", "address", "bool"],
|
||||
[SWAP_FEE_FACTOR_KEY, marketAddress, true],
|
||||
],
|
||||
swapFeeFactorForBalanceWasNotImproved: [
|
||||
["bytes32", "address", "bool"],
|
||||
[SWAP_FEE_FACTOR_KEY, marketAddress, false],
|
||||
],
|
||||
swapFeeFactorForPositiveImpact: [
|
||||
["bytes32", "address", "bool"],
|
||||
[SWAP_FEE_FACTOR_KEY, marketAddress, true],
|
||||
@@ -213,6 +255,10 @@ export function hashMarketConfigKeys(market: MarketConfig) {
|
||||
["bytes32", "address", "bool"],
|
||||
[SWAP_FEE_FACTOR_KEY, marketAddress, false],
|
||||
],
|
||||
atomicSwapFeeFactor: [
|
||||
["bytes32", "address"],
|
||||
[ATOMIC_SWAP_FEE_FACTOR_KEY, marketAddress],
|
||||
],
|
||||
swapImpactFactorPositive: [
|
||||
["bytes32", "address", "bool"],
|
||||
[SWAP_IMPACT_FACTOR_KEY, marketAddress, true],
|
||||
|
||||
@@ -15,6 +15,14 @@ export function getMarketFullName(p: { longToken: Token; shortToken: Token; inde
|
||||
}
|
||||
|
||||
export function getMarketIndexName(p: ({ indexToken: Token } | { glvToken: Token }) & { isSpotOnly: boolean }) {
|
||||
if (p.isSpotOnly) {
|
||||
return `SWAP-ONLY`;
|
||||
}
|
||||
|
||||
return `${getMarketBaseName(p)}/USD`;
|
||||
}
|
||||
|
||||
export function getMarketBaseName(p: ({ indexToken: Token } | { glvToken: Token }) & { isSpotOnly: boolean }) {
|
||||
const { isSpotOnly } = p;
|
||||
|
||||
const firstToken = "indexToken" in p ? p.indexToken : p.glvToken;
|
||||
@@ -25,13 +33,13 @@ export function getMarketIndexName(p: ({ indexToken: Token } | { glvToken: Token
|
||||
|
||||
const prefix = getTokenVisualMultiplier(firstToken);
|
||||
|
||||
return `${prefix}${firstToken.baseSymbol || firstToken.symbol}/USD`;
|
||||
return `${prefix}${firstToken.baseSymbol || firstToken.symbol}`;
|
||||
}
|
||||
|
||||
export function getMarketPoolName(p: { longToken: Token; shortToken: Token }) {
|
||||
export function getMarketPoolName(p: { longToken: Token; shortToken: Token }, separator = "-") {
|
||||
const { longToken, shortToken } = p;
|
||||
|
||||
return `${longToken.symbol}-${shortToken.symbol}`;
|
||||
return `${longToken.symbol}${separator}${shortToken.symbol}`;
|
||||
}
|
||||
|
||||
export function getContractMarketPrices(tokensData: TokensData, market: Market): ContractMarketPrices | undefined {
|
||||
@@ -213,3 +221,9 @@ export function getPriceForPnl(prices: TokenPrices, isLong: boolean, maximize: b
|
||||
|
||||
return maximize ? prices.minPrice : prices.maxPrice;
|
||||
}
|
||||
|
||||
export function getIsMarketAvailableForExpressSwaps(marketInfo: MarketInfo) {
|
||||
return [marketInfo.indexToken, marketInfo.longToken, marketInfo.shortToken].every(
|
||||
(token) => token.hasPriceFeedProvider
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { AbiId, abis as allAbis } from "../abis/index.js";
|
||||
import { GmxSdk } from "../index.js";
|
||||
import { sleep } from "./common.js";
|
||||
import {AbiId, abis as allAbis} from "../abis/index.js";
|
||||
import {GmxSdk} from "../index.js";
|
||||
import {sleep} from "./common.js";
|
||||
|
||||
export const MAX_TIMEOUT = 20000;
|
||||
export const MAX_TIMEOUT = 60000; // Increased from 20s to 60s
|
||||
|
||||
export type MulticallProviderUrls = {
|
||||
primary: string;
|
||||
@@ -33,13 +33,33 @@ export class Multicall {
|
||||
return this.sdk.chainId;
|
||||
}
|
||||
|
||||
async call(request: MulticallRequestConfig<any>, maxTimeout: number) {
|
||||
async call(request: MulticallRequestConfig<any>, maxTimeout: number, retryCount: number = 0) {
|
||||
const client = this.sdk.publicClient;
|
||||
|
||||
if (!client) {
|
||||
throw new Error("Public client is not initialized");
|
||||
}
|
||||
|
||||
const MAX_RETRIES = 3;
|
||||
const BASE_DELAY = 1000; // 1 second base delay
|
||||
|
||||
try {
|
||||
return await this.executeMulticallWithRetry(request, maxTimeout, retryCount, MAX_RETRIES, BASE_DELAY);
|
||||
} catch (error) {
|
||||
// If all retries failed, throw the error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async executeMulticallWithRetry(
|
||||
request: MulticallRequestConfig<any>,
|
||||
maxTimeout: number,
|
||||
retryCount: number,
|
||||
maxRetries: number,
|
||||
baseDelay: number
|
||||
) {
|
||||
const client = this.sdk.publicClient;
|
||||
|
||||
const originalKeys: {
|
||||
contractKey: string;
|
||||
callKey: string;
|
||||
@@ -47,7 +67,7 @@ export class Multicall {
|
||||
|
||||
const abis: any = {};
|
||||
|
||||
const encodedPayload: { address: string; abi: any; functionName: string; args: any }[] = [];
|
||||
const encodedPayload: { address: `0x${string}`; abi: any; functionName: string; args: any }[] = [];
|
||||
|
||||
const contractKeys = Object.keys(request);
|
||||
|
||||
@@ -78,7 +98,7 @@ export class Multicall {
|
||||
});
|
||||
|
||||
encodedPayload.push({
|
||||
address: contractCallConfig.contractAddress,
|
||||
address: contractCallConfig.contractAddress as `0x${string}`,
|
||||
functionName: call.methodName,
|
||||
args: call.params,
|
||||
abi,
|
||||
@@ -93,7 +113,7 @@ export class Multicall {
|
||||
data: {},
|
||||
};
|
||||
|
||||
response.forEach(({ result, status, error }, i) => {
|
||||
response.forEach(({ result, status, error }: { result: any; status: string; error: any }, i: number) => {
|
||||
const { contractKey, callKey } = originalKeys[i];
|
||||
|
||||
if (status === "success") {
|
||||
@@ -134,33 +154,65 @@ export class Multicall {
|
||||
|
||||
const timeoutController = new AbortController();
|
||||
|
||||
const result = await Promise.race([
|
||||
// @ts-ignore
|
||||
client.multicall({ contracts: encodedPayload as any }),
|
||||
sleep(maxTimeout, timeoutController.signal).then(() => Promise.reject(new Error("multicall timeout"))),
|
||||
])
|
||||
.then((response) => {
|
||||
timeoutController.abort();
|
||||
return processResponse(response);
|
||||
})
|
||||
.catch((_viemError) => {
|
||||
timeoutController.abort();
|
||||
const e = new Error(_viemError.message.slice(0, 150));
|
||||
const multicallPromise = (client.multicall as any)({ contracts: encodedPayload });
|
||||
const timeoutPromise = sleep(maxTimeout, timeoutController.signal).then(() => Promise.reject(new Error("multicall timeout")));
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
console.error(e);
|
||||
try {
|
||||
const result = await Promise.race([multicallPromise, timeoutPromise])
|
||||
.then((response) => {
|
||||
timeoutController.abort();
|
||||
return processResponse(response);
|
||||
});
|
||||
|
||||
throw e;
|
||||
});
|
||||
if (result.success) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
console.error(result.errors);
|
||||
|
||||
if (result.success) {
|
||||
return result;
|
||||
} catch (error) {
|
||||
timeoutController.abort();
|
||||
|
||||
// Better error handling to distinguish between different types of errors
|
||||
let errorMessage = error.message || 'Unknown multicall error';
|
||||
let shouldRetry = false;
|
||||
|
||||
if (errorMessage.includes('timeout') || errorMessage.includes('TIMEOUT')) {
|
||||
errorMessage = 'multicall timeout';
|
||||
shouldRetry = true;
|
||||
} else if (errorMessage.includes('RPC') || errorMessage.includes('rpc')) {
|
||||
errorMessage = 'RPC error: ' + errorMessage;
|
||||
shouldRetry = true;
|
||||
} else if (errorMessage.includes('network') || errorMessage.includes('NETWORK')) {
|
||||
errorMessage = 'Network error: ' + errorMessage;
|
||||
shouldRetry = true;
|
||||
} else if (errorMessage.includes('rate limit') || errorMessage.includes('RATE_LIMIT')) {
|
||||
errorMessage = 'Rate limit error: ' + errorMessage;
|
||||
shouldRetry = true;
|
||||
} else {
|
||||
errorMessage = 'Multicall error: ' + errorMessage.slice(0, 150);
|
||||
shouldRetry = false; // Don't retry for other types of errors
|
||||
}
|
||||
|
||||
// If we should retry and haven't exceeded max retries, retry with exponential backoff
|
||||
if (shouldRetry && retryCount < maxRetries) {
|
||||
const delay = baseDelay * Math.pow(2, retryCount); // Exponential backoff
|
||||
console.log(`Multicall failed (attempt ${retryCount + 1}/${maxRetries + 1}), retrying in ${delay}ms: ${errorMessage}`);
|
||||
|
||||
await sleep(delay);
|
||||
return this.executeMulticallWithRetry(request, maxTimeout, retryCount + 1, maxRetries, baseDelay);
|
||||
}
|
||||
|
||||
const e = new Error(errorMessage);
|
||||
e.name = 'MulticallError';
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
console.error('Multicall failed after all retries:', e.message);
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
console.error(result.errors);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,33 @@
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT} from "../configs/factors.js";
|
||||
import {formatUnits, parseUnits} from "viem/utils";
|
||||
import {bigMath} from "./bigmath.js";
|
||||
|
||||
export type Numeric = number | bigint;
|
||||
export type BigNumberish = string | Numeric;
|
||||
|
||||
export const USD_DECIMALS = 30;
|
||||
|
||||
export const BASIS_POINTS_DIVISOR = 10000;
|
||||
export const BASIS_POINTS_DIVISOR_BIGINT = 10000n;
|
||||
export const BASIS_POINTS_DECIMALS = 4;
|
||||
|
||||
export const PRECISION = expandDecimals(1, 30);
|
||||
export const PRECISION_DECIMALS = 30;
|
||||
export const PRECISION = expandDecimals(1, PRECISION_DECIMALS);
|
||||
|
||||
export const BN_ZERO = 0n;
|
||||
export const BN_ONE = 1n;
|
||||
export const BN_NEGATIVE_ONE = -1n;
|
||||
|
||||
export function expandDecimals(n: bigint | number, decimals: number): bigint {
|
||||
export const MaxUint256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
|
||||
export const PERCENT_PRECISION_DECIMALS = PRECISION_DECIMALS - 2;
|
||||
|
||||
const MAX_EXCEEDING_THRESHOLD = "1000000000";
|
||||
const MIN_EXCEEDING_THRESHOLD = "0.01";
|
||||
|
||||
export const TRIGGER_PREFIX_ABOVE = ">";
|
||||
export const TRIGGER_PREFIX_BELOW = "<";
|
||||
|
||||
export function expandDecimals(n: BigNumberish, decimals: number): bigint {
|
||||
return BigInt(n) * 10n ** BigInt(decimals);
|
||||
}
|
||||
|
||||
@@ -62,6 +82,13 @@ export function numberToBigint(value: number, decimals: number) {
|
||||
return negative ? -res : res;
|
||||
}
|
||||
|
||||
export const trimZeroDecimals = (amount: string) => {
|
||||
if (parseFloat(amount) === parseInt(amount)) {
|
||||
return parseInt(amount).toString();
|
||||
}
|
||||
return amount;
|
||||
};
|
||||
|
||||
export function bigintToNumber(value: bigint, decimals: number) {
|
||||
const negative = value < 0;
|
||||
if (negative) value *= -1n;
|
||||
@@ -76,3 +103,702 @@ export function bigintToNumber(value: bigint, decimals: number) {
|
||||
export function adjustForDecimals(amount: bigint, divDecimals: number, mulDecimals: number) {
|
||||
return (amount * expandDecimals(1, mulDecimals)) / expandDecimals(1, divDecimals);
|
||||
}
|
||||
|
||||
export function formatUsd(
|
||||
usd?: bigint,
|
||||
opts: {
|
||||
fallbackToZero?: boolean;
|
||||
displayDecimals?: number;
|
||||
maxThreshold?: string | null;
|
||||
minThreshold?: string;
|
||||
displayPlus?: boolean;
|
||||
visualMultiplier?: number;
|
||||
} = {}
|
||||
) {
|
||||
const { fallbackToZero = false, displayDecimals = 2 } = opts;
|
||||
|
||||
if (typeof usd !== "bigint") {
|
||||
if (fallbackToZero) {
|
||||
usd = 0n;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.visualMultiplier) {
|
||||
usd *= BigInt(opts.visualMultiplier);
|
||||
}
|
||||
|
||||
const defaultMinThreshold = displayDecimals > 1 ? "0." + "0".repeat(displayDecimals - 1) + "1" : undefined;
|
||||
|
||||
const exceedingInfo = getLimitedDisplay(usd, USD_DECIMALS, {
|
||||
maxThreshold: opts.maxThreshold,
|
||||
minThreshold: opts.minThreshold ?? defaultMinThreshold,
|
||||
});
|
||||
|
||||
const maybePlus = opts.displayPlus ? "+" : "";
|
||||
const sign = usd < 0n ? "-" : maybePlus;
|
||||
const symbol = exceedingInfo.symbol ? `${exceedingInfo.symbol} ` : "";
|
||||
const displayUsd = formatAmount(exceedingInfo.value, USD_DECIMALS, displayDecimals, true);
|
||||
return `${symbol}${sign}$\u200a${displayUsd}`;
|
||||
}
|
||||
|
||||
export function formatDeltaUsd(
|
||||
deltaUsd?: bigint,
|
||||
percentage?: bigint,
|
||||
opts: { fallbackToZero?: boolean; showPlusForZero?: boolean } = {}
|
||||
) {
|
||||
if (typeof deltaUsd !== "bigint") {
|
||||
if (opts.fallbackToZero) {
|
||||
return `${formatUsd(0n)} (${formatAmount(0n, 2, 2)}%)`;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sign = getPlusOrMinusSymbol(deltaUsd, { showPlusForZero: opts.showPlusForZero });
|
||||
|
||||
const exceedingInfo = getLimitedDisplay(deltaUsd, USD_DECIMALS);
|
||||
const percentageStr = percentage !== undefined ? ` (${sign}${formatPercentage(bigMath.abs(percentage))})` : "";
|
||||
const deltaUsdStr = formatAmount(exceedingInfo.value, USD_DECIMALS, 2, true);
|
||||
const symbol = exceedingInfo.symbol ? `${exceedingInfo.symbol} ` : "";
|
||||
|
||||
return `${symbol}${sign}$\u200a${deltaUsdStr}${percentageStr}`;
|
||||
}
|
||||
|
||||
export function formatPercentage(
|
||||
percentage?: bigint,
|
||||
opts: { fallbackToZero?: boolean; signed?: boolean; displayDecimals?: number; bps?: boolean } = {}
|
||||
) {
|
||||
const { fallbackToZero = false, signed = false, displayDecimals = 2, bps = true } = opts;
|
||||
|
||||
if (percentage === undefined) {
|
||||
if (fallbackToZero) {
|
||||
return `${formatAmount(0n, PERCENT_PRECISION_DECIMALS, displayDecimals)}%`;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const sign = signed ? `${getPlusOrMinusSymbol(percentage)}\u200a` : "";
|
||||
|
||||
return `${sign}${formatAmount(bigMath.abs(percentage), bps ? 2 : PERCENT_PRECISION_DECIMALS, displayDecimals)}%`;
|
||||
}
|
||||
|
||||
export function formatTokenAmount(
|
||||
amount?: bigint,
|
||||
tokenDecimals?: number,
|
||||
symbol?: string,
|
||||
opts: {
|
||||
showAllSignificant?: boolean;
|
||||
displayDecimals?: number;
|
||||
fallbackToZero?: boolean;
|
||||
useCommas?: boolean;
|
||||
minThreshold?: string;
|
||||
maxThreshold?: string;
|
||||
displayPlus?: boolean;
|
||||
isStable?: boolean;
|
||||
} = {}
|
||||
) {
|
||||
const {
|
||||
showAllSignificant = false,
|
||||
fallbackToZero = false,
|
||||
useCommas = false,
|
||||
minThreshold = "0",
|
||||
maxThreshold,
|
||||
} = opts;
|
||||
|
||||
const displayDecimals = opts.displayDecimals ?? (opts.isStable ? 2 : 4);
|
||||
|
||||
const symbolStr = symbol ? ` ${symbol}` : "";
|
||||
|
||||
if (typeof amount !== "bigint" || !tokenDecimals) {
|
||||
if (fallbackToZero) {
|
||||
amount = 0n;
|
||||
tokenDecimals = displayDecimals;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
let amountStr: string;
|
||||
|
||||
const maybePlus = opts.displayPlus ? "+" : "";
|
||||
const sign = amount < 0n ? "-" : maybePlus;
|
||||
|
||||
if (showAllSignificant) {
|
||||
amountStr = formatAmountFree(amount, tokenDecimals, tokenDecimals);
|
||||
} else {
|
||||
const exceedingInfo = getLimitedDisplay(amount, tokenDecimals, { maxThreshold, minThreshold });
|
||||
const symbol = exceedingInfo.symbol ? `${exceedingInfo.symbol} ` : "";
|
||||
amountStr = `${symbol}${sign}${formatAmount(exceedingInfo.value, tokenDecimals, displayDecimals, useCommas, undefined)}`;
|
||||
}
|
||||
|
||||
return `${amountStr}${symbolStr}`;
|
||||
}
|
||||
|
||||
export function formatTokenAmountWithUsd(
|
||||
tokenAmount?: bigint,
|
||||
usdAmount?: bigint,
|
||||
tokenSymbol?: string,
|
||||
tokenDecimals?: number,
|
||||
opts: {
|
||||
fallbackToZero?: boolean;
|
||||
displayDecimals?: number;
|
||||
displayPlus?: boolean;
|
||||
isStable?: boolean;
|
||||
} = {}
|
||||
) {
|
||||
if (typeof tokenAmount !== "bigint" || typeof usdAmount !== "bigint" || !tokenSymbol || !tokenDecimals) {
|
||||
if (!opts.fallbackToZero) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const tokenStr = formatTokenAmount(tokenAmount, tokenDecimals, tokenSymbol, {
|
||||
...opts,
|
||||
useCommas: true,
|
||||
displayPlus: opts.displayPlus,
|
||||
});
|
||||
|
||||
const usdStr = formatUsd(usdAmount, {
|
||||
fallbackToZero: opts.fallbackToZero,
|
||||
displayPlus: opts.displayPlus,
|
||||
});
|
||||
|
||||
return `${tokenStr} (${usdStr})`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param opts.signed - Default `true`. whether to display a `+` or `-` sign for all non-zero values.
|
||||
*/
|
||||
export function formatRatePercentage(rate?: bigint, opts?: { displayDecimals?: number; signed?: boolean }) {
|
||||
if (typeof rate !== "bigint") {
|
||||
return "-";
|
||||
}
|
||||
|
||||
const signed = opts?.signed ?? true;
|
||||
const plurOrMinus = signed ? getPlusOrMinusSymbol(rate) : "";
|
||||
|
||||
const amount = bigMath.abs(rate * 100n);
|
||||
return `${plurOrMinus}\u200a${formatAmount(amount, 30, opts?.displayDecimals ?? 4)}%`;
|
||||
}
|
||||
|
||||
export function formatUsdPrice(price?: bigint, opts: Parameters<typeof formatUsd>[1] = {}) {
|
||||
if (price === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (price < 0n) {
|
||||
return "NA";
|
||||
}
|
||||
|
||||
const decimals = calculateDisplayDecimals(price, undefined, opts.visualMultiplier);
|
||||
|
||||
return formatUsd(price, {
|
||||
...opts,
|
||||
displayDecimals: decimals,
|
||||
});
|
||||
}
|
||||
|
||||
export function formatPercentageDisplay(percentage: number, hideThreshold?: number) {
|
||||
if (hideThreshold && percentage < hideThreshold) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `${percentage}%`;
|
||||
}
|
||||
|
||||
export function formatAmountHuman(
|
||||
amount: BigNumberish | undefined,
|
||||
tokenDecimals: number,
|
||||
showDollar = false,
|
||||
displayDecimals = 1
|
||||
) {
|
||||
if (amount === undefined) {
|
||||
return "...";
|
||||
}
|
||||
|
||||
let n = Number(formatAmount(amount, tokenDecimals));
|
||||
// For large numbers, we can neglect the decimals to avoid decimals in cases like 9999999.99999
|
||||
if (n >= 1_000_000) {
|
||||
n = Math.round(n);
|
||||
}
|
||||
const isNegative = n < 0;
|
||||
const absN = Math.abs(n);
|
||||
const sign = showDollar ? "$\u200a" : "";
|
||||
|
||||
if (absN >= 1_000_000_000) {
|
||||
return `${isNegative ? "-" : ""}${sign}${(absN / 1_000_000_000).toFixed(displayDecimals)}b`;
|
||||
}
|
||||
|
||||
if (absN >= 1_000_000) {
|
||||
return `${isNegative ? "-" : ""}${sign}${(absN / 1_000_000).toFixed(displayDecimals)}m`;
|
||||
}
|
||||
|
||||
if (absN >= 1000) {
|
||||
return `${isNegative ? "-" : ""}${sign}${(absN / 1_000).toFixed(displayDecimals)}k`;
|
||||
}
|
||||
|
||||
return `${isNegative ? "-" : ""}${sign}${absN.toFixed(displayDecimals)}`;
|
||||
}
|
||||
|
||||
export function formatBalanceAmount(
|
||||
amount: bigint,
|
||||
tokenDecimals: number,
|
||||
tokenSymbol?: string,
|
||||
{
|
||||
showZero = false,
|
||||
toExponential = true,
|
||||
isStable = false,
|
||||
signed = false,
|
||||
}: {
|
||||
showZero?: boolean;
|
||||
toExponential?: boolean;
|
||||
isStable?: boolean;
|
||||
signed?: boolean;
|
||||
} = {}
|
||||
): string {
|
||||
if (amount === undefined) return "-";
|
||||
|
||||
if (amount === 0n) {
|
||||
if (showZero === true) {
|
||||
if (tokenSymbol) {
|
||||
if (isStable) {
|
||||
return `0.00 ${tokenSymbol}`;
|
||||
}
|
||||
return `0.0000 ${tokenSymbol}`;
|
||||
}
|
||||
if (isStable) {
|
||||
return "0.00";
|
||||
}
|
||||
return "0.0000";
|
||||
}
|
||||
|
||||
return "-";
|
||||
}
|
||||
|
||||
const sign = signed || amount < 0n ? getPlusOrMinusSymbol(amount) : "";
|
||||
const absAmount = bigMath.abs(amount);
|
||||
const absAmountFloat = bigintToNumber(absAmount, tokenDecimals);
|
||||
|
||||
let value = "";
|
||||
|
||||
const baseDecimals = isStable ? 2 : 4;
|
||||
if (absAmountFloat >= 1) value = formatAmount(absAmount, tokenDecimals, baseDecimals, true);
|
||||
else if (absAmountFloat >= 0.1) value = formatAmount(absAmount, tokenDecimals, baseDecimals + 1, true);
|
||||
else if (absAmountFloat >= 0.01) value = formatAmount(absAmount, tokenDecimals, baseDecimals + 2, true);
|
||||
else if (absAmountFloat >= 0.001) value = formatAmount(absAmount, tokenDecimals, baseDecimals + 3, true);
|
||||
else if (absAmountFloat >= 1e-8) value = formatAmount(absAmount, tokenDecimals, 8, true);
|
||||
else {
|
||||
if (toExponential) {
|
||||
value = bigintToNumber(absAmount, tokenDecimals).toExponential(2);
|
||||
} else {
|
||||
value = bigintToNumber(absAmount, tokenDecimals).toFixed(8);
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenSymbol) {
|
||||
// Non-breaking space
|
||||
return `${sign}${value} ${tokenSymbol}`;
|
||||
}
|
||||
|
||||
return `${sign}${value}`;
|
||||
}
|
||||
|
||||
export function formatFactor(factor: bigint) {
|
||||
if (factor == 0n) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
if (bigMath.abs(factor) > PRECISION * 1000n) {
|
||||
return (factor / PRECISION).toString();
|
||||
}
|
||||
|
||||
const trailingZeroes =
|
||||
bigMath
|
||||
.abs(factor)
|
||||
.toString()
|
||||
.match(/^(.+?)(?<zeroes>0*)$/)?.groups?.zeroes?.length || 0;
|
||||
const factorDecimals = 30 - trailingZeroes;
|
||||
return formatAmount(factor, 30, factorDecimals);
|
||||
}
|
||||
export function numberWithCommas(x: BigNumberish, { showDollar = false }: { showDollar?: boolean } = {}) {
|
||||
if (x === undefined || x === null) {
|
||||
return "...";
|
||||
}
|
||||
|
||||
const parts = x.toString().split(".");
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
return `${showDollar ? "$\u200a" : ""}${parts.join(".")}`;
|
||||
}
|
||||
|
||||
export const formatAmount = (
|
||||
amount: BigNumberish | undefined,
|
||||
tokenDecimals: number,
|
||||
displayDecimals?: number,
|
||||
useCommas?: boolean,
|
||||
defaultValue?: string,
|
||||
visualMultiplier?: number
|
||||
) => {
|
||||
if (defaultValue === undefined || defaultValue === null) {
|
||||
defaultValue = "...";
|
||||
}
|
||||
if (amount === undefined || amount === null || amount === "") {
|
||||
return defaultValue;
|
||||
}
|
||||
if (displayDecimals === undefined) {
|
||||
displayDecimals = 4;
|
||||
}
|
||||
const amountBigInt = roundWithDecimals(BigInt(amount) * BigInt(visualMultiplier ?? 1), {
|
||||
displayDecimals,
|
||||
decimals: tokenDecimals,
|
||||
});
|
||||
let amountStr = formatUnits(amountBigInt, tokenDecimals);
|
||||
amountStr = limitDecimals(amountStr, displayDecimals);
|
||||
if (displayDecimals !== 0) {
|
||||
amountStr = padDecimals(amountStr, displayDecimals);
|
||||
}
|
||||
if (useCommas) {
|
||||
return numberWithCommas(amountStr);
|
||||
}
|
||||
return amountStr;
|
||||
};
|
||||
|
||||
export const formatKeyAmount = <T extends {}>(
|
||||
map: T | undefined,
|
||||
key: keyof T,
|
||||
tokenDecimals: number,
|
||||
displayDecimals: number,
|
||||
useCommas?: boolean
|
||||
) => {
|
||||
const value = map ? map[key] ?? undefined : undefined;
|
||||
if (value === undefined || value === null) {
|
||||
return "...";
|
||||
}
|
||||
|
||||
return formatAmount(value as bigint, tokenDecimals, displayDecimals, useCommas);
|
||||
};
|
||||
|
||||
export const formatArrayAmount = (
|
||||
arr: any[],
|
||||
index: number,
|
||||
tokenDecimals: number,
|
||||
displayDecimals?: number,
|
||||
useCommas?: boolean
|
||||
) => {
|
||||
if (!arr || arr[index] === undefined || arr[index] === null) {
|
||||
return "...";
|
||||
}
|
||||
|
||||
return formatAmount(arr[index], tokenDecimals, displayDecimals, useCommas);
|
||||
};
|
||||
|
||||
export const formatAmountFree = (amount: BigNumberish, tokenDecimals: number, displayDecimals?: number) => {
|
||||
if (amount === undefined || amount === null) {
|
||||
return "...";
|
||||
}
|
||||
|
||||
amount = BigInt(amount);
|
||||
|
||||
let amountStr = formatUnits(amount, tokenDecimals);
|
||||
amountStr = limitDecimals(amountStr, displayDecimals);
|
||||
return trimZeroDecimals(amountStr);
|
||||
};
|
||||
|
||||
export function getLimitedDisplay(
|
||||
amount: bigint,
|
||||
tokenDecimals: number,
|
||||
opts: { maxThreshold?: BigNumberish | null; minThreshold?: BigNumberish } = {}
|
||||
) {
|
||||
const { maxThreshold = MAX_EXCEEDING_THRESHOLD, minThreshold = MIN_EXCEEDING_THRESHOLD } = opts;
|
||||
const max = maxThreshold === null ? null : expandDecimals(BigInt(maxThreshold), tokenDecimals);
|
||||
const min = parseUnits(minThreshold.toString(), tokenDecimals);
|
||||
const absAmount = bigMath.abs(amount);
|
||||
|
||||
if (absAmount == 0n) {
|
||||
return {
|
||||
symbol: "",
|
||||
value: absAmount,
|
||||
};
|
||||
}
|
||||
|
||||
const symbol = max !== null && absAmount > max ? TRIGGER_PREFIX_ABOVE : absAmount < min ? TRIGGER_PREFIX_BELOW : "";
|
||||
const value = max !== null && absAmount > max ? max : absAmount < min ? min : absAmount;
|
||||
|
||||
return {
|
||||
symbol,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
export const limitDecimals = (amount: BigNumberish, maxDecimals?: number) => {
|
||||
let amountStr = amount.toString();
|
||||
if (maxDecimals === undefined) {
|
||||
return amountStr;
|
||||
}
|
||||
if (maxDecimals === 0) {
|
||||
return amountStr.split(".")[0];
|
||||
}
|
||||
const dotIndex = amountStr.indexOf(".");
|
||||
if (dotIndex !== -1) {
|
||||
let decimals = amountStr.length - dotIndex - 1;
|
||||
if (decimals > maxDecimals) {
|
||||
amountStr = amountStr.substr(0, amountStr.length - (decimals - maxDecimals));
|
||||
}
|
||||
}
|
||||
|
||||
return amountStr;
|
||||
};
|
||||
|
||||
export const padDecimals = (amount: BigNumberish, minDecimals: number) => {
|
||||
let amountStr = amount.toString();
|
||||
const dotIndex = amountStr.indexOf(".");
|
||||
if (dotIndex !== -1) {
|
||||
const decimals = amountStr.length - dotIndex - 1;
|
||||
if (decimals < minDecimals) {
|
||||
amountStr = amountStr.padEnd(amountStr.length + (minDecimals - decimals), "0");
|
||||
}
|
||||
} else {
|
||||
amountStr = amountStr + "." + "0".repeat(minDecimals);
|
||||
}
|
||||
return amountStr;
|
||||
};
|
||||
|
||||
export function getPlusOrMinusSymbol(value?: bigint, opts: { showPlusForZero?: boolean } = {}): string {
|
||||
if (value === undefined) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const { showPlusForZero = false } = opts;
|
||||
return value === 0n ? (showPlusForZero ? "+" : "") : value < 0n ? "-" : "+";
|
||||
}
|
||||
|
||||
export function roundWithDecimals(value: BigNumberish, opts: { displayDecimals: number; decimals: number }): bigint {
|
||||
if (opts.displayDecimals === opts.decimals) {
|
||||
return BigInt(value);
|
||||
}
|
||||
|
||||
let valueString = value.toString();
|
||||
let isNegative = false;
|
||||
|
||||
if (valueString[0] === "-") {
|
||||
valueString = valueString.slice(1);
|
||||
isNegative = true;
|
||||
}
|
||||
|
||||
if (valueString.length < opts.decimals) {
|
||||
valueString = valueString.padStart(opts.decimals, "0");
|
||||
}
|
||||
|
||||
const mainPart = valueString.slice(0, valueString.length - opts.decimals + opts.displayDecimals);
|
||||
const partToRound = valueString.slice(valueString.length - opts.decimals + opts.displayDecimals);
|
||||
|
||||
let mainPartBigInt = BigInt(mainPart);
|
||||
|
||||
let returnValue = mainPartBigInt;
|
||||
|
||||
if (partToRound.length !== 0) {
|
||||
if (Number(partToRound[0]) >= 5) {
|
||||
mainPartBigInt += 1n;
|
||||
}
|
||||
|
||||
returnValue = BigInt(mainPartBigInt.toString() + new Array(partToRound.length).fill("0").join(""));
|
||||
}
|
||||
|
||||
return isNegative ? returnValue * -1n : returnValue;
|
||||
}
|
||||
|
||||
// TODO: Remove this function
|
||||
export function toBigNumberWithDecimals(value: string, decimals: number): bigint {
|
||||
if (!value) return BN_ZERO;
|
||||
|
||||
const parts = value.split(".");
|
||||
const integerPart = parts[0];
|
||||
const decimalPart = parts.length > 1 ? parts[1] : "";
|
||||
|
||||
const paddingZeros = decimals - decimalPart.length;
|
||||
|
||||
if (paddingZeros >= 0) {
|
||||
const result = integerPart + decimalPart + "0".repeat(paddingZeros);
|
||||
return BigInt(result);
|
||||
} else {
|
||||
const result = integerPart + decimalPart.substring(0, decimals);
|
||||
return BigInt(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated Use BigInt instead
|
||||
*/
|
||||
export function bigNumberify(n?: BigNumberish | null | undefined) {
|
||||
try {
|
||||
if (n === undefined) throw new Error("n is undefined");
|
||||
if (n === null) throw new Error("n is null");
|
||||
|
||||
return BigInt(n);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("bigNumberify error", e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export const parseValue = (value: string, tokenDecimals: number) => {
|
||||
const pValue = parseFloat(value);
|
||||
|
||||
if (isNaN(pValue)) {
|
||||
return undefined;
|
||||
}
|
||||
value = limitDecimals(value, tokenDecimals);
|
||||
const amount = parseUnits(value, tokenDecimals);
|
||||
return bigNumberify(amount);
|
||||
};
|
||||
|
||||
export function roundUpDivision(a: bigint, b: bigint) {
|
||||
return (a + b - 1n) / b;
|
||||
}
|
||||
|
||||
export function roundToTwoDecimals(n: number) {
|
||||
return Math.round(n * 100) / 100;
|
||||
}
|
||||
|
||||
export function roundToOrder(n: bigint, significantDigits = 1) {
|
||||
const decimals = Math.max(n.toString().length - significantDigits, 0);
|
||||
return (n / expandDecimals(1, decimals)) * expandDecimals(1, decimals);
|
||||
}
|
||||
|
||||
export function roundBigIntToDecimals(value: bigint, tokenDecimals: number, roundToDecimals: number): bigint {
|
||||
const excessDecimals = tokenDecimals - roundToDecimals;
|
||||
const divisor = BigInt(10 ** excessDecimals);
|
||||
const scaledValue = value / divisor;
|
||||
const remainder = scaledValue % 10n;
|
||||
const roundedValue = remainder >= 5n ? scaledValue + 10n - remainder : scaledValue - remainder;
|
||||
return roundedValue * divisor;
|
||||
}
|
||||
|
||||
export function minBigNumber(...args: bigint[]) {
|
||||
if (!args.length) return undefined;
|
||||
|
||||
return args.reduce((acc, num) => (num < acc ? num : acc), args[0]);
|
||||
}
|
||||
|
||||
export function maxbigint(...args: bigint[]) {
|
||||
if (!args.length) return undefined;
|
||||
|
||||
return args.reduce((acc, num) => (num > acc ? num : acc), args[0]);
|
||||
}
|
||||
|
||||
export function removeTrailingZeros(amount: string | number) {
|
||||
const amountWithoutZeros = Number(amount);
|
||||
if (!amountWithoutZeros) return amount;
|
||||
return amountWithoutZeros;
|
||||
}
|
||||
|
||||
type SerializedBigIntsInObject<T> = {
|
||||
[P in keyof T]: T[P] extends bigint
|
||||
? { type: "bigint"; value: bigint }
|
||||
: T[P] extends object
|
||||
? SerializedBigIntsInObject<T[P]>
|
||||
: T[P];
|
||||
};
|
||||
|
||||
type DeserializeBigIntInObject<T> = {
|
||||
[P in keyof T]: T[P] extends { type: "bigint"; value: bigint }
|
||||
? bigint
|
||||
: T[P] extends object
|
||||
? DeserializeBigIntInObject<T[P]>
|
||||
: T[P];
|
||||
};
|
||||
|
||||
export function serializeBigIntsInObject<T extends object>(obj: T): SerializedBigIntsInObject<T> {
|
||||
const result: any = Array.isArray(obj) ? [] : {};
|
||||
for (const key in obj) {
|
||||
const value = obj[key];
|
||||
if (typeof value === "bigint") {
|
||||
result[key] = { type: "bigint", value: String(value) };
|
||||
} else if (value && typeof value === "object") {
|
||||
result[key] = serializeBigIntsInObject(value);
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function deserializeBigIntsInObject<T extends object>(obj: T): DeserializeBigIntInObject<T> {
|
||||
const result: any = Array.isArray(obj) ? [] : {};
|
||||
for (const key in obj) {
|
||||
const value = obj[key];
|
||||
if (
|
||||
typeof value === "object" &&
|
||||
value !== null &&
|
||||
(("type" in value && value.type === "bigint") || ("_type" in value && value._type === "BigNumber"))
|
||||
) {
|
||||
if ("value" in value && typeof value.value === "string") {
|
||||
result[key] = BigInt(value.value);
|
||||
} else if ("hex" in value && typeof value.hex === "string") {
|
||||
if (value.hex.startsWith("-")) {
|
||||
result[key] = BigInt(value.hex.slice(1)) * -1n;
|
||||
} else {
|
||||
result[key] = BigInt(value.hex);
|
||||
}
|
||||
}
|
||||
} else if (value && typeof value === "object") {
|
||||
result[key] = deserializeBigIntsInObject(value);
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function calculateDisplayDecimals(
|
||||
price?: bigint,
|
||||
decimals = USD_DECIMALS,
|
||||
visualMultiplier = 1,
|
||||
isStable = false
|
||||
) {
|
||||
if (price === undefined || price === 0n) return 2;
|
||||
const priceNumber = bigintToNumber(bigMath.abs(price) * BigInt(visualMultiplier), decimals);
|
||||
|
||||
if (isNaN(priceNumber)) return 2;
|
||||
if (isStable) {
|
||||
if (priceNumber >= 0.1) return 2;
|
||||
if (priceNumber >= 0.01) return 3;
|
||||
if (priceNumber >= 0.001) return 4;
|
||||
if (priceNumber >= 0.0001) return 5;
|
||||
if (priceNumber >= 0.00001) return 6;
|
||||
if (priceNumber >= 0.000001) return 7;
|
||||
if (priceNumber >= 0.0000001) return 8;
|
||||
if (priceNumber >= 0.00000001) return 9;
|
||||
} else {
|
||||
if (priceNumber >= 1000) return 2;
|
||||
if (priceNumber >= 100) return 3;
|
||||
if (priceNumber >= 1) return 4;
|
||||
if (priceNumber >= 0.1) return 5;
|
||||
if (priceNumber >= 0.01) return 6;
|
||||
if (priceNumber >= 0.0001) return 7;
|
||||
if (priceNumber >= 0.00001) return 8;
|
||||
}
|
||||
|
||||
return 9;
|
||||
}
|
||||
|
||||
export function clamp(value: number, min: number, max: number): number {
|
||||
return Math.max(min, Math.min(value, max));
|
||||
}
|
||||
|
||||
export function absDiffBps(value: bigint, base: bigint) {
|
||||
if ((value === 0n && base !== 0n) || (value !== 0n && base === 0n)) {
|
||||
return BASIS_POINTS_DIVISOR_BIGINT;
|
||||
}
|
||||
|
||||
if (value === 0n && base === 0n) {
|
||||
return 0n;
|
||||
}
|
||||
|
||||
return bigMath.mulDiv(bigMath.abs(value - base), BASIS_POINTS_DIVISOR_BIGINT, base);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import isPlainObject from "lodash/isPlainObject.js";
|
||||
|
||||
export function setByKey<T>(obj: { [key: string]: T }, key: string, data: T) {
|
||||
return { ...obj, [key]: data };
|
||||
}
|
||||
@@ -19,3 +21,30 @@ export function deleteByKey<T>(obj: { [key: string]: T }, key: string) {
|
||||
delete newObj[key];
|
||||
return newObj;
|
||||
}
|
||||
|
||||
export function objectKeysDeep(obj: Record<string, any>, depth = 1): string[] {
|
||||
const keys = new Set<string>();
|
||||
|
||||
const scanQueue: {
|
||||
obj: Record<string, any>;
|
||||
currentDepth: number;
|
||||
}[] = [{ obj, currentDepth: 0 }];
|
||||
|
||||
while (scanQueue.length > 0) {
|
||||
const { obj, currentDepth } = scanQueue.pop()!;
|
||||
|
||||
if (currentDepth > depth) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const key of Object.keys(obj)) {
|
||||
keys.add(key);
|
||||
|
||||
if (isPlainObject(obj[key])) {
|
||||
scanQueue.push({ obj: obj[key], currentDepth: currentDepth + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(keys);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {bigMath} from "./bigmath.js";
|
||||
import {getByKey} from "./objects.js";
|
||||
import {parsePositionKey} from "./positions.js";
|
||||
import {getOrderThresholdType} from "./prices.js";
|
||||
import { getSwapPathOutputAddresses, getSwapPathStats } from "./swap/index.js";
|
||||
import {getSwapPathOutputAddresses, getSwapPathStats} from "./swap/index.js";
|
||||
import {
|
||||
convertToTokenAmount,
|
||||
convertToUsd,
|
||||
@@ -100,6 +100,7 @@ export function getOrderInfo(p: {
|
||||
)!,
|
||||
shouldUnwrapNativeToken: order.shouldUnwrapNativeToken,
|
||||
shouldApplyPriceImpact: true,
|
||||
isAtomicSwap: false,
|
||||
});
|
||||
|
||||
const priceImpactAmount = convertToTokenAmount(
|
||||
@@ -114,7 +115,7 @@ export function getOrderInfo(p: {
|
||||
targetCollateralToken.prices.minPrice
|
||||
);
|
||||
|
||||
let toAmount;
|
||||
let toAmount = order.minOutputAmount;
|
||||
let triggerRatio;
|
||||
|
||||
const isLimitSwapOrder = isLimitSwapOrderType(order.orderType);
|
||||
@@ -153,6 +154,8 @@ export function getOrderInfo(p: {
|
||||
triggerRatio,
|
||||
initialCollateralToken,
|
||||
targetCollateralToken,
|
||||
isSwap: true,
|
||||
isTwap: false,
|
||||
};
|
||||
|
||||
return orderInfo;
|
||||
@@ -191,9 +194,13 @@ export function getOrderInfo(p: {
|
||||
)!,
|
||||
shouldUnwrapNativeToken: order.shouldUnwrapNativeToken,
|
||||
shouldApplyPriceImpact: true,
|
||||
isAtomicSwap: false,
|
||||
});
|
||||
|
||||
const triggerThresholdType = getOrderThresholdType(order.orderType, order.isLong);
|
||||
let triggerThresholdType;
|
||||
if (!isMarketOrderType(order.orderType)) {
|
||||
triggerThresholdType = getOrderThresholdType(order.orderType, order.isLong);
|
||||
}
|
||||
|
||||
const orderInfo: PositionOrderInfo = {
|
||||
...order,
|
||||
@@ -205,6 +212,8 @@ export function getOrderInfo(p: {
|
||||
acceptablePrice,
|
||||
triggerPrice,
|
||||
triggerThresholdType,
|
||||
isSwap: false,
|
||||
isTwap: false,
|
||||
};
|
||||
|
||||
return orderInfo;
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import {bigMath} from "./bigmath.js";
|
||||
import {applyFactor, expandDecimals} from "./numbers.js";
|
||||
import {getCappedPoolPnl, getMarketPnl, getPoolUsdWithoutPnl} from "./markets.js";
|
||||
import {applyFactor, expandDecimals, PRECISION} from "./numbers.js";
|
||||
import {getCappedPoolPnl, getMarketPnl, getOpenInterestUsd, getPoolUsdWithoutPnl} from "./markets.js";
|
||||
import {convertToUsd, getIsEquivalentTokens} from "./tokens.js";
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT} from "../configs/factors.js";
|
||||
import {Token, TokenData} from "../types/tokens.js";
|
||||
import {UserReferralInfo} from "../types/referrals.js";
|
||||
import {MarketInfo} from "../types/markets.js";
|
||||
import {getPriceImpactForPosition} from "./fees/priceImpact.js";
|
||||
import {
|
||||
capPositionImpactUsdByMaxImpactPool,
|
||||
capPositionImpactUsdByMaxPriceImpactFactor,
|
||||
getMaxPositionImpactFactors,
|
||||
getPriceImpactForPosition,
|
||||
getProportionalPendingImpactValues
|
||||
} from "./fees/priceImpact.js";
|
||||
import {getPositionFee} from "./fees/index.js";
|
||||
import {PositionInfoLoaded} from "../types/positions.js";
|
||||
|
||||
export function getPositionKey(account: string, marketAddress: string, collateralAddress: string, isLong: boolean) {
|
||||
return `${account}:${marketAddress}:${collateralAddress}:${isLong}`;
|
||||
@@ -78,6 +85,8 @@ export function getPositionPendingFeesUsd(p: { pendingFundingFeesUsd: bigint; pe
|
||||
}
|
||||
|
||||
export function getPositionNetValue(p: {
|
||||
totalPendingImpactDeltaUsd: bigint;
|
||||
priceImpactDiffUsd: bigint;
|
||||
collateralUsd: bigint;
|
||||
pendingFundingFeesUsd: bigint;
|
||||
pendingBorrowingFeesUsd: bigint;
|
||||
@@ -85,11 +94,42 @@ export function getPositionNetValue(p: {
|
||||
closingFeeUsd: bigint;
|
||||
uiFeeUsd: bigint;
|
||||
}) {
|
||||
const { pnl, closingFeeUsd, collateralUsd, uiFeeUsd } = p;
|
||||
const { pnl, closingFeeUsd, collateralUsd, uiFeeUsd, totalPendingImpactDeltaUsd, priceImpactDiffUsd } = p;
|
||||
|
||||
const pendingFeesUsd = getPositionPendingFeesUsd(p);
|
||||
|
||||
return collateralUsd - pendingFeesUsd - closingFeeUsd - uiFeeUsd + pnl;
|
||||
return (
|
||||
collateralUsd - pendingFeesUsd - closingFeeUsd - uiFeeUsd + pnl + totalPendingImpactDeltaUsd + priceImpactDiffUsd
|
||||
);
|
||||
}
|
||||
|
||||
export function getPositionPnlAfterFees({
|
||||
pnl,
|
||||
pendingBorrowingFeesUsd,
|
||||
pendingFundingFeesUsd,
|
||||
closingFeeUsd,
|
||||
uiFeeUsd,
|
||||
totalPendingImpactDeltaUsd,
|
||||
priceImpactDiffUsd,
|
||||
}: {
|
||||
pnl: bigint;
|
||||
pendingBorrowingFeesUsd: bigint;
|
||||
pendingFundingFeesUsd: bigint;
|
||||
closingFeeUsd: bigint;
|
||||
uiFeeUsd: bigint;
|
||||
totalPendingImpactDeltaUsd: bigint;
|
||||
priceImpactDiffUsd: bigint;
|
||||
}) {
|
||||
const pnlAfterFees =
|
||||
pnl -
|
||||
pendingBorrowingFeesUsd -
|
||||
pendingFundingFeesUsd -
|
||||
closingFeeUsd -
|
||||
uiFeeUsd +
|
||||
totalPendingImpactDeltaUsd +
|
||||
priceImpactDiffUsd;
|
||||
|
||||
return pnlAfterFees;
|
||||
}
|
||||
|
||||
export function getLeverage(p: {
|
||||
@@ -121,6 +161,7 @@ export function getLiquidationPrice(p: {
|
||||
marketInfo: MarketInfo;
|
||||
pendingFundingFeesUsd: bigint;
|
||||
pendingBorrowingFeesUsd: bigint;
|
||||
pendingImpactAmount: bigint;
|
||||
minCollateralUsd: bigint;
|
||||
isLong: boolean;
|
||||
useMaxPriceImpact?: boolean;
|
||||
@@ -135,6 +176,7 @@ export function getLiquidationPrice(p: {
|
||||
collateralToken,
|
||||
pendingFundingFeesUsd,
|
||||
pendingBorrowingFeesUsd,
|
||||
pendingImpactAmount,
|
||||
minCollateralUsd,
|
||||
isLong,
|
||||
userReferralInfo,
|
||||
@@ -158,19 +200,29 @@ export function getLiquidationPrice(p: {
|
||||
if (useMaxPriceImpact) {
|
||||
priceImpactDeltaUsd = maxNegativePriceImpactUsd;
|
||||
} else {
|
||||
priceImpactDeltaUsd = getPriceImpactForPosition(marketInfo, -sizeInUsd, isLong, { fallbackToZero: true });
|
||||
const priceImpactForPosition = getPriceImpactForPosition(marketInfo, -sizeInUsd, isLong, { fallbackToZero: true });
|
||||
priceImpactDeltaUsd = priceImpactForPosition.priceImpactDeltaUsd;
|
||||
|
||||
if (priceImpactDeltaUsd < maxNegativePriceImpactUsd) {
|
||||
priceImpactDeltaUsd = maxNegativePriceImpactUsd;
|
||||
if (priceImpactDeltaUsd > 0) {
|
||||
priceImpactDeltaUsd = capPositionImpactUsdByMaxPriceImpactFactor(marketInfo, sizeInUsd, priceImpactDeltaUsd);
|
||||
}
|
||||
|
||||
// Ignore positive price impact
|
||||
const pendingImpactUsd = convertToUsd(
|
||||
pendingImpactAmount,
|
||||
marketInfo.indexToken.decimals,
|
||||
pendingImpactAmount > 0 ? marketInfo.indexToken.prices.minPrice : marketInfo.indexToken.prices.maxPrice
|
||||
)!;
|
||||
|
||||
priceImpactDeltaUsd = priceImpactDeltaUsd + pendingImpactUsd;
|
||||
|
||||
if (priceImpactDeltaUsd > 0) {
|
||||
priceImpactDeltaUsd = 0n;
|
||||
} else if (priceImpactDeltaUsd < maxNegativePriceImpactUsd) {
|
||||
priceImpactDeltaUsd = maxNegativePriceImpactUsd;
|
||||
}
|
||||
}
|
||||
|
||||
let liquidationCollateralUsd = applyFactor(sizeInUsd, marketInfo.minCollateralFactor);
|
||||
let liquidationCollateralUsd = applyFactor(sizeInUsd, marketInfo.minCollateralFactorForLiquidation);
|
||||
if (liquidationCollateralUsd < minCollateralUsd) {
|
||||
liquidationCollateralUsd = minCollateralUsd;
|
||||
}
|
||||
@@ -223,3 +275,88 @@ export function getLiquidationPrice(p: {
|
||||
|
||||
return liquidationPrice;
|
||||
}
|
||||
|
||||
export function getNetPriceImpactDeltaUsdForDecrease({
|
||||
marketInfo,
|
||||
sizeInUsd,
|
||||
pendingImpactAmount,
|
||||
priceImpactDeltaUsd,
|
||||
sizeDeltaUsd,
|
||||
}: {
|
||||
marketInfo: MarketInfo;
|
||||
sizeInUsd: bigint;
|
||||
pendingImpactAmount: bigint;
|
||||
sizeDeltaUsd: bigint;
|
||||
priceImpactDeltaUsd: bigint;
|
||||
}) {
|
||||
const { proportionalPendingImpactDeltaUsd } = getProportionalPendingImpactValues({
|
||||
sizeInUsd,
|
||||
sizeDeltaUsd,
|
||||
pendingImpactAmount,
|
||||
indexToken: marketInfo.indexToken,
|
||||
});
|
||||
|
||||
let totalImpactDeltaUsd = priceImpactDeltaUsd + proportionalPendingImpactDeltaUsd;
|
||||
|
||||
const priceImpactDiffUsd = getPriceImpactDiffUsd({
|
||||
totalImpactDeltaUsd,
|
||||
marketInfo,
|
||||
sizeDeltaUsd,
|
||||
});
|
||||
|
||||
if (totalImpactDeltaUsd > 0) {
|
||||
totalImpactDeltaUsd = capPositionImpactUsdByMaxPriceImpactFactor(marketInfo, sizeDeltaUsd, totalImpactDeltaUsd);
|
||||
}
|
||||
|
||||
totalImpactDeltaUsd = capPositionImpactUsdByMaxImpactPool(marketInfo, totalImpactDeltaUsd);
|
||||
|
||||
return {
|
||||
totalImpactDeltaUsd,
|
||||
proportionalPendingImpactDeltaUsd,
|
||||
priceImpactDiffUsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function getPriceImpactDiffUsd({
|
||||
totalImpactDeltaUsd,
|
||||
marketInfo,
|
||||
sizeDeltaUsd,
|
||||
}: {
|
||||
totalImpactDeltaUsd: bigint;
|
||||
marketInfo: MarketInfo;
|
||||
sizeDeltaUsd: bigint;
|
||||
}) {
|
||||
if (totalImpactDeltaUsd > 0) {
|
||||
return 0n;
|
||||
}
|
||||
|
||||
const { maxNegativeImpactFactor } = getMaxPositionImpactFactors(marketInfo);
|
||||
|
||||
const maxNegativeImpactUsd = -applyFactor(sizeDeltaUsd, maxNegativeImpactFactor);
|
||||
|
||||
let priceImpactDiffUsd = 0n;
|
||||
|
||||
if (totalImpactDeltaUsd < maxNegativeImpactUsd) {
|
||||
priceImpactDiffUsd = maxNegativeImpactUsd - totalImpactDeltaUsd;
|
||||
}
|
||||
|
||||
return priceImpactDiffUsd;
|
||||
}
|
||||
|
||||
export function getMinCollateralFactorForPosition(position: PositionInfoLoaded, openInterestDelta: bigint) {
|
||||
const marketInfo = position.marketInfo;
|
||||
|
||||
const isLong = position.isLong;
|
||||
const openInterest = getOpenInterestUsd(marketInfo, isLong) + openInterestDelta;
|
||||
const minCollateralFactorMultiplier = isLong
|
||||
? marketInfo.minCollateralFactorForOpenInterestLong
|
||||
: marketInfo.minCollateralFactorForOpenInterestShort;
|
||||
let minCollateralFactor = bigMath.mulDiv(openInterest, minCollateralFactorMultiplier, PRECISION);
|
||||
const minCollateralFactorForMarket = marketInfo.minCollateralFactor;
|
||||
|
||||
if (minCollateralFactorForMarket > minCollateralFactor) {
|
||||
minCollateralFactor = minCollateralFactorForMarket;
|
||||
}
|
||||
|
||||
return minCollateralFactor;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { BASIS_POINTS_DIVISOR_BIGINT, DEFAULT_ACCEPTABLE_PRICE_IMPACT_BUFFER } from "../configs/factors.js";
|
||||
import { MarketInfo } from "../types/markets.js";
|
||||
import { OrderType } from "../types/orders.js";
|
||||
import { TokenPrices } from "../types/tokens.js";
|
||||
import { getPriceImpactByAcceptablePrice } from "./fees/index.js";
|
||||
import { bigMath } from "./bigmath.js";
|
||||
import { getCappedPositionImpactUsd } from "./fees/index.js";
|
||||
import { convertToTokenAmount } from "./tokens.js";
|
||||
import { expandDecimals, getBasisPoints } from "./numbers.js";
|
||||
import { roundUpMagnitudeDivision } from "./numbers.js";
|
||||
import { applyFactor } from "./numbers.js";
|
||||
import { TriggerThresholdType } from "../types/trade.js";
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT, DEFAULT_ACCEPTABLE_PRICE_IMPACT_BUFFER} from "../configs/factors.js";
|
||||
import {MarketInfo} from "../types/markets.js";
|
||||
import {OrderType} from "../types/orders.js";
|
||||
import {TokenPrices} from "../types/tokens.js";
|
||||
import {getCappedPositionImpactUsd, getPriceImpactByAcceptablePrice} from "./fees/index.js";
|
||||
import {bigMath} from "./bigmath.js";
|
||||
import {convertToTokenAmount} from "./tokens.js";
|
||||
import {expandDecimals, getBasisPoints, roundUpMagnitudeDivision} from "./numbers.js";
|
||||
import {TriggerThresholdType} from "../types/trade.js";
|
||||
|
||||
export function getMarkPrice(p: { prices: TokenPrices; isIncrease: boolean; isLong: boolean }) {
|
||||
const { prices, isIncrease, isLong } = p;
|
||||
@@ -44,12 +41,13 @@ export function getOrderThresholdType(orderType: OrderType, isLong: boolean) {
|
||||
return isLong ? TriggerThresholdType.Below : TriggerThresholdType.Above;
|
||||
}
|
||||
|
||||
throw new Error("Invalid trigger order type");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getAcceptablePriceInfo(p: {
|
||||
marketInfo: MarketInfo;
|
||||
isIncrease: boolean;
|
||||
isLimit: boolean;
|
||||
isLong: boolean;
|
||||
indexPrice: bigint;
|
||||
sizeDeltaUsd: bigint;
|
||||
@@ -61,9 +59,12 @@ export function getAcceptablePriceInfo(p: {
|
||||
const values = {
|
||||
acceptablePrice: 0n,
|
||||
acceptablePriceDeltaBps: 0n,
|
||||
cappedPriceImpactDeltaUsd: 0n,
|
||||
cappedPriceImpactDeltaAmount: 0n,
|
||||
priceImpactDeltaAmount: 0n,
|
||||
priceImpactDeltaUsd: 0n,
|
||||
priceImpactDiffUsd: 0n,
|
||||
balanceWasImproved: false,
|
||||
};
|
||||
|
||||
if (sizeDeltaUsd <= 0 || indexPrice == 0n) {
|
||||
@@ -94,23 +95,24 @@ export function getAcceptablePriceInfo(p: {
|
||||
return values;
|
||||
}
|
||||
|
||||
values.priceImpactDeltaUsd = getCappedPositionImpactUsd(
|
||||
const { priceImpactDeltaUsd, balanceWasImproved } = getCappedPositionImpactUsd(
|
||||
marketInfo,
|
||||
isIncrease ? sizeDeltaUsd : sizeDeltaUsd * -1n,
|
||||
sizeDeltaUsd,
|
||||
isLong,
|
||||
isIncrease,
|
||||
{
|
||||
fallbackToZero: !isIncrease,
|
||||
shouldCapNegativeImpact: false,
|
||||
}
|
||||
);
|
||||
|
||||
if (!isIncrease && values.priceImpactDeltaUsd < 0) {
|
||||
const minPriceImpactUsd = applyFactor(sizeDeltaUsd, marketInfo.maxPositionImpactFactorNegative) * -1n;
|
||||
|
||||
if (values.priceImpactDeltaUsd < minPriceImpactUsd) {
|
||||
values.priceImpactDiffUsd = minPriceImpactUsd - values.priceImpactDeltaUsd;
|
||||
values.priceImpactDeltaUsd = minPriceImpactUsd;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* We display this value as price impact on action (increase or decrease)
|
||||
* But for acceptable price calculation uncapped price impact is used
|
||||
* Also on decrease action we calculate totalImpactUsd which will be deducted from the collateral
|
||||
*/
|
||||
values.priceImpactDeltaUsd = priceImpactDeltaUsd;
|
||||
values.balanceWasImproved = balanceWasImproved;
|
||||
|
||||
if (values.priceImpactDeltaUsd > 0) {
|
||||
values.priceImpactDeltaAmount = convertToTokenAmount(
|
||||
@@ -125,12 +127,24 @@ export function getAcceptablePriceInfo(p: {
|
||||
);
|
||||
}
|
||||
|
||||
// Use uncapped price impact for the acceptable price calculation
|
||||
const { priceImpactDeltaUsd: priceImpactDeltaUsdForAcceptablePrice } = getCappedPositionImpactUsd(
|
||||
marketInfo,
|
||||
sizeDeltaUsd,
|
||||
isLong,
|
||||
isIncrease,
|
||||
{
|
||||
fallbackToZero: !isIncrease,
|
||||
shouldCapNegativeImpact: false,
|
||||
}
|
||||
);
|
||||
|
||||
const acceptablePriceValues = getAcceptablePriceByPriceImpact({
|
||||
isIncrease,
|
||||
isLong,
|
||||
indexPrice,
|
||||
sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: values.priceImpactDeltaUsd,
|
||||
priceImpactDeltaUsd: priceImpactDeltaUsdForAcceptablePrice,
|
||||
});
|
||||
|
||||
values.acceptablePrice = acceptablePriceValues.acceptablePrice;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Abi, Address, decodeErrorResult, encodeFunctionData, withRetry } from "viem";
|
||||
import {Abi, Address, decodeErrorResult, encodeFunctionData, withRetry} from "viem";
|
||||
|
||||
|
||||
import { getContract } from "../configs/contracts.js";
|
||||
import { convertTokenAddress } from "../configs/tokens.js";
|
||||
import {getContract} from "../configs/contracts.js";
|
||||
import {convertTokenAddress} from "../configs/tokens.js";
|
||||
|
||||
import { SwapPricingType } from "../types/orders.js";
|
||||
import { TokenPrices, TokensData } from "../types/tokens.js";
|
||||
import { convertToContractPrice, getTokenData } from "./tokens.js";
|
||||
import {SwapPricingType} from "../types/orders.js";
|
||||
import {TokenPrices, TokensData} from "../types/tokens.js";
|
||||
import {convertToContractPrice, getTokenData} from "./tokens.js";
|
||||
|
||||
import type { GmxSdk } from "../index.js";
|
||||
import { extractError } from "./contracts.js";
|
||||
import { abis } from "../abis/index.js";
|
||||
import type {GmxSdk} from "../index.js";
|
||||
import {extractError} from "./contracts.js";
|
||||
import {abis} from "../abis/index.js";
|
||||
|
||||
export type PriceOverrides = {
|
||||
[address: string]: TokenPrices | undefined;
|
||||
@@ -42,6 +42,7 @@ export async function simulateExecuteOrder(sdk: GmxSdk, p: SimulateExecuteParams
|
||||
abi: abis.Multicall as Abi,
|
||||
functionName: "getCurrentBlockTimestamp",
|
||||
args: [],
|
||||
authorizationList: [],
|
||||
});
|
||||
|
||||
const blockNumber = await client.getBlockNumber();
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import {encodeFunctionData} from "viem";
|
||||
|
||||
import StBTCABI from "../../abis/StBTC.json" with {type: "json"};
|
||||
import {BOTANIX} from "../../configs/chains.js";
|
||||
import {getContract} from "../../configs/contracts.js";
|
||||
import {TokensData} from "../../types/tokens.js";
|
||||
import {ExternalSwapAggregator, ExternalSwapQuote} from "../../types/trade.js";
|
||||
import {bigMath} from "../bigmath.js";
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT, getBasisPoints} from "../numbers.js";
|
||||
import {AVAILABLE_BOTANIX_DEPOSIT_PAIRS, AVAILABLE_BOTANIX_WITHDRAW_PAIRS} from "./externalSwapPath.js";
|
||||
import {convertToUsd, getMidPrice, getTokenData} from "../tokens.js";
|
||||
|
||||
const COEF_REDUCER = getBasisPoints(1n, 10000n);
|
||||
|
||||
export const getBotanixStakingExternalSwapQuote = ({
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
amountIn,
|
||||
gasPrice,
|
||||
receiverAddress,
|
||||
tokensData,
|
||||
assetsPerShare,
|
||||
}: {
|
||||
tokenInAddress: string;
|
||||
tokenOutAddress: string;
|
||||
amountIn: bigint;
|
||||
gasPrice: bigint;
|
||||
receiverAddress: string;
|
||||
tokensData: TokensData;
|
||||
assetsPerShare: bigint;
|
||||
}): ExternalSwapQuote | undefined => {
|
||||
const inTokenData = getTokenData(tokensData, tokenInAddress);
|
||||
const outTokenData = getTokenData(tokensData, tokenOutAddress);
|
||||
|
||||
const assetsPerShareRate = getBasisPoints(assetsPerShare, 10n ** 18n) - COEF_REDUCER;
|
||||
const sharesPerAssetRate = getBasisPoints(10n ** 18n, assetsPerShare) - COEF_REDUCER;
|
||||
|
||||
if (!inTokenData || !outTokenData) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (AVAILABLE_BOTANIX_DEPOSIT_PAIRS.some((pair) => pair.from === tokenInAddress && pair.to === tokenOutAddress)) {
|
||||
const priceIn = getMidPrice(inTokenData.prices);
|
||||
const priceOut = bigMath.mulDiv(priceIn, sharesPerAssetRate, BASIS_POINTS_DIVISOR_BIGINT);
|
||||
const usdIn = convertToUsd(amountIn, inTokenData.decimals, priceIn);
|
||||
const amountOut =
|
||||
amountIn > 0n ? bigMath.mulDiv(amountIn, sharesPerAssetRate, BASIS_POINTS_DIVISOR_BIGINT) - gasPrice : 0n;
|
||||
const usdOut = amountOut > 0n ? convertToUsd(amountOut, outTokenData.decimals, priceOut) : 0n;
|
||||
return {
|
||||
aggregator: ExternalSwapAggregator.BotanixStaking,
|
||||
inTokenAddress: tokenInAddress,
|
||||
outTokenAddress: tokenOutAddress,
|
||||
receiver: receiverAddress,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn: usdIn!,
|
||||
usdOut: usdOut!,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: gasPrice,
|
||||
needSpenderApproval: true,
|
||||
txnData: {
|
||||
to: getContract(BOTANIX, "StBTC"),
|
||||
data: encodeFunctionData({
|
||||
abi: StBTCABI.abi,
|
||||
functionName: "deposit",
|
||||
args: [amountIn, receiverAddress],
|
||||
}),
|
||||
value: 0n,
|
||||
estimatedGas: gasPrice,
|
||||
estimatedExecutionFee: gasPrice,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (AVAILABLE_BOTANIX_WITHDRAW_PAIRS.some((pair) => pair.from === tokenInAddress && pair.to === tokenOutAddress)) {
|
||||
const priceIn = getMidPrice(inTokenData.prices);
|
||||
const priceOut = bigMath.mulDiv(priceIn, assetsPerShareRate, BASIS_POINTS_DIVISOR_BIGINT);
|
||||
const usdIn = convertToUsd(amountIn, inTokenData.decimals, priceIn);
|
||||
const amountOut =
|
||||
amountIn > 0n ? bigMath.mulDiv(amountIn, assetsPerShareRate, BASIS_POINTS_DIVISOR_BIGINT) - gasPrice : 0n;
|
||||
const usdOut = amountOut > 0n ? convertToUsd(amountOut, outTokenData.decimals, priceOut) : 0n;
|
||||
|
||||
return {
|
||||
aggregator: ExternalSwapAggregator.BotanixStaking,
|
||||
inTokenAddress: tokenInAddress,
|
||||
outTokenAddress: tokenOutAddress,
|
||||
receiver: receiverAddress,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn: usdIn!,
|
||||
usdOut: usdOut!,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: gasPrice,
|
||||
needSpenderApproval: true,
|
||||
txnData: {
|
||||
to: getContract(BOTANIX, "StBTC"),
|
||||
data: encodeFunctionData({
|
||||
abi: StBTCABI.abi,
|
||||
functionName: "withdraw",
|
||||
args: [amountIn, receiverAddress, getContract(BOTANIX, "ExternalHandler")],
|
||||
}),
|
||||
value: 0n,
|
||||
estimatedGas: gasPrice,
|
||||
estimatedExecutionFee: gasPrice,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import type {MarketConfig} from "../../configs/markets.js";
|
||||
|
||||
type FromTokenAddress = string;
|
||||
type ToTokenAddress = string;
|
||||
type MarketAddress = string;
|
||||
|
||||
export type MarketsGraph = {
|
||||
[from: FromTokenAddress]: {
|
||||
[to: ToTokenAddress]: MarketAddress[];
|
||||
};
|
||||
};
|
||||
|
||||
export function buildMarketsAdjacencyGraph(marketsMap: Record<string, MarketConfig>): MarketsGraph {
|
||||
const graph: MarketsGraph = {};
|
||||
|
||||
for (const marketTokenAddress in marketsMap) {
|
||||
const { longTokenAddress, shortTokenAddress } = marketsMap[marketTokenAddress];
|
||||
|
||||
const isSameCollaterals = longTokenAddress === shortTokenAddress;
|
||||
|
||||
if (isSameCollaterals) {
|
||||
const tokenAddress = longTokenAddress;
|
||||
|
||||
graph[tokenAddress] = graph[tokenAddress] || {};
|
||||
graph[tokenAddress][tokenAddress] = graph[tokenAddress][tokenAddress] || [];
|
||||
graph[tokenAddress][tokenAddress].push(marketTokenAddress);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
graph[longTokenAddress] = graph[longTokenAddress] || {};
|
||||
graph[longTokenAddress][shortTokenAddress] = graph[longTokenAddress][shortTokenAddress] || [];
|
||||
graph[longTokenAddress][shortTokenAddress].push(marketTokenAddress);
|
||||
|
||||
graph[shortTokenAddress] = graph[shortTokenAddress] || {};
|
||||
graph[shortTokenAddress][longTokenAddress] = graph[shortTokenAddress][longTokenAddress] || [];
|
||||
graph[shortTokenAddress][longTokenAddress].push(marketTokenAddress);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
import {MarketsInfoData} from "../../types/markets.js";
|
||||
import {SwapStrategyForSwapOrders} from "../../types/swapStrategy.js";
|
||||
import {TokenData} from "../../types/tokens.js";
|
||||
import {ExternalSwapQuoteParams, SwapOptimizationOrderArray} from "../../types/trade.js";
|
||||
import {bigMath} from "../../utils/bigmath.js";
|
||||
import {
|
||||
convertToTokenAmount,
|
||||
convertToUsd,
|
||||
getIsEquivalentTokens,
|
||||
getIsStake,
|
||||
getIsUnstake,
|
||||
getMidPrice,
|
||||
} from "../../utils/tokens.js";
|
||||
|
||||
import {getAvailableExternalSwapPaths} from "./externalSwapPath.js";
|
||||
import {getExternalSwapQuoteByPath} from "./externalSwapQuoteByPath.js";
|
||||
import {createFindSwapPath} from "./swapPath.js";
|
||||
|
||||
/*
|
||||
Order/Priority of getting swap strategy:
|
||||
1. Check if it needs a swap and return noSwap if tokens are equivalent, stake or unstake [noSwap]
|
||||
2. Check if there is a swap path stats for the internal swap quote and return internalSwap if there is [internalSwap]
|
||||
3. Check if there is a combined swap strategy and return combinedSwap if there is [combinedSwap]
|
||||
4. Return defaultSwapStrategy (noSwap) if there is no other swap strategy [noSwap]
|
||||
*/
|
||||
|
||||
export function buildSwapStrategy({
|
||||
amountIn,
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
swapOptimizationOrder,
|
||||
externalSwapQuoteParams,
|
||||
}: {
|
||||
chainId: number;
|
||||
amountIn: bigint;
|
||||
tokenIn: TokenData;
|
||||
tokenOut: TokenData;
|
||||
marketsInfoData: MarketsInfoData | undefined;
|
||||
swapOptimizationOrder: SwapOptimizationOrderArray | undefined;
|
||||
externalSwapQuoteParams: ExternalSwapQuoteParams;
|
||||
}): SwapStrategyForSwapOrders {
|
||||
const priceIn = tokenIn.prices.minPrice;
|
||||
const usdIn = convertToUsd(amountIn, tokenIn.decimals, priceIn)!;
|
||||
|
||||
if (amountIn < 0n) {
|
||||
amountIn = 0n;
|
||||
}
|
||||
|
||||
const defaultSwapStrategy: SwapStrategyForSwapOrders = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn,
|
||||
amountOut: convertToTokenAmount(usdIn, tokenOut.decimals, tokenOut.prices.maxPrice)!,
|
||||
usdIn,
|
||||
usdOut: usdIn,
|
||||
priceIn,
|
||||
priceOut: tokenOut.prices.maxPrice,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
if (getIsEquivalentTokens(tokenIn, tokenOut) || getIsStake(tokenIn, tokenOut) || getIsUnstake(tokenIn, tokenOut)) {
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
|
||||
const findSwapPath = createFindSwapPath({
|
||||
chainId,
|
||||
fromTokenAddress: tokenIn.address,
|
||||
toTokenAddress: tokenOut.address,
|
||||
marketsInfoData,
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const swapPathStats = findSwapPath(usdIn, { order: swapOptimizationOrder });
|
||||
|
||||
if (swapPathStats) {
|
||||
return {
|
||||
type: "internalSwap",
|
||||
swapPathStats,
|
||||
externalSwapQuote: undefined,
|
||||
amountIn,
|
||||
amountOut: swapPathStats.amountOut,
|
||||
usdIn: usdIn,
|
||||
usdOut: swapPathStats.usdOut,
|
||||
priceIn: priceIn,
|
||||
priceOut: tokenOut.prices.maxPrice,
|
||||
feesUsd: usdIn - swapPathStats.usdOut,
|
||||
};
|
||||
}
|
||||
|
||||
const availableExternalSwapPaths = getAvailableExternalSwapPaths({ chainId, fromTokenAddress: tokenIn.address });
|
||||
|
||||
const suitableSwapPath = availableExternalSwapPaths.find((path) => {
|
||||
const findSwapPath = createFindSwapPath({
|
||||
chainId,
|
||||
fromTokenAddress: path.outTokenAddress,
|
||||
toTokenAddress: tokenOut.address,
|
||||
marketsInfoData,
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const swapPathStats = findSwapPath(usdIn);
|
||||
|
||||
return Boolean(swapPathStats);
|
||||
});
|
||||
|
||||
if (suitableSwapPath && suitableSwapPath.outTokenAddress !== tokenOut.address) {
|
||||
const externalSwapQuoteForCombinedSwap = getExternalSwapQuoteByPath({
|
||||
amountIn,
|
||||
externalSwapPath: suitableSwapPath,
|
||||
externalSwapQuoteParams,
|
||||
});
|
||||
const findSwapPathForSuitableSwapPath = createFindSwapPath({
|
||||
chainId,
|
||||
fromTokenAddress: suitableSwapPath.outTokenAddress,
|
||||
toTokenAddress: tokenOut.address,
|
||||
marketsInfoData,
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const swapPathStatsForCombinedSwap = externalSwapQuoteForCombinedSwap
|
||||
? findSwapPathForSuitableSwapPath(externalSwapQuoteForCombinedSwap.usdOut)
|
||||
: undefined;
|
||||
|
||||
return externalSwapQuoteForCombinedSwap && swapPathStatsForCombinedSwap
|
||||
? {
|
||||
type: "combinedSwap",
|
||||
externalSwapQuote: externalSwapQuoteForCombinedSwap,
|
||||
swapPathStats: swapPathStatsForCombinedSwap,
|
||||
amountIn,
|
||||
amountOut: swapPathStatsForCombinedSwap.amountOut,
|
||||
usdIn: externalSwapQuoteForCombinedSwap.usdIn,
|
||||
usdOut: swapPathStatsForCombinedSwap.usdOut,
|
||||
priceIn: externalSwapQuoteForCombinedSwap.priceIn,
|
||||
priceOut: tokenOut.prices.maxPrice,
|
||||
feesUsd: BigInt(externalSwapQuoteForCombinedSwap.usdIn - swapPathStatsForCombinedSwap.usdOut),
|
||||
}
|
||||
: defaultSwapStrategy;
|
||||
}
|
||||
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
|
||||
// Used for getting swap amounts by to value
|
||||
export function buildReverseSwapStrategy({
|
||||
amountOut,
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
swapOptimizationOrder,
|
||||
}: {
|
||||
chainId: number;
|
||||
amountOut: bigint;
|
||||
tokenIn: TokenData;
|
||||
tokenOut: TokenData;
|
||||
marketsInfoData: MarketsInfoData | undefined;
|
||||
externalSwapQuoteParams: ExternalSwapQuoteParams;
|
||||
swapOptimizationOrder: SwapOptimizationOrderArray | undefined;
|
||||
}): SwapStrategyForSwapOrders {
|
||||
const priceIn = getMidPrice(tokenIn.prices);
|
||||
const priceOut = getMidPrice(tokenOut.prices);
|
||||
|
||||
const preferredUsdOut = convertToUsd(amountOut, tokenOut.decimals, getMidPrice(tokenOut.prices))!;
|
||||
const approximateAmountIn = convertToTokenAmount(preferredUsdOut, tokenIn.decimals, getMidPrice(tokenIn.prices))!;
|
||||
const approximateUsdIn = preferredUsdOut;
|
||||
|
||||
const defaultSwapStrategy: SwapStrategyForSwapOrders = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn: approximateAmountIn,
|
||||
amountOut: amountOut,
|
||||
usdIn: approximateUsdIn,
|
||||
usdOut: preferredUsdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
if (getIsEquivalentTokens(tokenIn, tokenOut) || getIsStake(tokenIn, tokenOut) || getIsUnstake(tokenIn, tokenOut)) {
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
|
||||
const findSwapPath = createFindSwapPath({
|
||||
chainId,
|
||||
fromTokenAddress: tokenIn.address,
|
||||
toTokenAddress: tokenOut.address,
|
||||
marketsInfoData,
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const approximateSwapPathStats = findSwapPath(approximateUsdIn, { order: swapOptimizationOrder });
|
||||
|
||||
if (approximateSwapPathStats) {
|
||||
// Increase or decrease usdIn the same way preferred usdOut is different from swapStrategy.usdOut
|
||||
// preferred_in / approximate_in = preferred_out / approximate_out
|
||||
// preferred_in = approximate_in * preferred_out / approximate_out
|
||||
const adjustedUsdIn =
|
||||
approximateSwapPathStats.usdOut > 0
|
||||
? bigMath.mulDiv(approximateUsdIn, preferredUsdOut, approximateSwapPathStats.usdOut)
|
||||
: 0n;
|
||||
const adjustedAmountIn = convertToTokenAmount(adjustedUsdIn, tokenIn.decimals, getMidPrice(tokenIn.prices))!;
|
||||
|
||||
const adjustedSwapPathStats = findSwapPath(adjustedUsdIn, { order: swapOptimizationOrder });
|
||||
|
||||
if (adjustedSwapPathStats) {
|
||||
return {
|
||||
type: "internalSwap",
|
||||
swapPathStats: adjustedSwapPathStats,
|
||||
externalSwapQuote: undefined,
|
||||
amountIn: adjustedAmountIn,
|
||||
amountOut: adjustedSwapPathStats.amountOut,
|
||||
usdIn: adjustedUsdIn,
|
||||
usdOut: adjustedSwapPathStats.usdOut,
|
||||
priceIn: priceIn,
|
||||
priceOut: priceOut,
|
||||
feesUsd: adjustedUsdIn - adjustedSwapPathStats.usdOut,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const availableExternalSwapPaths = getAvailableExternalSwapPaths({ chainId, fromTokenAddress: tokenIn.address });
|
||||
|
||||
const suitableSwapPath = availableExternalSwapPaths.find((path) => {
|
||||
if (path.outTokenAddress !== tokenOut.address) return false;
|
||||
|
||||
const findSwapPath = createFindSwapPath({
|
||||
chainId,
|
||||
fromTokenAddress: tokenIn.address,
|
||||
toTokenAddress: path.inTokenAddress,
|
||||
marketsInfoData,
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const swapPathStats = findSwapPath(approximateUsdIn);
|
||||
|
||||
return Boolean(swapPathStats);
|
||||
});
|
||||
|
||||
if (suitableSwapPath) {
|
||||
const approximateExternalSwapQuoteForCombinedSwap = getExternalSwapQuoteByPath({
|
||||
amountIn: approximateAmountIn,
|
||||
externalSwapPath: suitableSwapPath,
|
||||
externalSwapQuoteParams,
|
||||
});
|
||||
|
||||
if (!approximateExternalSwapQuoteForCombinedSwap) {
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
|
||||
const findSwapPathForSuitableSwapPath = createFindSwapPath({
|
||||
chainId,
|
||||
fromTokenAddress: tokenIn.address,
|
||||
toTokenAddress: suitableSwapPath.inTokenAddress,
|
||||
marketsInfoData,
|
||||
isExpressFeeSwap: false,
|
||||
});
|
||||
|
||||
const approximateSwapPathStatsForCombinedSwap = findSwapPathForSuitableSwapPath(
|
||||
approximateExternalSwapQuoteForCombinedSwap.usdOut
|
||||
);
|
||||
|
||||
if (!approximateSwapPathStatsForCombinedSwap) {
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
|
||||
const adjustedUsdIn =
|
||||
approximateSwapPathStatsForCombinedSwap.usdOut > 0
|
||||
? bigMath.mulDiv(approximateUsdIn, preferredUsdOut, approximateSwapPathStatsForCombinedSwap.usdOut)
|
||||
: 0n;
|
||||
|
||||
const adjustedAmountIn = convertToTokenAmount(adjustedUsdIn, tokenIn.decimals, getMidPrice(tokenIn.prices))!;
|
||||
|
||||
const adjustedExternalSwapQuoteForCombinedSwap = getExternalSwapQuoteByPath({
|
||||
amountIn: adjustedAmountIn,
|
||||
externalSwapPath: suitableSwapPath,
|
||||
externalSwapQuoteParams,
|
||||
});
|
||||
|
||||
if (!adjustedExternalSwapQuoteForCombinedSwap) {
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
|
||||
const adjustedSwapPathStatsForCombinedSwap = findSwapPathForSuitableSwapPath(
|
||||
adjustedExternalSwapQuoteForCombinedSwap.usdOut
|
||||
);
|
||||
|
||||
if (!adjustedSwapPathStatsForCombinedSwap) {
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
|
||||
return {
|
||||
type: "combinedSwap",
|
||||
externalSwapQuote: adjustedExternalSwapQuoteForCombinedSwap,
|
||||
swapPathStats: adjustedSwapPathStatsForCombinedSwap,
|
||||
amountIn: adjustedAmountIn,
|
||||
amountOut: adjustedSwapPathStatsForCombinedSwap.amountOut,
|
||||
usdIn: adjustedExternalSwapQuoteForCombinedSwap.usdIn,
|
||||
usdOut: adjustedSwapPathStatsForCombinedSwap.usdOut,
|
||||
priceIn: adjustedExternalSwapQuoteForCombinedSwap.priceIn,
|
||||
priceOut: priceOut,
|
||||
feesUsd: BigInt(adjustedExternalSwapQuoteForCombinedSwap.usdIn - adjustedSwapPathStatsForCombinedSwap.usdOut),
|
||||
};
|
||||
}
|
||||
|
||||
return defaultSwapStrategy;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const MAX_EDGE_PATH_LENGTH = 3;
|
||||
export const DEFAULT_NAIVE_TOP_PATHS_COUNT = 3;
|
||||
@@ -0,0 +1,49 @@
|
||||
import {BOTANIX} from "../../configs/chains.js";
|
||||
import {getTokenBySymbol, NATIVE_TOKEN_ADDRESS} from "../../configs/tokens.js";
|
||||
import {ExternalSwapAggregator, ExternalSwapPath} from "../../types/trade.js";
|
||||
|
||||
const BBTC_ADDRESS = NATIVE_TOKEN_ADDRESS;
|
||||
const PBTC_ADDRESS = getTokenBySymbol(BOTANIX, "PBTC").address;
|
||||
const STBTC_ADDRESS = getTokenBySymbol(BOTANIX, "STBTC").address;
|
||||
|
||||
export const AVAILABLE_BOTANIX_DEPOSIT_PAIRS = [
|
||||
{
|
||||
from: BBTC_ADDRESS,
|
||||
to: STBTC_ADDRESS,
|
||||
},
|
||||
{
|
||||
from: PBTC_ADDRESS,
|
||||
to: STBTC_ADDRESS,
|
||||
},
|
||||
];
|
||||
|
||||
export const AVAILABLE_BOTANIX_WITHDRAW_PAIRS = [
|
||||
{
|
||||
from: STBTC_ADDRESS,
|
||||
to: PBTC_ADDRESS,
|
||||
},
|
||||
];
|
||||
|
||||
const getBotanixStakingExternalSwapPaths = ({ fromTokenAddress }: { fromTokenAddress: string }): ExternalSwapPath[] => {
|
||||
return [...AVAILABLE_BOTANIX_DEPOSIT_PAIRS, ...AVAILABLE_BOTANIX_WITHDRAW_PAIRS]
|
||||
.filter((pair) => pair.from === fromTokenAddress)
|
||||
.map((pair) => ({
|
||||
aggregator: ExternalSwapAggregator.BotanixStaking,
|
||||
inTokenAddress: pair.from,
|
||||
outTokenAddress: pair.to,
|
||||
}));
|
||||
};
|
||||
|
||||
export const getAvailableExternalSwapPaths = ({
|
||||
chainId,
|
||||
fromTokenAddress,
|
||||
}: {
|
||||
chainId: number;
|
||||
fromTokenAddress: string;
|
||||
}): ExternalSwapPath[] => {
|
||||
if (chainId === BOTANIX) {
|
||||
return getBotanixStakingExternalSwapPaths({ fromTokenAddress });
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
ExternalSwapAggregator,
|
||||
ExternalSwapPath,
|
||||
ExternalSwapQuote,
|
||||
ExternalSwapQuoteParams
|
||||
} from "../../types/trade.js";
|
||||
|
||||
import {getBotanixStakingExternalSwapQuote} from "./botanixStaking.js";
|
||||
|
||||
export const getExternalSwapQuoteByPath = ({
|
||||
amountIn,
|
||||
externalSwapPath,
|
||||
externalSwapQuoteParams,
|
||||
}: {
|
||||
amountIn: bigint;
|
||||
externalSwapPath: ExternalSwapPath;
|
||||
externalSwapQuoteParams: ExternalSwapQuoteParams;
|
||||
}): ExternalSwapQuote | undefined => {
|
||||
if (
|
||||
amountIn === undefined ||
|
||||
externalSwapQuoteParams.gasPrice === undefined ||
|
||||
externalSwapQuoteParams.tokensData === undefined ||
|
||||
externalSwapQuoteParams.botanixStakingAssetsPerShare === undefined ||
|
||||
externalSwapQuoteParams.receiverAddress === undefined
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (externalSwapPath.aggregator === ExternalSwapAggregator.BotanixStaking) {
|
||||
return getBotanixStakingExternalSwapQuote({
|
||||
tokenInAddress: externalSwapPath.inTokenAddress,
|
||||
tokenOutAddress: externalSwapPath.outTokenAddress,
|
||||
amountIn,
|
||||
gasPrice: externalSwapQuoteParams.gasPrice,
|
||||
receiverAddress: externalSwapQuoteParams.receiverAddress,
|
||||
tokensData: externalSwapQuoteParams.tokensData,
|
||||
assetsPerShare: externalSwapQuoteParams.botanixStakingAssetsPerShare,
|
||||
});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import {objectKeysDeep} from "../objects.js";
|
||||
import type {MarketsGraph} from "./buildMarketsAdjacencyGraph.js";
|
||||
import {MAX_EDGE_PATH_LENGTH} from "./constants.js";
|
||||
|
||||
export function findReachableTokens(graph: MarketsGraph): Record<string, string[]> {
|
||||
const reachableTokens: Record<string, string[]> = {};
|
||||
|
||||
const allTokens = objectKeysDeep(graph, 1).sort();
|
||||
|
||||
for (const startToken of allTokens) {
|
||||
type TokenSearchState = {
|
||||
currentToken: string;
|
||||
pathLength: number;
|
||||
};
|
||||
|
||||
const searchQueue: TokenSearchState[] = [
|
||||
{
|
||||
currentToken: startToken,
|
||||
pathLength: 0,
|
||||
},
|
||||
];
|
||||
const visitedTokens = new Set<string>();
|
||||
|
||||
while (searchQueue.length > 0) {
|
||||
const { currentToken, pathLength } = searchQueue.shift()!;
|
||||
|
||||
if (visitedTokens.has(currentToken)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visitedTokens.add(currentToken);
|
||||
|
||||
if (pathLength >= MAX_EDGE_PATH_LENGTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const nextToken in graph[currentToken]) {
|
||||
searchQueue.push({
|
||||
currentToken: nextToken,
|
||||
pathLength: pathLength + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
reachableTokens[startToken] = Array.from(visitedTokens);
|
||||
}
|
||||
|
||||
return reachableTokens;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import type {SwapPaths} from "../../types/trade.js";
|
||||
import {objectKeysDeep} from "../objects.js";
|
||||
|
||||
import type {MarketsGraph} from "./buildMarketsAdjacencyGraph";
|
||||
import {MAX_EDGE_PATH_LENGTH} from "./constants.js";
|
||||
|
||||
export function findSwapPathsBetweenTokens(graph: MarketsGraph): SwapPaths {
|
||||
const swapRoutes: SwapPaths = {};
|
||||
|
||||
const allTokens = objectKeysDeep(graph, 1).sort();
|
||||
|
||||
for (const tokenAAddress of allTokens) {
|
||||
swapRoutes[tokenAAddress] = {};
|
||||
|
||||
let empty = true;
|
||||
for (const tokenBAddress of allTokens) {
|
||||
if (tokenAAddress === tokenBAddress || swapRoutes[tokenBAddress]?.[tokenAAddress]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const result: Record<string, string[]> = {};
|
||||
|
||||
type SwapPathNode = {
|
||||
currentToken: string;
|
||||
tokenPath: string[];
|
||||
};
|
||||
|
||||
const searchQueue: SwapPathNode[] = [
|
||||
{
|
||||
currentToken: tokenAAddress,
|
||||
tokenPath: [tokenAAddress],
|
||||
},
|
||||
];
|
||||
|
||||
while (searchQueue.length > 0) {
|
||||
const { currentToken, tokenPath } = searchQueue.shift()!;
|
||||
|
||||
// Example
|
||||
// ... -> ANIME -> USDC -> ANIME
|
||||
// There is only one edge from ANIME to USDC, so we skip this path
|
||||
// Because its almost always a guaranteed loss
|
||||
if (tokenPath.length >= 3) {
|
||||
const lastToken = tokenPath[tokenPath.length - 1];
|
||||
const secondLastToken = tokenPath[tokenPath.length - 2];
|
||||
const thirdLastToken = tokenPath[tokenPath.length - 3];
|
||||
|
||||
if (lastToken === thirdLastToken) {
|
||||
const lastEdge = graph[lastToken]?.[secondLastToken];
|
||||
if (lastEdge && lastEdge.length === 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenPath.length >= 2) {
|
||||
const lastToken = tokenPath[tokenPath.length - 1];
|
||||
const secondLastToken = tokenPath[tokenPath.length - 2];
|
||||
|
||||
if (lastToken === secondLastToken) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentToken === tokenBAddress) {
|
||||
const intermediateTokenPath = tokenPath.slice(1, -1);
|
||||
const pathKey = intermediateTokenPath.join(",");
|
||||
if (!result[pathKey]) {
|
||||
result[pathKey] = intermediateTokenPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenPath.length >= MAX_EDGE_PATH_LENGTH + 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const nextToken in graph[currentToken]) {
|
||||
searchQueue.push({
|
||||
currentToken: nextToken,
|
||||
tokenPath: [...tokenPath, nextToken],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(result).length > 0) {
|
||||
empty = false;
|
||||
swapRoutes[tokenAAddress][tokenBAddress] = Object.values(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
delete swapRoutes[tokenAAddress];
|
||||
}
|
||||
}
|
||||
|
||||
return swapRoutes;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import {MARKETS} from "../../configs/markets.js";
|
||||
import {SwapPaths} from "../../types/trade.js";
|
||||
|
||||
import {buildMarketsAdjacencyGraph, MarketsGraph} from "./buildMarketsAdjacencyGraph.js";
|
||||
import {findReachableTokens} from "./findReachableTokens.js";
|
||||
import {findSwapPathsBetweenTokens} from "./findSwapPathsBetweenTokens.js";
|
||||
|
||||
const MARKETS_ADJACENCY_GRAPH: {
|
||||
[chainId: number]: MarketsGraph;
|
||||
} = {};
|
||||
|
||||
for (const chainId in MARKETS) {
|
||||
const markets = MARKETS[chainId];
|
||||
const chainGraph = buildMarketsAdjacencyGraph(markets);
|
||||
|
||||
MARKETS_ADJACENCY_GRAPH[chainId] = chainGraph;
|
||||
}
|
||||
|
||||
const TOKEN_SWAP_PATHS: {
|
||||
[chainId: number]: SwapPaths;
|
||||
} = {};
|
||||
|
||||
for (const chainId in MARKETS) {
|
||||
const chainGraph = MARKETS_ADJACENCY_GRAPH[chainId];
|
||||
const chainSwapPaths = findSwapPathsBetweenTokens(chainGraph);
|
||||
|
||||
TOKEN_SWAP_PATHS[chainId] = chainSwapPaths;
|
||||
}
|
||||
|
||||
const REACHABLE_TOKENS: {
|
||||
[chainId: number]: {
|
||||
[token: string]: string[];
|
||||
};
|
||||
} = {};
|
||||
|
||||
for (const chainId in MARKETS) {
|
||||
const chainGraph = MARKETS_ADJACENCY_GRAPH[chainId];
|
||||
const chainReachableTokens = findReachableTokens(chainGraph);
|
||||
|
||||
REACHABLE_TOKENS[chainId] = chainReachableTokens;
|
||||
}
|
||||
|
||||
export { MARKETS_ADJACENCY_GRAPH, REACHABLE_TOKENS, TOKEN_SWAP_PATHS };
|
||||
@@ -1,72 +1,215 @@
|
||||
import { convertTokenAddress, getWrappedToken, NATIVE_TOKEN_ADDRESS } from "../../configs/tokens.js";
|
||||
import { MarketsInfoData } from "../../types/markets.js";
|
||||
import { FindSwapPath, MarketsGraph, SwapEstimator, SwapRoute } from "../../types/trade.js";
|
||||
import { findAllPaths, getBestSwapPath } from "./swapRouting.js";
|
||||
import { getSwapPathStats } from "./swapStats.js";
|
||||
import { getSwapPathComparator } from "./swapValues.js";
|
||||
import {MarketConfig, MARKETS} from "../../configs/markets.js";
|
||||
import {convertTokenAddress, getWrappedToken, NATIVE_TOKEN_ADDRESS} from "../../configs/tokens.js";
|
||||
import {GasLimitsConfig} from "../../types/fees.js";
|
||||
import {MarketsInfoData} from "../../types/markets.js";
|
||||
import {TokensData} from "../../types/tokens.js";
|
||||
import {FindSwapPath, SwapPathStats} from "../../types/trade.js";
|
||||
import {LRUCache} from "../LruCache.js";
|
||||
import {getIsMarketAvailableForExpressSwaps} from "../markets.js";
|
||||
|
||||
import {buildMarketsAdjacencyGraph, MarketsGraph} from "./buildMarketsAdjacencyGraph.js";
|
||||
import {
|
||||
createMarketEdgeLiquidityGetter,
|
||||
createNaiveNetworkEstimator,
|
||||
createNaiveSwapEstimator,
|
||||
createSwapEstimator,
|
||||
getBestSwapPath,
|
||||
getMarketAdjacencyGraph,
|
||||
getMaxLiquidityMarketSwapPathFromTokenSwapPaths,
|
||||
getNaiveBestMarketSwapPathsFromTokenSwapPaths,
|
||||
getTokenSwapPathsForTokenPairPrebuilt,
|
||||
marketRouteToMarketEdges,
|
||||
} from "./swapRouting.js";
|
||||
import {getSwapPathStats} from "./swapStats.js";
|
||||
|
||||
export const getWrappedAddress = (chainId: number, address: string | undefined) => {
|
||||
return address ? convertTokenAddress(chainId, address, "wrapped") : undefined;
|
||||
};
|
||||
|
||||
export const findAllSwapPaths = (params: {
|
||||
chainId: number;
|
||||
fromTokenAddress: string | undefined;
|
||||
toTokenAddress: string | undefined;
|
||||
marketsInfoData: MarketsInfoData;
|
||||
graph: MarketsGraph | undefined;
|
||||
wrappedFromAddress: string | undefined;
|
||||
wrappedToAddress: string | undefined;
|
||||
}) => {
|
||||
const { chainId, fromTokenAddress, toTokenAddress, marketsInfoData, graph, wrappedFromAddress, wrappedToAddress } =
|
||||
params;
|
||||
const DEBUG_MARKET_ADJACENCY_GRAPH_CACHE = new LRUCache<MarketsGraph>(100);
|
||||
|
||||
if (!marketsInfoData) return undefined;
|
||||
|
||||
const wrappedToken = getWrappedToken(chainId);
|
||||
const isWrap = fromTokenAddress === NATIVE_TOKEN_ADDRESS && toTokenAddress === wrappedToken.address;
|
||||
const isUnwrap = fromTokenAddress === wrappedToken.address && toTokenAddress === NATIVE_TOKEN_ADDRESS;
|
||||
const isSameToken = fromTokenAddress === toTokenAddress;
|
||||
|
||||
if (!graph || !wrappedFromAddress || !wrappedToAddress || isWrap || isUnwrap || isSameToken) {
|
||||
return undefined;
|
||||
function buildMarketAdjacencyGraph(chainId: number, disabledMarkets?: string[] | undefined) {
|
||||
if (!disabledMarkets?.length) {
|
||||
return getMarketAdjacencyGraph(chainId);
|
||||
}
|
||||
|
||||
return findAllPaths(marketsInfoData, graph, wrappedFromAddress, wrappedToAddress)?.sort((a, b) =>
|
||||
b.liquidity - a.liquidity > 0 ? 1 : -1
|
||||
const cacheKey = `${chainId}-${JSON.stringify(disabledMarkets)}`;
|
||||
|
||||
const cachedGraph = DEBUG_MARKET_ADJACENCY_GRAPH_CACHE.get(cacheKey);
|
||||
|
||||
if (cachedGraph) {
|
||||
return cachedGraph;
|
||||
}
|
||||
|
||||
const disabledMarketAddresses = disabledMarkets;
|
||||
|
||||
const strippedMarkets = Object.fromEntries(
|
||||
Object.entries(MARKETS[chainId]).filter(([marketAddress]) => !disabledMarketAddresses.includes(marketAddress))
|
||||
);
|
||||
};
|
||||
|
||||
const graph = buildMarketsAdjacencyGraph(strippedMarkets as Record<string, MarketConfig>);
|
||||
|
||||
DEBUG_MARKET_ADJACENCY_GRAPH_CACHE.set(cacheKey, graph);
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
const FALLBACK_FIND_SWAP_PATH: FindSwapPath = () => undefined;
|
||||
|
||||
export const createFindSwapPath = (params: {
|
||||
chainId: number;
|
||||
fromTokenAddress: string | undefined;
|
||||
toTokenAddress: string | undefined;
|
||||
marketsInfoData: MarketsInfoData | undefined;
|
||||
estimator: SwapEstimator | undefined;
|
||||
allPaths: SwapRoute[] | undefined;
|
||||
/**
|
||||
* Pass gas limits to take into account gas costs in swap path
|
||||
*/
|
||||
gasEstimationParams?:
|
||||
| {
|
||||
gasPrice: bigint;
|
||||
gasLimits: GasLimitsConfig;
|
||||
tokensData: TokensData;
|
||||
}
|
||||
| undefined;
|
||||
isExpressFeeSwap: boolean | undefined;
|
||||
disabledMarkets?: string[] | undefined;
|
||||
manualPath?: string[] | undefined;
|
||||
maxSwapPathLength?: number | undefined;
|
||||
}): FindSwapPath => {
|
||||
const { chainId, fromTokenAddress, toTokenAddress, marketsInfoData, estimator, allPaths } = params;
|
||||
const {
|
||||
chainId,
|
||||
fromTokenAddress,
|
||||
toTokenAddress,
|
||||
marketsInfoData,
|
||||
disabledMarkets,
|
||||
manualPath,
|
||||
gasEstimationParams,
|
||||
isExpressFeeSwap,
|
||||
maxSwapPathLength,
|
||||
} = params;
|
||||
const wrappedFromAddress = getWrappedAddress(chainId, fromTokenAddress);
|
||||
const wrappedToAddress = getWrappedAddress(chainId, toTokenAddress);
|
||||
const wrappedToken = getWrappedToken(chainId);
|
||||
|
||||
const findSwapPath: FindSwapPath = (usdIn: bigint, opts: { order?: ("liquidity" | "length")[] }) => {
|
||||
if (!allPaths?.length || !estimator || !marketsInfoData || !fromTokenAddress) {
|
||||
let tokenSwapPaths =
|
||||
wrappedFromAddress && wrappedToAddress
|
||||
? getTokenSwapPathsForTokenPairPrebuilt(chainId, wrappedFromAddress, wrappedToAddress)
|
||||
: [];
|
||||
|
||||
if (maxSwapPathLength) {
|
||||
/**
|
||||
* As tokenSwapPath contains what tokens can we between input and output token,
|
||||
* restricting intermediate tokens to 0 would mean we filter out any non-direct market swaps,
|
||||
* length of 1 would mean all 2-step swaps
|
||||
*/
|
||||
const nonDirectPathLength = maxSwapPathLength - 1;
|
||||
tokenSwapPaths = tokenSwapPaths.filter((path) => path.length <= nonDirectPathLength);
|
||||
}
|
||||
|
||||
const finalDisabledMarkets = [...(disabledMarkets ?? [])];
|
||||
|
||||
if (isExpressFeeSwap) {
|
||||
const expressSwapUnavailableMarkets = Object.values(marketsInfoData ?? {})
|
||||
.filter((market) => !getIsMarketAvailableForExpressSwaps(market))
|
||||
.map((market) => market.marketTokenAddress);
|
||||
|
||||
finalDisabledMarkets.push(...expressSwapUnavailableMarkets);
|
||||
}
|
||||
|
||||
const isAtomicSwap = Boolean(isExpressFeeSwap);
|
||||
|
||||
const marketAdjacencyGraph = buildMarketAdjacencyGraph(chainId, finalDisabledMarkets);
|
||||
|
||||
const cache: Record<string, SwapPathStats | undefined> = {};
|
||||
|
||||
if (!marketsInfoData) {
|
||||
return FALLBACK_FIND_SWAP_PATH;
|
||||
}
|
||||
|
||||
const marketEdgeLiquidityGetter = createMarketEdgeLiquidityGetter(marketsInfoData);
|
||||
const naiveEstimator = createNaiveSwapEstimator(marketsInfoData, isAtomicSwap);
|
||||
const naiveNetworkEstimator = gasEstimationParams
|
||||
? createNaiveNetworkEstimator({
|
||||
gasLimits: gasEstimationParams.gasLimits,
|
||||
tokensData: gasEstimationParams.tokensData,
|
||||
gasPrice: gasEstimationParams.gasPrice,
|
||||
chainId,
|
||||
})
|
||||
: undefined;
|
||||
const estimator = createSwapEstimator(marketsInfoData, isAtomicSwap);
|
||||
|
||||
const findSwapPath: FindSwapPath = (usdIn: bigint, opts?: { order?: ("liquidity" | "length")[] }) => {
|
||||
if (tokenSwapPaths.length === 0 || !fromTokenAddress || !wrappedFromAddress || !wrappedToAddress) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cacheKey = `${usdIn}-${opts?.order?.join("-") || "none"}`;
|
||||
if (cache[cacheKey]) {
|
||||
return cache[cacheKey];
|
||||
}
|
||||
|
||||
let swapPath: string[] | undefined = undefined;
|
||||
const sortedPaths = opts.order ? [...allPaths].sort(getSwapPathComparator(opts.order ?? [])) : allPaths;
|
||||
|
||||
if (opts.order) {
|
||||
swapPath = sortedPaths[0].path;
|
||||
if (manualPath !== undefined) {
|
||||
swapPath = manualPath;
|
||||
} else if (opts?.order || usdIn === 0n) {
|
||||
const primaryOrder = opts?.order?.at(0) === "length" ? "length" : "liquidity";
|
||||
|
||||
if (!marketEdgeLiquidityGetter) {
|
||||
swapPath = undefined;
|
||||
} else {
|
||||
let applicableTokenSwapPaths = tokenSwapPaths;
|
||||
|
||||
if (primaryOrder === "length") {
|
||||
const shortestLength = Math.min(...tokenSwapPaths.map((path) => path.length));
|
||||
applicableTokenSwapPaths = tokenSwapPaths.filter((path) => path.length === shortestLength);
|
||||
}
|
||||
|
||||
const maxLiquidityPathInfo = getMaxLiquidityMarketSwapPathFromTokenSwapPaths({
|
||||
graph: marketAdjacencyGraph,
|
||||
tokenSwapPaths: applicableTokenSwapPaths,
|
||||
tokenInAddress: wrappedFromAddress,
|
||||
tokenOutAddress: wrappedToAddress,
|
||||
getLiquidity: marketEdgeLiquidityGetter,
|
||||
});
|
||||
|
||||
if (maxLiquidityPathInfo) {
|
||||
swapPath = maxLiquidityPathInfo.path;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
swapPath = getBestSwapPath(allPaths, usdIn, estimator);
|
||||
if (naiveEstimator) {
|
||||
const naiveSwapRoutes = getNaiveBestMarketSwapPathsFromTokenSwapPaths({
|
||||
graph: marketAdjacencyGraph,
|
||||
tokenSwapPaths,
|
||||
usdIn,
|
||||
tokenInAddress: wrappedFromAddress,
|
||||
tokenOutAddress: wrappedToAddress,
|
||||
estimator: naiveEstimator,
|
||||
networkEstimator: naiveNetworkEstimator,
|
||||
});
|
||||
|
||||
if (naiveSwapRoutes?.length) {
|
||||
const edges = naiveSwapRoutes.map((path) =>
|
||||
marketRouteToMarketEdges(path, wrappedFromAddress, marketsInfoData)
|
||||
);
|
||||
|
||||
swapPath = getBestSwapPath({
|
||||
routes: edges,
|
||||
usdIn,
|
||||
estimator,
|
||||
networkEstimator: naiveNetworkEstimator,
|
||||
})?.map((edge) => edge.marketAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!swapPath) {
|
||||
cache[cacheKey] = undefined;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getSwapPathStats({
|
||||
let result: SwapPathStats | undefined = getSwapPathStats({
|
||||
marketsInfoData,
|
||||
swapPath,
|
||||
initialCollateralAddress: fromTokenAddress,
|
||||
@@ -74,7 +217,12 @@ export const createFindSwapPath = (params: {
|
||||
shouldUnwrapNativeToken: toTokenAddress === NATIVE_TOKEN_ADDRESS,
|
||||
shouldApplyPriceImpact: true,
|
||||
usdIn,
|
||||
isAtomicSwap,
|
||||
});
|
||||
|
||||
cache[cacheKey] = result;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return findSwapPath;
|
||||
|
||||
@@ -1,122 +1,45 @@
|
||||
import { MarketInfo, MarketsInfoData } from "../../types/markets.js";
|
||||
import { MarketEdge, SwapEstimator, SwapRoute } from "../../types/trade.js";
|
||||
import { MarketsGraph } from "../../types/trade.js";
|
||||
import { bigMath } from "../bigmath.js";
|
||||
import { getAvailableUsdLiquidityForCollateral } from "../markets.js";
|
||||
import { getMaxSwapPathLiquidity, getSwapCapacityUsd, getSwapStats } from "./swapStats.js";
|
||||
import { SWAP_GRAPH_MAX_MARKETS_PER_TOKEN } from "../../configs/markets.js";
|
||||
import {maxUint256} from "viem";
|
||||
|
||||
export function limitMarketsPerTokens(
|
||||
markets: MarketInfo[],
|
||||
limit: number = SWAP_GRAPH_MAX_MARKETS_PER_TOKEN
|
||||
): MarketInfo[] {
|
||||
const marketsByTokens: { [token: string]: MarketInfo[] } = {};
|
||||
import {GasLimitsConfig} from "../../types/fees.js";
|
||||
import {MarketsInfoData} from "../../types/markets.js";
|
||||
import {TokensData} from "../../types/tokens.js";
|
||||
import {
|
||||
MarketEdge,
|
||||
MarketEdgeLiquidityGetter,
|
||||
NaiveNetworkEstimator,
|
||||
NaiveSwapEstimator,
|
||||
SwapEstimator,
|
||||
SwapPaths,
|
||||
} from "../../types/trade.js";
|
||||
import {bigMath} from "../../utils/bigmath.js";
|
||||
import {estimateOrderOraclePriceCount, getExecutionFee} from "../../utils/fees/index.js";
|
||||
import {getNaiveEstimatedGasBySwapCount} from "../../utils/fees/getNaiveEstimatedGasBySwapCount.js";
|
||||
import {getAvailableUsdLiquidityForCollateral, getTokenPoolType} from "../../utils/markets.js";
|
||||
import {bigintToNumber, PRECISION, PRECISION_DECIMALS} from "../../utils/numbers.js";
|
||||
import {getByKey} from "../../utils/objects.js";
|
||||
import {MarketsGraph} from "../../utils/swap/buildMarketsAdjacencyGraph.js";
|
||||
import {DEFAULT_NAIVE_TOP_PATHS_COUNT} from "../../utils/swap/constants.js";
|
||||
import {MARKETS_ADJACENCY_GRAPH, REACHABLE_TOKENS, TOKEN_SWAP_PATHS} from "../../utils/swap/preparedSwapData.js";
|
||||
|
||||
for (const market of markets) {
|
||||
if (market.isSameCollaterals || market.isDisabled) {
|
||||
continue;
|
||||
}
|
||||
import {getSwapStats} from "./swapStats.js";
|
||||
|
||||
const { longTokenAddress, shortTokenAddress } = market;
|
||||
|
||||
marketsByTokens[longTokenAddress] = marketsByTokens[longTokenAddress] || [];
|
||||
marketsByTokens[longTokenAddress].push(market);
|
||||
|
||||
marketsByTokens[shortTokenAddress] = marketsByTokens[shortTokenAddress] || [];
|
||||
marketsByTokens[shortTokenAddress].push(market);
|
||||
}
|
||||
|
||||
const resultMarkets: { [marketAddress: string]: MarketInfo } = {};
|
||||
|
||||
const tokenAddresses = Object.keys(marketsByTokens);
|
||||
|
||||
for (const tokenAddress of tokenAddresses) {
|
||||
const markets = marketsByTokens[tokenAddress];
|
||||
|
||||
const sortedMarkets = markets.sort((m1, m2) => {
|
||||
const liq1 = getAvailableUsdLiquidityForCollateral(m1, m1.longTokenAddress === tokenAddress);
|
||||
const cap1 = getSwapCapacityUsd(m1, m1.longTokenAddress === tokenAddress);
|
||||
|
||||
const limit1 = bigMath.min(liq1, cap1);
|
||||
|
||||
const liq2 = getAvailableUsdLiquidityForCollateral(m2, m2.longTokenAddress === tokenAddress);
|
||||
const cap2 = getSwapCapacityUsd(m2, m2.longTokenAddress === tokenAddress);
|
||||
|
||||
const limit2 = bigMath.min(liq2, cap2);
|
||||
|
||||
return Number(limit2 - limit1);
|
||||
});
|
||||
|
||||
let marketsPerTokenCount = 0;
|
||||
|
||||
for (const market of sortedMarkets) {
|
||||
if (marketsPerTokenCount >= limit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (resultMarkets[market.marketTokenAddress]) {
|
||||
marketsPerTokenCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
marketsPerTokenCount++;
|
||||
resultMarkets[market.marketTokenAddress] = market;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(resultMarkets);
|
||||
}
|
||||
|
||||
export function getMarketsGraph(markets: MarketInfo[]): MarketsGraph {
|
||||
const graph: MarketsGraph = {
|
||||
abjacencyList: {},
|
||||
edges: [],
|
||||
};
|
||||
|
||||
const limitedMarkets = limitMarketsPerTokens(markets);
|
||||
|
||||
for (const market of limitedMarkets) {
|
||||
const { longTokenAddress, shortTokenAddress, marketTokenAddress, isSameCollaterals, isDisabled } = market;
|
||||
|
||||
if (isSameCollaterals || isDisabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const longShortEdge: MarketEdge = {
|
||||
marketInfo: market,
|
||||
marketAddress: marketTokenAddress,
|
||||
from: longTokenAddress,
|
||||
to: shortTokenAddress,
|
||||
};
|
||||
|
||||
const shortLongEdge: MarketEdge = {
|
||||
marketInfo: market,
|
||||
marketAddress: marketTokenAddress,
|
||||
from: shortTokenAddress,
|
||||
to: longTokenAddress,
|
||||
};
|
||||
|
||||
graph.abjacencyList[longTokenAddress] = graph.abjacencyList[longTokenAddress] || [];
|
||||
graph.abjacencyList[longTokenAddress].push(longShortEdge);
|
||||
graph.abjacencyList[shortTokenAddress] = graph.abjacencyList[shortTokenAddress] || [];
|
||||
graph.abjacencyList[shortTokenAddress].push(shortLongEdge);
|
||||
|
||||
graph.edges.push(longShortEdge, shortLongEdge);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
export const createSwapEstimator = (marketsInfoData: MarketsInfoData): SwapEstimator => {
|
||||
export const createSwapEstimator = (marketsInfoData: MarketsInfoData, isAtomicSwap: boolean): SwapEstimator => {
|
||||
return (e: MarketEdge, usdIn: bigint) => {
|
||||
const marketInfo = marketsInfoData[e.marketAddress];
|
||||
|
||||
if (!marketInfo || marketInfo.isDisabled) {
|
||||
return {
|
||||
usdOut: 0n,
|
||||
};
|
||||
}
|
||||
|
||||
const swapStats = getSwapStats({
|
||||
marketInfo,
|
||||
usdIn,
|
||||
tokenInAddress: e.from,
|
||||
tokenOutAddress: e.to,
|
||||
shouldApplyPriceImpact: true,
|
||||
isAtomicSwap,
|
||||
});
|
||||
|
||||
const isOutLiquidity = swapStats?.isOutLiquidity;
|
||||
@@ -135,23 +58,111 @@ export const createSwapEstimator = (marketsInfoData: MarketsInfoData): SwapEstim
|
||||
};
|
||||
};
|
||||
|
||||
export function getBestSwapPath(routes: SwapRoute[], usdIn: bigint, estimator: SwapEstimator) {
|
||||
export const createMarketEdgeLiquidityGetter = (marketsInfoData: MarketsInfoData): MarketEdgeLiquidityGetter => {
|
||||
return (e: MarketEdge) => {
|
||||
const marketInfo = getByKey(marketsInfoData, e.marketAddress);
|
||||
|
||||
if (!marketInfo || marketInfo.isDisabled) {
|
||||
return 0n;
|
||||
}
|
||||
|
||||
const isTokenOutLong = getTokenPoolType(marketInfo, e.to) === "long";
|
||||
const liquidity = getAvailableUsdLiquidityForCollateral(marketInfo, isTokenOutLong);
|
||||
|
||||
return liquidity;
|
||||
};
|
||||
};
|
||||
|
||||
export const createNaiveSwapEstimator = (
|
||||
marketsInfoData: MarketsInfoData,
|
||||
isAtomicSwap: boolean
|
||||
): NaiveSwapEstimator => {
|
||||
return (e: MarketEdge, usdIn: bigint) => {
|
||||
let marketInfo = marketsInfoData[e.marketAddress];
|
||||
|
||||
if (marketInfo === undefined || marketInfo.isDisabled) {
|
||||
return { swapYield: 0 };
|
||||
}
|
||||
|
||||
const swapStats = getSwapStats({
|
||||
marketInfo,
|
||||
usdIn,
|
||||
tokenInAddress: e.from,
|
||||
tokenOutAddress: e.to,
|
||||
shouldApplyPriceImpact: true,
|
||||
isAtomicSwap,
|
||||
});
|
||||
|
||||
const usdOut = swapStats?.usdOut;
|
||||
|
||||
if (usdOut === undefined || usdOut === 0n || swapStats.isOutCapacity || swapStats.isOutLiquidity) {
|
||||
return { swapYield: 0 };
|
||||
}
|
||||
|
||||
const swapYield = bigintToNumber((usdOut * PRECISION) / usdIn, PRECISION_DECIMALS);
|
||||
|
||||
return { swapYield };
|
||||
};
|
||||
};
|
||||
|
||||
export const createNaiveNetworkEstimator = ({
|
||||
gasLimits,
|
||||
tokensData,
|
||||
gasPrice,
|
||||
chainId,
|
||||
}: {
|
||||
gasLimits: GasLimitsConfig;
|
||||
tokensData: TokensData;
|
||||
gasPrice: bigint;
|
||||
chainId: number;
|
||||
}): NaiveNetworkEstimator => {
|
||||
return (usdIn: bigint, swapsCount: number) => {
|
||||
const estimatedGas = getNaiveEstimatedGasBySwapCount(gasLimits.singleSwap, swapsCount);
|
||||
if (estimatedGas === null || estimatedGas === undefined) return { networkYield: 1.0, usdOut: usdIn };
|
||||
|
||||
const oraclePriceCount = estimateOrderOraclePriceCount(swapsCount);
|
||||
|
||||
const feeUsd = getExecutionFee(chainId, gasLimits, tokensData, estimatedGas, gasPrice, oraclePriceCount)?.feeUsd;
|
||||
if (feeUsd === undefined) return { networkYield: 1.0, usdOut: usdIn };
|
||||
|
||||
const networkYield = bigintToNumber(bigMath.mulDiv(usdIn, PRECISION, usdIn + feeUsd), PRECISION_DECIMALS);
|
||||
|
||||
return { networkYield, usdOut: usdIn - feeUsd };
|
||||
};
|
||||
};
|
||||
|
||||
export function getBestSwapPath({
|
||||
routes,
|
||||
usdIn,
|
||||
estimator,
|
||||
networkEstimator,
|
||||
}: {
|
||||
routes: MarketEdge[][];
|
||||
usdIn: bigint;
|
||||
estimator: SwapEstimator;
|
||||
networkEstimator?: NaiveNetworkEstimator;
|
||||
}): MarketEdge[] | undefined {
|
||||
if (routes.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let bestPath = routes[0].path;
|
||||
let bestRoute = routes[0];
|
||||
let bestUsdOut = 0n;
|
||||
|
||||
for (const route of routes) {
|
||||
try {
|
||||
const pathUsdOut = route.edges.reduce((prevUsdOut, edge) => {
|
||||
let pathUsdOut = route.reduce((prevUsdOut, edge) => {
|
||||
const { usdOut } = estimator(edge, prevUsdOut);
|
||||
return usdOut;
|
||||
}, usdIn);
|
||||
|
||||
if (networkEstimator) {
|
||||
const { usdOut } = networkEstimator(pathUsdOut, route.length);
|
||||
pathUsdOut = usdOut;
|
||||
}
|
||||
|
||||
if (pathUsdOut > bestUsdOut) {
|
||||
bestPath = route.path;
|
||||
bestRoute = route;
|
||||
bestUsdOut = pathUsdOut;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -159,57 +170,433 @@ export function getBestSwapPath(routes: SwapRoute[], usdIn: bigint, estimator: S
|
||||
}
|
||||
}
|
||||
|
||||
return bestPath;
|
||||
return bestRoute;
|
||||
}
|
||||
|
||||
export function findAllPaths(
|
||||
marketsInfoData: MarketsInfoData,
|
||||
graph: MarketsGraph,
|
||||
from: string,
|
||||
to: string,
|
||||
maxDepth = 3
|
||||
): SwapRoute[] | undefined {
|
||||
const routes: SwapRoute[] = [];
|
||||
export function getNaiveBestMarketSwapPathsFromTokenSwapPaths({
|
||||
graph,
|
||||
tokenSwapPaths,
|
||||
usdIn,
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
estimator,
|
||||
topPathsCount = DEFAULT_NAIVE_TOP_PATHS_COUNT,
|
||||
networkEstimator,
|
||||
}: {
|
||||
graph: MarketsGraph;
|
||||
tokenSwapPaths: string[][];
|
||||
usdIn: bigint;
|
||||
tokenInAddress: string;
|
||||
tokenOutAddress: string;
|
||||
estimator: NaiveSwapEstimator;
|
||||
topPathsCount?: number;
|
||||
networkEstimator?: NaiveNetworkEstimator;
|
||||
}): string[][] {
|
||||
// This seems to be true, because for any path if we have performed swaps to and from token
|
||||
// The best markets sequence is the same
|
||||
const cachedBestMarketForTokenEdge: Record<
|
||||
// Key: tokenHopFromAddress-tokenHopToAddress-count
|
||||
string,
|
||||
{
|
||||
marketAddress: string;
|
||||
swapYield: number;
|
||||
}
|
||||
> = {};
|
||||
|
||||
const edges = graph.abjacencyList[from];
|
||||
const calculatedCache: Record<
|
||||
// From token address
|
||||
string,
|
||||
Record<
|
||||
// To token address
|
||||
string,
|
||||
Record<
|
||||
// Market address
|
||||
string,
|
||||
number
|
||||
>
|
||||
>
|
||||
> = {};
|
||||
|
||||
if (!edges?.length) {
|
||||
const topPaths: {
|
||||
marketPath: string[];
|
||||
swapYield: number;
|
||||
}[] = [];
|
||||
|
||||
const networkYieldCache: Record<number, number> = {};
|
||||
|
||||
for (const pathType of tokenSwapPaths) {
|
||||
const marketPath: string[] = [];
|
||||
let pathTypeSwapYield = 1;
|
||||
let bad = false;
|
||||
|
||||
// Just how many times we have swapped from token A to token B
|
||||
const tokenSwapCounter: Record<
|
||||
// From token address
|
||||
string,
|
||||
Record<
|
||||
// To token address
|
||||
string,
|
||||
number
|
||||
>
|
||||
> = {};
|
||||
|
||||
for (let hopIndex = 0; hopIndex <= pathType.length; hopIndex++) {
|
||||
const tokenHopFromAddress = hopIndex === 0 ? tokenInAddress : pathType[hopIndex - 1];
|
||||
const tokenHopToAddress = hopIndex === pathType.length ? tokenOutAddress : pathType[hopIndex];
|
||||
|
||||
// prevTokenAddress -> tokenAddress
|
||||
// get all markets for prevTokenAddress -> tokenAddress
|
||||
const marketAddresses = getMarketsForTokenPair(graph, tokenHopFromAddress, tokenHopToAddress);
|
||||
|
||||
if (marketAddresses.length === 0) {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const tokenSwapCount = tokenSwapCounter[tokenHopFromAddress]?.[tokenHopToAddress] || 0;
|
||||
|
||||
const key = `${tokenHopFromAddress}-${tokenHopToAddress}-${tokenSwapCount}`;
|
||||
|
||||
let bestMarketInfo:
|
||||
| {
|
||||
marketAddress: string;
|
||||
swapYield: number;
|
||||
}
|
||||
| undefined = cachedBestMarketForTokenEdge[key];
|
||||
|
||||
if (!bestMarketInfo) {
|
||||
calculatedCache[tokenHopFromAddress] = calculatedCache[tokenHopFromAddress] || {};
|
||||
calculatedCache[tokenHopFromAddress][tokenHopToAddress] =
|
||||
calculatedCache[tokenHopFromAddress][tokenHopToAddress] || {};
|
||||
|
||||
bestMarketInfo = getBestMarketForTokenEdge({
|
||||
marketAddresses,
|
||||
usdIn,
|
||||
tokenInAddress: tokenHopFromAddress,
|
||||
tokenOutAddress: tokenHopToAddress,
|
||||
estimator,
|
||||
marketPath,
|
||||
calculatedCache: calculatedCache[tokenHopFromAddress][tokenHopToAddress],
|
||||
});
|
||||
|
||||
if (!bestMarketInfo) {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
|
||||
cachedBestMarketForTokenEdge[key] = bestMarketInfo;
|
||||
}
|
||||
|
||||
if (bestMarketInfo.swapYield === 0) {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
|
||||
pathTypeSwapYield *= bestMarketInfo.swapYield;
|
||||
marketPath.push(bestMarketInfo.marketAddress);
|
||||
|
||||
tokenSwapCounter[tokenHopFromAddress] = tokenSwapCounter[tokenHopFromAddress] || {};
|
||||
tokenSwapCounter[tokenHopFromAddress][tokenHopToAddress] =
|
||||
(tokenSwapCounter[tokenHopFromAddress][tokenHopToAddress] || 0) + 1;
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (topPaths.length < topPathsCount) {
|
||||
topPaths.push({ marketPath: marketPath, swapYield: pathTypeSwapYield });
|
||||
} else {
|
||||
let adjustedPathTypeSwapYield = pathTypeSwapYield;
|
||||
|
||||
if (networkEstimator) {
|
||||
let networkYield = networkYieldCache[marketPath.length];
|
||||
|
||||
if (networkYield === undefined) {
|
||||
networkYield = networkEstimator(usdIn, marketPath.length).networkYield;
|
||||
|
||||
networkYieldCache[marketPath.length] = networkYield;
|
||||
}
|
||||
|
||||
adjustedPathTypeSwapYield = adjustedPathTypeSwapYield * networkYield;
|
||||
}
|
||||
|
||||
// if yield is greater than any of the top paths, replace the one with the lowest yield
|
||||
let minSwapYield = topPaths[0].swapYield;
|
||||
let minSwapYieldIndex = 0;
|
||||
for (let i = 1; i < topPaths.length; i++) {
|
||||
if (topPaths[i].swapYield < minSwapYield) {
|
||||
minSwapYield = topPaths[i].swapYield;
|
||||
minSwapYieldIndex = i;
|
||||
}
|
||||
}
|
||||
if (adjustedPathTypeSwapYield > minSwapYield) {
|
||||
topPaths[minSwapYieldIndex] = { marketPath: marketPath, swapYield: adjustedPathTypeSwapYield };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return topPaths.map((p) => p.marketPath);
|
||||
}
|
||||
|
||||
export function getMarketsForTokenPair(graph: MarketsGraph, tokenAAddress: string, tokenBAddress: string): string[] {
|
||||
if (graph[tokenAAddress]?.[tokenBAddress]) {
|
||||
return graph[tokenAAddress][tokenBAddress];
|
||||
}
|
||||
|
||||
if (graph[tokenBAddress]?.[tokenAAddress]) {
|
||||
return graph[tokenBAddress][tokenAAddress];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
export function getBestMarketForTokenEdge({
|
||||
marketAddresses,
|
||||
usdIn,
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
estimator,
|
||||
marketPath,
|
||||
calculatedCache,
|
||||
}: {
|
||||
marketAddresses: string[];
|
||||
usdIn: bigint;
|
||||
tokenInAddress: string;
|
||||
tokenOutAddress: string;
|
||||
estimator: NaiveSwapEstimator;
|
||||
marketPath?: string[];
|
||||
calculatedCache?: Record<
|
||||
// Key: market address
|
||||
string,
|
||||
number
|
||||
>;
|
||||
}):
|
||||
| {
|
||||
marketAddress: string;
|
||||
swapYield: number;
|
||||
}
|
||||
| undefined {
|
||||
let bestMarketAddress = marketAddresses[0];
|
||||
let bestYield = 0;
|
||||
let found = false;
|
||||
|
||||
for (const marketAddress of marketAddresses) {
|
||||
if (marketPath && marketPath.includes(marketAddress)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let swapYield: number | undefined = undefined;
|
||||
|
||||
const key = marketAddress;
|
||||
if (calculatedCache) {
|
||||
swapYield = calculatedCache[key];
|
||||
}
|
||||
|
||||
if (swapYield === undefined) {
|
||||
swapYield = estimator(
|
||||
{
|
||||
marketAddress,
|
||||
from: tokenInAddress,
|
||||
to: tokenOutAddress,
|
||||
},
|
||||
usdIn
|
||||
).swapYield;
|
||||
|
||||
if (calculatedCache) {
|
||||
calculatedCache[key] = swapYield;
|
||||
}
|
||||
}
|
||||
|
||||
if (swapYield > bestYield) {
|
||||
bestYield = swapYield;
|
||||
bestMarketAddress = marketAddress;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (const e of edges) {
|
||||
dfs(e, [], [], {});
|
||||
}
|
||||
|
||||
function dfs(edge: MarketEdge, path: string[], pathEdges: MarketEdge[], visited: { [edgeId: string]: boolean }) {
|
||||
// avoid too deep paths and cycles
|
||||
if (path.length >= maxDepth || visited[edge.marketAddress]) {
|
||||
return;
|
||||
}
|
||||
|
||||
visited[edge.marketAddress] = true;
|
||||
pathEdges.push(edge);
|
||||
path.push(edge.marketAddress);
|
||||
|
||||
if (edge.to === to) {
|
||||
routes.push({
|
||||
edges: pathEdges,
|
||||
path: path,
|
||||
liquidity: getMaxSwapPathLiquidity({ marketsInfoData, swapPath: path, initialCollateralAddress: from }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const edges = graph.abjacencyList[edge.to];
|
||||
|
||||
if (!edges?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const e of edges) {
|
||||
dfs(e, [...path], [...pathEdges], { ...visited });
|
||||
}
|
||||
}
|
||||
|
||||
return routes;
|
||||
return {
|
||||
marketAddress: bestMarketAddress,
|
||||
swapYield: bestYield,
|
||||
};
|
||||
}
|
||||
|
||||
export function marketRouteToMarketEdges(
|
||||
marketPath: string[],
|
||||
from: string,
|
||||
marketsInfoData: MarketsInfoData
|
||||
): MarketEdge[] {
|
||||
let edges: MarketEdge[] = [];
|
||||
|
||||
for (let i = 0; i < marketPath.length; i++) {
|
||||
const currentFrom = i === 0 ? from : edges[i - 1].to;
|
||||
const currentTo =
|
||||
marketsInfoData[marketPath[i]].longTokenAddress === currentFrom
|
||||
? marketsInfoData[marketPath[i]].shortTokenAddress
|
||||
: marketsInfoData[marketPath[i]].longTokenAddress;
|
||||
|
||||
edges.push({ from: currentFrom, to: currentTo, marketAddress: marketPath[i] });
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
export function getTokenSwapPathsForTokenPair(
|
||||
tokenSwapPaths: SwapPaths,
|
||||
tokenAAddress: string,
|
||||
tokenBAddress: string
|
||||
): string[][] {
|
||||
if (tokenSwapPaths[tokenAAddress]?.[tokenBAddress]) {
|
||||
return tokenSwapPaths[tokenAAddress][tokenBAddress];
|
||||
}
|
||||
|
||||
if (tokenSwapPaths[tokenBAddress]?.[tokenAAddress]) {
|
||||
return tokenSwapPaths[tokenBAddress][tokenAAddress].map((route) => [...route].reverse());
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
export function getTokenSwapPathsForTokenPairPrebuilt(chainId: number, from: string, to: string): string[][] {
|
||||
return getTokenSwapPathsForTokenPair(TOKEN_SWAP_PATHS[chainId], from, to);
|
||||
}
|
||||
|
||||
export function getMarketAdjacencyGraph(chainId: number): MarketsGraph {
|
||||
return MARKETS_ADJACENCY_GRAPH[chainId];
|
||||
}
|
||||
|
||||
export function findAllReachableTokens(chainId: number, from: string): string[] {
|
||||
return REACHABLE_TOKENS[chainId][from];
|
||||
}
|
||||
|
||||
export function getMaxLiquidityMarketSwapPathFromTokenSwapPaths({
|
||||
graph,
|
||||
tokenSwapPaths,
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
getLiquidity,
|
||||
}: {
|
||||
graph: MarketsGraph;
|
||||
tokenSwapPaths: string[][];
|
||||
tokenInAddress: string;
|
||||
tokenOutAddress: string;
|
||||
getLiquidity: MarketEdgeLiquidityGetter;
|
||||
}): { path: string[]; liquidity: bigint } | undefined {
|
||||
// go through all edges and find best yield market for it
|
||||
|
||||
const cachedMaxLiquidityMarketForTokenEdge: Record<
|
||||
string,
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
marketAddress: string;
|
||||
liquidity: bigint;
|
||||
}
|
||||
>
|
||||
> = {};
|
||||
|
||||
let bestMarketPath: string[] | undefined = undefined;
|
||||
let bestLiquidity = 0n;
|
||||
|
||||
for (const pathType of tokenSwapPaths) {
|
||||
let bad = false;
|
||||
let bestMarketPathForPathType: string[] = [];
|
||||
let pathTypeBestLiquidity = maxUint256;
|
||||
|
||||
for (let hopIndex = 0; hopIndex <= pathType.length; hopIndex++) {
|
||||
const tokenFromAddress = hopIndex === 0 ? tokenInAddress : pathType[hopIndex - 1];
|
||||
const tokenToAddress = hopIndex === pathType.length ? tokenOutAddress : pathType[hopIndex];
|
||||
|
||||
// prevTokenAddress -> tokenAddress
|
||||
// get all markets for prevTokenAddress -> tokenAddress
|
||||
const markets = getMarketsForTokenPair(graph, tokenFromAddress, tokenToAddress);
|
||||
|
||||
if (markets.length === 0) {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
|
||||
let bestMarketInfo:
|
||||
| {
|
||||
marketAddress: string;
|
||||
liquidity: bigint;
|
||||
}
|
||||
| undefined = cachedMaxLiquidityMarketForTokenEdge[tokenFromAddress]?.[tokenToAddress];
|
||||
|
||||
if (!bestMarketInfo) {
|
||||
bestMarketInfo = getMaxLiquidityMarketForTokenEdge({
|
||||
markets,
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
getLiquidity,
|
||||
});
|
||||
|
||||
cachedMaxLiquidityMarketForTokenEdge[tokenFromAddress] =
|
||||
cachedMaxLiquidityMarketForTokenEdge[tokenFromAddress] || {};
|
||||
cachedMaxLiquidityMarketForTokenEdge[tokenFromAddress][tokenToAddress] = bestMarketInfo;
|
||||
}
|
||||
|
||||
bestMarketPathForPathType.push(bestMarketInfo.marketAddress);
|
||||
|
||||
if (bestMarketInfo.liquidity < pathTypeBestLiquidity) {
|
||||
pathTypeBestLiquidity = bestMarketInfo.liquidity;
|
||||
}
|
||||
|
||||
if (pathTypeBestLiquidity < bestLiquidity) {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pathTypeBestLiquidity > bestLiquidity) {
|
||||
bestLiquidity = pathTypeBestLiquidity;
|
||||
bestMarketPath = bestMarketPathForPathType;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMarketPath ? { path: bestMarketPath, liquidity: bestLiquidity } : undefined;
|
||||
}
|
||||
|
||||
export function getMaxLiquidityMarketForTokenEdge({
|
||||
markets,
|
||||
tokenInAddress,
|
||||
tokenOutAddress,
|
||||
getLiquidity,
|
||||
}: {
|
||||
markets: string[];
|
||||
tokenInAddress: string;
|
||||
tokenOutAddress: string;
|
||||
getLiquidity: MarketEdgeLiquidityGetter;
|
||||
}): {
|
||||
marketAddress: string;
|
||||
liquidity: bigint;
|
||||
} {
|
||||
let bestMarketAddress = markets[0];
|
||||
let bestLiquidity = 0n;
|
||||
|
||||
for (const market of markets) {
|
||||
const liquidity = getLiquidity({
|
||||
marketAddress: market,
|
||||
from: tokenInAddress,
|
||||
to: tokenOutAddress,
|
||||
});
|
||||
|
||||
if (liquidity > bestLiquidity) {
|
||||
bestLiquidity = liquidity;
|
||||
bestMarketAddress = market;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
marketAddress: bestMarketAddress,
|
||||
liquidity: bestLiquidity,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { NATIVE_TOKEN_ADDRESS } from "../../configs/tokens.js";
|
||||
import { MarketInfo, MarketsInfoData } from "../../types/markets.js";
|
||||
import { getByKey } from "../objects.js";
|
||||
import { getAvailableUsdLiquidityForCollateral, getOppositeCollateral, getTokenPoolType } from "../markets.js";
|
||||
import { SwapPathStats, SwapStats } from "../../types/trade.js";
|
||||
import { convertToTokenAmount, convertToUsd, getMidPrice } from "../tokens.js";
|
||||
import { applySwapImpactWithCap, getPriceImpactForSwap, getSwapFee } from "../fees/index.js";
|
||||
import { maxUint256 } from "viem";
|
||||
import {maxUint256} from "viem";
|
||||
|
||||
import {NATIVE_TOKEN_ADDRESS} from "../../configs/tokens.js";
|
||||
import {MarketInfo, MarketsInfoData} from "../../types/markets.js";
|
||||
import {SwapPathStats, SwapStats} from "../../types/trade.js";
|
||||
|
||||
import {getByKey} from "../objects.js";
|
||||
import {convertToTokenAmount, convertToUsd, getMidPrice} from "../tokens.js";
|
||||
import {getAvailableUsdLiquidityForCollateral, getOppositeCollateral, getTokenPoolType} from "../markets.js";
|
||||
import {getSwapFee} from "../fees/index.js";
|
||||
import {applySwapImpactWithCap, getPriceImpactForSwap} from "../fees/priceImpact.js";
|
||||
|
||||
export function getSwapCapacityUsd(marketInfo: MarketInfo, isLong: boolean) {
|
||||
const poolAmount = isLong ? marketInfo.longPoolAmount : marketInfo.shortPoolAmount;
|
||||
@@ -118,6 +121,7 @@ export function getSwapPathStats(p: {
|
||||
usdIn: bigint;
|
||||
shouldUnwrapNativeToken: boolean;
|
||||
shouldApplyPriceImpact: boolean;
|
||||
isAtomicSwap: boolean;
|
||||
}): SwapPathStats | undefined {
|
||||
const {
|
||||
marketsInfoData,
|
||||
@@ -127,6 +131,7 @@ export function getSwapPathStats(p: {
|
||||
shouldUnwrapNativeToken,
|
||||
shouldApplyPriceImpact,
|
||||
wrappedNativeTokenAddress,
|
||||
isAtomicSwap,
|
||||
} = p;
|
||||
|
||||
if (swapPath.length === 0) {
|
||||
@@ -147,7 +152,17 @@ export function getSwapPathStats(p: {
|
||||
const marketAddress = swapPath[i];
|
||||
const marketInfo = marketsInfoData[marketAddress];
|
||||
|
||||
tokenOutAddress = getOppositeCollateral(marketInfo, tokenInAddress)!.address;
|
||||
if (!marketInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const nextTokenOutAddress = getOppositeCollateral(marketInfo, tokenInAddress)?.address;
|
||||
|
||||
if (!nextTokenOutAddress) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
tokenOutAddress = nextTokenOutAddress;
|
||||
|
||||
if (i === swapPath.length - 1 && shouldUnwrapNativeToken && tokenOutAddress === wrappedNativeTokenAddress) {
|
||||
tokenOutAddress = NATIVE_TOKEN_ADDRESS;
|
||||
@@ -159,6 +174,7 @@ export function getSwapPathStats(p: {
|
||||
tokenOutAddress,
|
||||
usdIn: usdOut,
|
||||
shouldApplyPriceImpact,
|
||||
isAtomicSwap,
|
||||
});
|
||||
|
||||
tokenInAddress = swapStep.tokenOutAddress;
|
||||
@@ -178,7 +194,7 @@ export function getSwapPathStats(p: {
|
||||
|
||||
return {
|
||||
swapPath,
|
||||
tokenInAddress,
|
||||
tokenInAddress: initialCollateralAddress,
|
||||
tokenOutAddress,
|
||||
targetMarketAddress,
|
||||
swapSteps,
|
||||
@@ -196,8 +212,9 @@ export function getSwapStats(p: {
|
||||
tokenOutAddress: string;
|
||||
usdIn: bigint;
|
||||
shouldApplyPriceImpact: boolean;
|
||||
isAtomicSwap: boolean;
|
||||
}): SwapStats {
|
||||
const { marketInfo, tokenInAddress, tokenOutAddress, usdIn, shouldApplyPriceImpact } = p;
|
||||
const { marketInfo, tokenInAddress, tokenOutAddress, usdIn, shouldApplyPriceImpact, isAtomicSwap } = p;
|
||||
|
||||
const isWrap = tokenInAddress === NATIVE_TOKEN_ADDRESS;
|
||||
const isUnwrap = tokenOutAddress === NATIVE_TOKEN_ADDRESS;
|
||||
@@ -214,10 +231,19 @@ export function getSwapStats(p: {
|
||||
const amountIn = convertToTokenAmount(usdIn, tokenIn.decimals, priceIn)!;
|
||||
|
||||
let priceImpactDeltaUsd: bigint;
|
||||
let balanceWasImproved: boolean;
|
||||
|
||||
try {
|
||||
priceImpactDeltaUsd = getPriceImpactForSwap(marketInfo, tokenIn, tokenOut, usdIn, usdIn * -1n);
|
||||
const priceImpactValues = getPriceImpactForSwap(marketInfo, tokenIn, tokenOut, usdIn, usdIn * -1n);
|
||||
priceImpactDeltaUsd = priceImpactValues.priceImpactDeltaUsd;
|
||||
balanceWasImproved = priceImpactValues.balanceWasImproved;
|
||||
} catch (e) {
|
||||
// Approximate if the market would be out of capacity
|
||||
const capacityUsd = getSwapCapacityUsd(marketInfo, getTokenPoolType(marketInfo, tokenInAddress) === "long");
|
||||
const swapFeeUsd = getSwapFee(marketInfo, usdIn, false, isAtomicSwap);
|
||||
const usdInAfterFees = usdIn - swapFeeUsd;
|
||||
const isOutCapacity = capacityUsd < usdInAfterFees;
|
||||
|
||||
return {
|
||||
swapFeeUsd: 0n,
|
||||
swapFeeAmount: 0n,
|
||||
@@ -233,11 +259,12 @@ export function getSwapStats(p: {
|
||||
amountOut: 0n,
|
||||
usdOut: 0n,
|
||||
isOutLiquidity: true,
|
||||
isOutCapacity,
|
||||
};
|
||||
}
|
||||
|
||||
const swapFeeAmount = getSwapFee(marketInfo, amountIn, priceImpactDeltaUsd > 0);
|
||||
const swapFeeUsd = getSwapFee(marketInfo, usdIn, priceImpactDeltaUsd > 0);
|
||||
const swapFeeAmount = getSwapFee(marketInfo, amountIn, balanceWasImproved, isAtomicSwap);
|
||||
const swapFeeUsd = getSwapFee(marketInfo, usdIn, balanceWasImproved, isAtomicSwap);
|
||||
|
||||
const amountInAfterFees = amountIn - swapFeeAmount;
|
||||
const usdInAfterFees = usdIn - swapFeeUsd;
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import type {FindSwapPath, SwapAmounts} from "../../types/trade.js";
|
||||
import {SwapRoute} from "../../types/trade.js";
|
||||
import type {TokenData, TokensRatio} from "../../types/tokens.js";
|
||||
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT} from "../../configs/factors.js";
|
||||
import {MarketsInfoData} from "../../types/markets.js";
|
||||
import {InternalSwapStrategy, NoSwapStrategy} from "../../types/swapStrategy.js";
|
||||
import type {TokenData, TokensRatio} from "../../types/tokens.js";
|
||||
import type {FindSwapPath, SwapAmounts, SwapOptimizationOrderArray} from "../../types/trade.js";
|
||||
import {ExternalSwapQuoteParams, SwapRoute} from "../../types/trade.js";
|
||||
import {bigMath} from "../../utils/bigmath.js";
|
||||
import {getTotalSwapVolumeFromSwapStats} from "../../utils/fees/index.js";
|
||||
import {applyFactor} from "../../utils/numbers.js";
|
||||
import {
|
||||
convertToTokenAmount,
|
||||
convertToUsd,
|
||||
getAmountByRatio,
|
||||
getIsEquivalentTokens,
|
||||
getIsStake,
|
||||
getIsUnstake,
|
||||
} from "../../utils/tokens.js";
|
||||
|
||||
import {applyFactor} from "../numbers.js";
|
||||
import {bigMath} from "../bigmath.js";
|
||||
import {convertToTokenAmount, convertToUsd, getAmountByRatio, getIsEquivalentTokens} from "../tokens.js";
|
||||
import {getTotalSwapVolumeFromSwapStats} from "../fees/index.js";
|
||||
import {buildReverseSwapStrategy, buildSwapStrategy} from "./buildSwapStrategy.js";
|
||||
|
||||
export function getSwapAmountsByFromValue(p: {
|
||||
tokenIn: TokenData;
|
||||
@@ -15,7 +24,220 @@ export function getSwapAmountsByFromValue(p: {
|
||||
amountIn: bigint;
|
||||
triggerRatio?: TokensRatio;
|
||||
isLimit: boolean;
|
||||
swapOptimizationOrder?: Parameters<FindSwapPath>[1]["order"];
|
||||
swapOptimizationOrder?: SwapOptimizationOrderArray;
|
||||
allowedSwapSlippageBps?: bigint;
|
||||
uiFeeFactor: bigint;
|
||||
marketsInfoData: MarketsInfoData | undefined;
|
||||
chainId: number;
|
||||
externalSwapQuoteParams: ExternalSwapQuoteParams | undefined;
|
||||
findSwapPath: FindSwapPath;
|
||||
}): SwapAmounts {
|
||||
const {
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
amountIn,
|
||||
triggerRatio,
|
||||
isLimit,
|
||||
swapOptimizationOrder,
|
||||
uiFeeFactor,
|
||||
allowedSwapSlippageBps,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
} = p;
|
||||
|
||||
if (!externalSwapQuoteParams) {
|
||||
return getSwapAmountsByFromValueDefault(p);
|
||||
}
|
||||
|
||||
const swapStrategy = buildSwapStrategy({
|
||||
amountIn,
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
swapOptimizationOrder,
|
||||
externalSwapQuoteParams,
|
||||
});
|
||||
|
||||
const swapPathStats = swapStrategy.swapPathStats;
|
||||
|
||||
const totalSwapVolume = getTotalSwapVolumeFromSwapStats(swapPathStats?.swapSteps);
|
||||
const swapUiFeeUsd = applyFactor(totalSwapVolume, uiFeeFactor);
|
||||
const swapUiFeeAmount = convertToTokenAmount(swapUiFeeUsd, tokenOut.decimals, swapStrategy.priceOut)!;
|
||||
|
||||
const defaultAmounts: SwapAmounts = {
|
||||
amountIn,
|
||||
usdIn: swapStrategy.usdIn,
|
||||
amountOut: swapStrategy.amountOut,
|
||||
usdOut: swapStrategy.usdOut,
|
||||
minOutputAmount: swapStrategy.amountOut,
|
||||
priceIn: swapStrategy.priceIn,
|
||||
priceOut: swapStrategy.priceOut,
|
||||
swapStrategy,
|
||||
};
|
||||
|
||||
let amountOut = swapStrategy.amountOut;
|
||||
let usdOut = swapStrategy.usdOut;
|
||||
let minOutputAmount = 0n;
|
||||
|
||||
if (isLimit) {
|
||||
if (!triggerRatio) {
|
||||
return defaultAmounts;
|
||||
}
|
||||
|
||||
amountOut = getAmountByRatio({
|
||||
fromToken: tokenIn,
|
||||
toToken: tokenOut,
|
||||
fromTokenAmount: amountIn,
|
||||
ratio: triggerRatio.ratio,
|
||||
shouldInvertRatio: triggerRatio.largestToken.address === tokenOut.address,
|
||||
allowedSwapSlippageBps,
|
||||
});
|
||||
|
||||
usdOut = convertToUsd(amountOut, tokenOut.decimals, swapStrategy.priceOut)!;
|
||||
amountOut = convertToTokenAmount(usdOut, tokenOut.decimals, swapStrategy.priceOut)!;
|
||||
minOutputAmount = amountOut;
|
||||
} else {
|
||||
usdOut = swapStrategy.usdOut - swapUiFeeUsd;
|
||||
amountOut = swapStrategy.amountOut - swapUiFeeAmount;
|
||||
minOutputAmount = amountOut;
|
||||
}
|
||||
|
||||
if (amountOut < 0) {
|
||||
amountOut = 0n;
|
||||
usdOut = 0n;
|
||||
minOutputAmount = 0n;
|
||||
}
|
||||
|
||||
return {
|
||||
amountIn,
|
||||
usdIn: swapStrategy.usdIn,
|
||||
amountOut,
|
||||
usdOut,
|
||||
priceIn: swapStrategy.priceIn,
|
||||
priceOut: swapStrategy.priceOut,
|
||||
minOutputAmount,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
export function getSwapAmountsByToValue(p: {
|
||||
tokenIn: TokenData;
|
||||
tokenOut: TokenData;
|
||||
amountOut: bigint;
|
||||
triggerRatio?: TokensRatio;
|
||||
isLimit: boolean;
|
||||
swapOptimizationOrder?: SwapOptimizationOrderArray;
|
||||
allowedSwapSlippageBps?: bigint;
|
||||
uiFeeFactor: bigint;
|
||||
marketsInfoData: MarketsInfoData | undefined;
|
||||
chainId: number;
|
||||
externalSwapQuoteParams: ExternalSwapQuoteParams | undefined;
|
||||
findSwapPath: FindSwapPath;
|
||||
}): SwapAmounts {
|
||||
const {
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
amountOut,
|
||||
triggerRatio,
|
||||
isLimit,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
allowedSwapSlippageBps,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
} = p;
|
||||
|
||||
if (!externalSwapQuoteParams) {
|
||||
return getSwapAmountsByToValueDefault(p);
|
||||
}
|
||||
|
||||
const swapStrategy = buildReverseSwapStrategy({
|
||||
amountOut,
|
||||
tokenIn,
|
||||
tokenOut,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
swapOptimizationOrder,
|
||||
});
|
||||
|
||||
const uiFeeUsd = applyFactor(swapStrategy.usdIn, uiFeeFactor);
|
||||
|
||||
const defaultAmounts: SwapAmounts = {
|
||||
amountIn: swapStrategy.amountIn,
|
||||
usdIn: swapStrategy.usdIn,
|
||||
amountOut: swapStrategy.amountOut,
|
||||
usdOut: swapStrategy.usdOut,
|
||||
minOutputAmount: swapStrategy.amountOut,
|
||||
priceIn: swapStrategy.priceIn,
|
||||
priceOut: swapStrategy.priceOut,
|
||||
swapStrategy,
|
||||
};
|
||||
|
||||
if (!swapStrategy.swapPathStats) {
|
||||
return defaultAmounts;
|
||||
}
|
||||
|
||||
let amountIn = swapStrategy.amountIn;
|
||||
let usdIn = swapStrategy.usdIn;
|
||||
let minOutputAmount = 0n;
|
||||
|
||||
if (isLimit) {
|
||||
if (!triggerRatio) {
|
||||
return defaultAmounts;
|
||||
}
|
||||
|
||||
amountIn = getAmountByRatio({
|
||||
fromToken: tokenOut,
|
||||
toToken: tokenIn,
|
||||
fromTokenAmount: amountOut,
|
||||
ratio: triggerRatio.ratio,
|
||||
shouldInvertRatio: triggerRatio.largestToken.address === tokenIn.address,
|
||||
});
|
||||
|
||||
usdIn = convertToUsd(amountIn, tokenIn.decimals, swapStrategy.priceIn)!;
|
||||
if (allowedSwapSlippageBps !== undefined) {
|
||||
usdIn += bigMath.mulDiv(usdIn, allowedSwapSlippageBps ?? 0n, BASIS_POINTS_DIVISOR_BIGINT);
|
||||
} else {
|
||||
usdIn =
|
||||
usdIn +
|
||||
swapStrategy.swapPathStats.totalSwapFeeUsd +
|
||||
uiFeeUsd -
|
||||
swapStrategy.swapPathStats.totalSwapPriceImpactDeltaUsd;
|
||||
}
|
||||
amountIn = convertToTokenAmount(usdIn, tokenIn.decimals, swapStrategy.priceIn)!;
|
||||
} else {
|
||||
usdIn = swapStrategy.usdIn + uiFeeUsd;
|
||||
amountIn = convertToTokenAmount(usdIn, tokenIn.decimals, swapStrategy.priceIn)!;
|
||||
}
|
||||
|
||||
if (amountIn < 0) {
|
||||
amountIn = 0n;
|
||||
usdIn = 0n;
|
||||
}
|
||||
|
||||
return {
|
||||
amountIn: swapStrategy.amountIn,
|
||||
amountOut: swapStrategy.amountOut,
|
||||
usdIn,
|
||||
minOutputAmount,
|
||||
usdOut: swapStrategy.usdOut,
|
||||
priceIn: swapStrategy.priceIn,
|
||||
priceOut: swapStrategy.priceOut,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
function getSwapAmountsByFromValueDefault(p: {
|
||||
tokenIn: TokenData;
|
||||
tokenOut: TokenData;
|
||||
amountIn: bigint;
|
||||
triggerRatio?: TokensRatio;
|
||||
isLimit: boolean;
|
||||
swapOptimizationOrder?: SwapOptimizationOrderArray;
|
||||
allowedSwapSlippageBps?: bigint;
|
||||
findSwapPath: FindSwapPath;
|
||||
uiFeeFactor: bigint;
|
||||
@@ -41,6 +263,19 @@ export function getSwapAmountsByFromValue(p: {
|
||||
let usdOut = 0n;
|
||||
let minOutputAmount = 0n;
|
||||
|
||||
const defaultSwapStrategy: NoSwapStrategy = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
const defaultAmounts: SwapAmounts = {
|
||||
amountIn,
|
||||
usdIn,
|
||||
@@ -49,7 +284,7 @@ export function getSwapAmountsByFromValue(p: {
|
||||
minOutputAmount,
|
||||
priceIn,
|
||||
priceOut,
|
||||
swapPathStats: undefined,
|
||||
swapStrategy: defaultSwapStrategy,
|
||||
};
|
||||
|
||||
if (amountIn <= 0) {
|
||||
@@ -61,6 +296,19 @@ export function getSwapAmountsByFromValue(p: {
|
||||
usdOut = usdIn;
|
||||
minOutputAmount = amountOut;
|
||||
|
||||
const swapStrategy: NoSwapStrategy = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
return {
|
||||
amountIn,
|
||||
usdIn,
|
||||
@@ -69,23 +317,21 @@ export function getSwapAmountsByFromValue(p: {
|
||||
minOutputAmount,
|
||||
priceIn,
|
||||
priceOut,
|
||||
swapPathStats: undefined,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
const swapPathStats = findSwapPath(defaultAmounts.usdIn, { order: swapOptimizationOrder });
|
||||
if (getIsStake(tokenIn, tokenOut) || getIsUnstake(tokenIn, tokenOut)) {
|
||||
return getPlainSwapAmountsByFromToken(tokenIn, tokenOut, amountIn);
|
||||
}
|
||||
|
||||
console.log("🔍 Swap Path Stats Debug:");
|
||||
console.log(" - swapPathStats found:", !!swapPathStats);
|
||||
console.log(" - swapPathStats.swapPath:", swapPathStats?.swapPath);
|
||||
console.log(" - usdIn:", defaultAmounts.usdIn.toString());
|
||||
const swapPathStats = findSwapPath(defaultAmounts.usdIn, { order: swapOptimizationOrder });
|
||||
|
||||
const totalSwapVolume = getTotalSwapVolumeFromSwapStats(swapPathStats?.swapSteps);
|
||||
const swapUiFeeUsd = applyFactor(totalSwapVolume, uiFeeFactor);
|
||||
const swapUiFeeAmount = convertToTokenAmount(swapUiFeeUsd, tokenOut.decimals, priceOut)!;
|
||||
|
||||
if (!swapPathStats) {
|
||||
console.log("❌ No swap path found, returning default amounts");
|
||||
return defaultAmounts;
|
||||
}
|
||||
|
||||
@@ -118,6 +364,19 @@ export function getSwapAmountsByFromValue(p: {
|
||||
minOutputAmount = 0n;
|
||||
}
|
||||
|
||||
const swapStrategy: InternalSwapStrategy = {
|
||||
type: "internalSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: usdIn - usdOut,
|
||||
};
|
||||
|
||||
return {
|
||||
amountIn,
|
||||
usdIn,
|
||||
@@ -126,18 +385,18 @@ export function getSwapAmountsByFromValue(p: {
|
||||
priceIn,
|
||||
priceOut,
|
||||
minOutputAmount,
|
||||
swapPathStats,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
export function getSwapAmountsByToValue(p: {
|
||||
function getSwapAmountsByToValueDefault(p: {
|
||||
tokenIn: TokenData;
|
||||
tokenOut: TokenData;
|
||||
amountOut: bigint;
|
||||
triggerRatio?: TokensRatio;
|
||||
isLimit: boolean;
|
||||
findSwapPath: FindSwapPath;
|
||||
swapOptimizationOrder?: Parameters<FindSwapPath>[1]["order"];
|
||||
swapOptimizationOrder?: SwapOptimizationOrderArray;
|
||||
allowedSwapSlippageBps?: bigint;
|
||||
uiFeeFactor: bigint;
|
||||
}): SwapAmounts {
|
||||
@@ -164,6 +423,19 @@ export function getSwapAmountsByToValue(p: {
|
||||
let amountIn = 0n;
|
||||
let usdIn = 0n;
|
||||
|
||||
const defaultSwapStrategy: NoSwapStrategy = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
const defaultAmounts: SwapAmounts = {
|
||||
amountIn,
|
||||
usdIn,
|
||||
@@ -172,7 +444,7 @@ export function getSwapAmountsByToValue(p: {
|
||||
minOutputAmount,
|
||||
priceIn,
|
||||
priceOut,
|
||||
swapPathStats: undefined,
|
||||
swapStrategy: defaultSwapStrategy,
|
||||
};
|
||||
|
||||
if (amountOut <= 0) {
|
||||
@@ -183,6 +455,19 @@ export function getSwapAmountsByToValue(p: {
|
||||
amountIn = amountOut;
|
||||
usdIn = usdOut;
|
||||
|
||||
const swapStrategy: NoSwapStrategy = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
return {
|
||||
amountIn,
|
||||
usdIn,
|
||||
@@ -191,10 +476,14 @@ export function getSwapAmountsByToValue(p: {
|
||||
minOutputAmount,
|
||||
priceIn,
|
||||
priceOut,
|
||||
swapPathStats: undefined,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
if (getIsStake(tokenIn, tokenOut) || getIsUnstake(tokenIn, tokenOut)) {
|
||||
return getPlainSwapAmountsByToToken(tokenIn, tokenOut, amountOut);
|
||||
}
|
||||
|
||||
const baseUsdIn = usdOut;
|
||||
const swapPathStats = findSwapPath(baseUsdIn, { order: swapOptimizationOrder });
|
||||
|
||||
@@ -234,6 +523,19 @@ export function getSwapAmountsByToValue(p: {
|
||||
usdIn = 0n;
|
||||
}
|
||||
|
||||
const swapStrategy: InternalSwapStrategy = {
|
||||
type: "internalSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: usdIn - usdOut,
|
||||
};
|
||||
|
||||
return {
|
||||
amountIn,
|
||||
usdIn,
|
||||
@@ -242,11 +544,11 @@ export function getSwapAmountsByToValue(p: {
|
||||
minOutputAmount,
|
||||
priceIn,
|
||||
priceOut,
|
||||
swapPathStats,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
export function getSwapPathComparator(order: Parameters<FindSwapPath>[1]["order"]) {
|
||||
export function getSwapPathComparator(order?: SwapOptimizationOrderArray | undefined) {
|
||||
return function (a: SwapRoute, b: SwapRoute) {
|
||||
for (const field of order || []) {
|
||||
const isLiquidity = field === "liquidity";
|
||||
@@ -265,3 +567,67 @@ export function getSwapPathComparator(order: Parameters<FindSwapPath>[1]["order"
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
function getPlainSwapAmountsByFromToken(tokenIn: TokenData, tokenOut: TokenData, amountIn: bigint): SwapAmounts {
|
||||
const usdIn = convertToUsd(amountIn, tokenIn.decimals, tokenIn.prices.minPrice)!;
|
||||
const usdOut = usdIn;
|
||||
const amountOut = convertToTokenAmount(usdOut, tokenOut.decimals, tokenOut.prices.maxPrice)!;
|
||||
const priceIn = tokenIn.prices.minPrice;
|
||||
const priceOut = tokenOut.prices.maxPrice;
|
||||
|
||||
const swapStrategy: NoSwapStrategy = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
return {
|
||||
amountIn,
|
||||
usdIn,
|
||||
amountOut,
|
||||
usdOut,
|
||||
minOutputAmount: amountOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
function getPlainSwapAmountsByToToken(tokenIn: TokenData, tokenOut: TokenData, amountOut: bigint): SwapAmounts {
|
||||
const priceIn = tokenIn.prices.minPrice;
|
||||
const priceOut = tokenOut.prices.maxPrice;
|
||||
const usdOut = convertToUsd(amountOut, tokenOut.decimals, priceOut)!;
|
||||
const usdIn = usdOut;
|
||||
const amountIn = convertToTokenAmount(usdIn, tokenIn.decimals, priceIn)!;
|
||||
|
||||
const swapStrategy: NoSwapStrategy = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn,
|
||||
amountOut,
|
||||
usdIn,
|
||||
usdOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
return {
|
||||
amountIn,
|
||||
usdIn,
|
||||
amountOut,
|
||||
usdOut,
|
||||
minOutputAmount: amountOut,
|
||||
priceIn,
|
||||
priceOut,
|
||||
swapStrategy,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {createTestClient, http, publicActions, walletActions} from "viem";
|
||||
|
||||
import {ARBITRUM, getChain} from "../configs/chains.js";
|
||||
import {ARBITRUM, ContractsChainId, getViemChain} from "../configs/chains.js";
|
||||
|
||||
import {GmxSdk} from "../index.js";
|
||||
|
||||
const client = createTestClient({
|
||||
chain: getChain(ARBITRUM),
|
||||
chain: getViemChain(ARBITRUM),
|
||||
mode: "hardhat",
|
||||
transport: http(),
|
||||
})
|
||||
@@ -13,7 +13,7 @@ const client = createTestClient({
|
||||
.extend(walletActions);
|
||||
|
||||
export const arbitrumSdkConfig = {
|
||||
chainId: ARBITRUM,
|
||||
chainId: ARBITRUM as ContractsChainId,
|
||||
account: "0x9f7198eb1b9Ccc0Eb7A07eD228d8FbC12963ea33",
|
||||
oracleUrl: "https://arbitrum-api.gmxinfra.io",
|
||||
rpcUrl: "https://arb1.arbitrum.io/rpc",
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import {Token, TokenPrices, TokensData, TokensRatio, TokensRatioAndSlippage} from "../types/tokens.js";
|
||||
import {
|
||||
ContractPrice,
|
||||
Token,
|
||||
TokenData,
|
||||
TokenPrices,
|
||||
TokensData,
|
||||
TokensRatio,
|
||||
TokensRatioAndSlippage
|
||||
} from "../types/tokens.js";
|
||||
import {adjustForDecimals, expandDecimals, PRECISION} from "./numbers.js";
|
||||
import {NATIVE_TOKEN_ADDRESS} from "../configs/tokens.js";
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT, DEFAULT_ALLOWED_SWAP_SLIPPAGE_BPS} from "../configs/factors.js";
|
||||
@@ -8,8 +16,8 @@ export function parseContractPrice(price: bigint, tokenDecimals: number) {
|
||||
return price * expandDecimals(1, tokenDecimals);
|
||||
}
|
||||
|
||||
export function convertToContractPrice(price: bigint, tokenDecimals: number) {
|
||||
return price / expandDecimals(1, tokenDecimals);
|
||||
export function convertToContractPrice(price: bigint, tokenDecimals: number): ContractPrice {
|
||||
return (price / expandDecimals(1, tokenDecimals)) as ContractPrice;
|
||||
}
|
||||
|
||||
export function convertToContractTokenPrices(prices: TokenPrices, tokenDecimals: number) {
|
||||
@@ -43,6 +51,29 @@ export function convertToUsd(
|
||||
return (tokenAmount * price) / expandDecimals(1, tokenDecimals);
|
||||
}
|
||||
|
||||
export function convertBetweenTokens(
|
||||
tokenAmount: bigint | undefined,
|
||||
fromToken: TokenData | undefined,
|
||||
toToken: TokenData | undefined,
|
||||
maximize: boolean
|
||||
) {
|
||||
if (tokenAmount === undefined || fromToken === undefined || toToken === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (getIsEquivalentTokens(fromToken, toToken)) {
|
||||
return tokenAmount;
|
||||
}
|
||||
|
||||
const fromPrice = maximize ? fromToken.prices.maxPrice : fromToken.prices.minPrice;
|
||||
const toPrice = maximize ? toToken.prices.minPrice : toToken.prices.maxPrice;
|
||||
|
||||
const usd = convertToUsd(tokenAmount, fromToken.decimals, fromPrice)!;
|
||||
const amount = convertToTokenAmount(usd, toToken.decimals, toPrice)!;
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
export function getMidPrice(prices: TokenPrices) {
|
||||
return (prices.minPrice + prices.maxPrice) / 2n;
|
||||
}
|
||||
@@ -187,6 +218,15 @@ export function getIsUnwrap(token1: Token, token2: Token) {
|
||||
return token1.isWrapped && token2.isNative;
|
||||
}
|
||||
|
||||
export function getIsStake(token1: Token, token2: Token) {
|
||||
return (token1.isWrapped || token1.isNative) && token2.isStaking;
|
||||
}
|
||||
|
||||
export function getIsUnstake(token1: Token, token2: Token) {
|
||||
// can't unstake straight to native token
|
||||
return token1.isStaking && token2.isWrapped;
|
||||
}
|
||||
|
||||
export function getTokensRatioByPrice(p: {
|
||||
fromToken: Token;
|
||||
toToken: Token;
|
||||
|
||||
@@ -0,0 +1,727 @@
|
||||
import {DEFAULT_ACCEPTABLE_PRICE_IMPACT_BUFFER} from "../../configs/factors.js";
|
||||
import {MarketInfo} from "../../types/markets.js";
|
||||
import {DecreasePositionSwapType, OrderType} from "../../types/orders.js";
|
||||
import {PositionInfo, PositionInfoLoaded} from "../../types/positions.js";
|
||||
import {UserReferralInfo} from "../../types/referrals.js";
|
||||
import {TokenData} from "../../types/tokens.js";
|
||||
import {DecreasePositionAmounts, NextPositionValues} from "../../types/trade.js";
|
||||
import {bigMath} from "../../utils/bigmath.js";
|
||||
import {getPositionFee} from "../../utils/fees/index.js";
|
||||
import {
|
||||
applyFactor,
|
||||
BASIS_POINTS_DIVISOR_BIGINT,
|
||||
expandDecimals,
|
||||
getBasisPoints,
|
||||
MaxUint256,
|
||||
roundUpDivision,
|
||||
USD_DECIMALS,
|
||||
} from "../../utils/numbers.js";
|
||||
import {
|
||||
getLeverage,
|
||||
getLiquidationPrice,
|
||||
getMinCollateralFactorForPosition,
|
||||
getNetPriceImpactDeltaUsdForDecrease,
|
||||
getPositionPnlUsd,
|
||||
} from "../../utils/positions.js";
|
||||
import {
|
||||
getAcceptablePriceInfo,
|
||||
getDefaultAcceptablePriceImpactBps,
|
||||
getMarkPrice,
|
||||
getOrderThresholdType,
|
||||
} from "../../utils/prices.js";
|
||||
import {getSwapStats} from "../../utils/swap/index.js";
|
||||
import {convertToTokenAmount, convertToUsd, getIsEquivalentTokens} from "../../utils/tokens.js";
|
||||
|
||||
export function getDecreasePositionAmounts(p: {
|
||||
marketInfo: MarketInfo;
|
||||
collateralToken: TokenData;
|
||||
isLong: boolean;
|
||||
position: PositionInfoLoaded | undefined;
|
||||
closeSizeUsd: bigint;
|
||||
keepLeverage: boolean;
|
||||
triggerPrice?: bigint;
|
||||
fixedAcceptablePriceImpactBps?: bigint;
|
||||
acceptablePriceImpactBuffer?: number;
|
||||
userReferralInfo: UserReferralInfo | undefined;
|
||||
minCollateralUsd: bigint;
|
||||
minPositionSizeUsd: bigint;
|
||||
uiFeeFactor: bigint;
|
||||
isLimit?: boolean;
|
||||
limitPrice?: bigint;
|
||||
triggerOrderType?: DecreasePositionAmounts["triggerOrderType"];
|
||||
|
||||
receiveToken?: TokenData;
|
||||
}) {
|
||||
const {
|
||||
marketInfo,
|
||||
collateralToken,
|
||||
isLong,
|
||||
position,
|
||||
closeSizeUsd,
|
||||
keepLeverage,
|
||||
triggerPrice,
|
||||
fixedAcceptablePriceImpactBps,
|
||||
acceptablePriceImpactBuffer,
|
||||
userReferralInfo,
|
||||
minCollateralUsd,
|
||||
minPositionSizeUsd,
|
||||
uiFeeFactor,
|
||||
triggerOrderType: orderType,
|
||||
receiveToken: receiveTokenArg,
|
||||
} = p;
|
||||
|
||||
const { indexToken } = marketInfo;
|
||||
const receiveToken = receiveTokenArg ?? collateralToken;
|
||||
|
||||
const values: DecreasePositionAmounts = {
|
||||
isFullClose: false,
|
||||
sizeDeltaUsd: 0n,
|
||||
sizeDeltaInTokens: 0n,
|
||||
collateralDeltaUsd: 0n,
|
||||
collateralDeltaAmount: 0n,
|
||||
|
||||
indexPrice: 0n,
|
||||
collateralPrice: 0n,
|
||||
triggerPrice: 0n,
|
||||
acceptablePrice: 0n,
|
||||
|
||||
proportionalPendingImpactDeltaUsd: 0n,
|
||||
closePriceImpactDeltaUsd: 0n,
|
||||
totalPendingImpactDeltaUsd: 0n,
|
||||
priceImpactDiffUsd: 0n,
|
||||
balanceWasImproved: false,
|
||||
acceptablePriceDeltaBps: 0n,
|
||||
recommendedAcceptablePriceDeltaBps: 0n,
|
||||
|
||||
estimatedPnl: 0n,
|
||||
estimatedPnlPercentage: 0n,
|
||||
realizedPnl: 0n,
|
||||
realizedPnlPercentage: 0n,
|
||||
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
feeDiscountUsd: 0n,
|
||||
swapProfitFeeUsd: 0n,
|
||||
payedOutputUsd: 0n,
|
||||
payedRemainingCollateralUsd: 0n,
|
||||
payedRemainingCollateralAmount: 0n,
|
||||
|
||||
receiveTokenAmount: 0n,
|
||||
receiveUsd: 0n,
|
||||
|
||||
triggerOrderType: orderType,
|
||||
triggerThresholdType: undefined,
|
||||
decreaseSwapType: DecreasePositionSwapType.NoSwap,
|
||||
};
|
||||
|
||||
const pnlToken = isLong ? marketInfo.longToken : marketInfo.shortToken;
|
||||
|
||||
values.decreaseSwapType = getDecreaseSwapType(pnlToken, collateralToken, receiveToken);
|
||||
|
||||
const markPrice = getMarkPrice({ prices: indexToken.prices, isIncrease: false, isLong });
|
||||
const isTrigger = orderType !== undefined;
|
||||
|
||||
if (orderType) {
|
||||
values.triggerPrice = triggerPrice;
|
||||
values.indexPrice = triggerPrice ?? markPrice;
|
||||
|
||||
values.collateralPrice = getIsEquivalentTokens(indexToken, collateralToken)
|
||||
? triggerPrice ?? markPrice
|
||||
: collateralToken.prices.minPrice;
|
||||
|
||||
values.triggerThresholdType = getOrderThresholdType(orderType, isLong);
|
||||
} else {
|
||||
values.indexPrice = markPrice;
|
||||
values.collateralPrice = collateralToken.prices.minPrice;
|
||||
}
|
||||
|
||||
if (closeSizeUsd <= 0) {
|
||||
return values;
|
||||
}
|
||||
|
||||
values.sizeDeltaUsd = closeSizeUsd;
|
||||
|
||||
if (!position || position.sizeInUsd <= 0 || position.sizeInTokens <= 0) {
|
||||
applyAcceptablePrice({
|
||||
position,
|
||||
marketInfo,
|
||||
isLong,
|
||||
isTrigger,
|
||||
fixedAcceptablePriceImpactBps,
|
||||
acceptablePriceImpactBuffer,
|
||||
values,
|
||||
});
|
||||
|
||||
const positionFeeInfo = getPositionFee(
|
||||
marketInfo,
|
||||
values.sizeDeltaUsd,
|
||||
values.balanceWasImproved,
|
||||
userReferralInfo
|
||||
);
|
||||
|
||||
values.positionFeeUsd = positionFeeInfo.positionFeeUsd;
|
||||
values.feeDiscountUsd = positionFeeInfo.discountUsd;
|
||||
values.uiFeeUsd = applyFactor(values.sizeDeltaUsd, uiFeeFactor);
|
||||
|
||||
const totalFeesUsd = getTotalFeesUsdForDecrease({
|
||||
positionFeeUsd: values.positionFeeUsd,
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
swapProfitFeeUsd: 0n,
|
||||
swapUiFeeUsd: 0n,
|
||||
uiFeeUsd: values.uiFeeUsd,
|
||||
pnlUsd: 0n,
|
||||
totalPendingImpactDeltaUsd: values.totalPendingImpactDeltaUsd,
|
||||
});
|
||||
|
||||
values.payedOutputUsd = totalFeesUsd;
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
const estimatedCollateralUsd = convertToUsd(
|
||||
position.collateralAmount,
|
||||
collateralToken.decimals,
|
||||
values.collateralPrice
|
||||
)!;
|
||||
|
||||
let estimatedCollateralDeltaUsd = 0n;
|
||||
|
||||
if (keepLeverage) {
|
||||
estimatedCollateralDeltaUsd = bigMath.mulDiv(values.sizeDeltaUsd, estimatedCollateralUsd, position.sizeInUsd);
|
||||
}
|
||||
|
||||
values.isFullClose = getIsFullClose({
|
||||
position,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
indexPrice: values.indexPrice,
|
||||
remainingCollateralUsd: estimatedCollateralUsd - estimatedCollateralDeltaUsd,
|
||||
minCollateralUsd,
|
||||
minPositionSizeUsd,
|
||||
});
|
||||
|
||||
if (values.isFullClose) {
|
||||
values.sizeDeltaUsd = position.sizeInUsd;
|
||||
values.sizeDeltaInTokens = position.sizeInTokens;
|
||||
} else {
|
||||
if (position.isLong) {
|
||||
values.sizeDeltaInTokens = roundUpDivision(position.sizeInTokens * values.sizeDeltaUsd, position.sizeInUsd);
|
||||
} else {
|
||||
values.sizeDeltaInTokens = bigMath.mulDiv(position.sizeInTokens, values.sizeDeltaUsd, position.sizeInUsd);
|
||||
}
|
||||
}
|
||||
|
||||
// PNL
|
||||
values.estimatedPnl = getPositionPnlUsd({
|
||||
marketInfo,
|
||||
sizeInUsd: position.sizeInUsd,
|
||||
sizeInTokens: position.sizeInTokens,
|
||||
markPrice: values.indexPrice,
|
||||
isLong,
|
||||
});
|
||||
|
||||
values.realizedPnl = bigMath.mulDiv(values.estimatedPnl, values.sizeDeltaInTokens, position.sizeInTokens);
|
||||
values.realizedPnlPercentage =
|
||||
estimatedCollateralUsd !== 0n ? getBasisPoints(values.realizedPnl, estimatedCollateralUsd) : 0n;
|
||||
values.estimatedPnlPercentage =
|
||||
estimatedCollateralUsd !== 0n ? getBasisPoints(values.estimatedPnl, estimatedCollateralUsd) : 0n;
|
||||
|
||||
applyAcceptablePrice({
|
||||
position,
|
||||
marketInfo,
|
||||
isLong,
|
||||
isTrigger,
|
||||
fixedAcceptablePriceImpactBps,
|
||||
acceptablePriceImpactBuffer,
|
||||
values,
|
||||
});
|
||||
|
||||
// Profit
|
||||
let profitUsd = 0n;
|
||||
if (values.realizedPnl > 0) {
|
||||
profitUsd = profitUsd + values.realizedPnl;
|
||||
}
|
||||
if (values.totalPendingImpactDeltaUsd > 0) {
|
||||
profitUsd = profitUsd + values.totalPendingImpactDeltaUsd;
|
||||
}
|
||||
const profitAmount = convertToTokenAmount(profitUsd, collateralToken.decimals, values.collateralPrice)!;
|
||||
|
||||
// Fees
|
||||
const positionFeeInfo = getPositionFee(marketInfo, values.sizeDeltaUsd, values.balanceWasImproved, userReferralInfo);
|
||||
const estimatedPositionFeeCost = estimateCollateralCost(
|
||||
positionFeeInfo.positionFeeUsd,
|
||||
collateralToken,
|
||||
values.collateralPrice
|
||||
);
|
||||
const estimatedDiscountCost = estimateCollateralCost(
|
||||
positionFeeInfo.discountUsd,
|
||||
collateralToken,
|
||||
values.collateralPrice
|
||||
);
|
||||
|
||||
values.positionFeeUsd = estimatedPositionFeeCost.usd;
|
||||
values.feeDiscountUsd = estimatedDiscountCost.usd;
|
||||
values.uiFeeUsd = applyFactor(values.sizeDeltaUsd, uiFeeFactor);
|
||||
|
||||
const borrowFeeCost = estimateCollateralCost(
|
||||
position.pendingBorrowingFeesUsd,
|
||||
collateralToken,
|
||||
values.collateralPrice
|
||||
);
|
||||
|
||||
values.borrowingFeeUsd = borrowFeeCost.usd;
|
||||
|
||||
const fundingFeeCost = estimateCollateralCost(
|
||||
position.pendingFundingFeesUsd,
|
||||
collateralToken,
|
||||
values.collateralPrice
|
||||
);
|
||||
|
||||
values.fundingFeeUsd = fundingFeeCost.usd;
|
||||
|
||||
if (profitUsd > 0 && values.decreaseSwapType === DecreasePositionSwapType.SwapPnlTokenToCollateralToken) {
|
||||
const swapProfitStats = getSwapStats({
|
||||
marketInfo,
|
||||
tokenInAddress: pnlToken.address,
|
||||
tokenOutAddress: collateralToken.address,
|
||||
usdIn: profitUsd,
|
||||
shouldApplyPriceImpact: true,
|
||||
isAtomicSwap: false,
|
||||
});
|
||||
|
||||
values.swapProfitFeeUsd = swapProfitStats.swapFeeUsd - swapProfitStats.priceImpactDeltaUsd;
|
||||
values.swapUiFeeUsd = applyFactor(swapProfitStats.usdIn, uiFeeFactor);
|
||||
} else {
|
||||
values.swapProfitFeeUsd = 0n;
|
||||
}
|
||||
|
||||
const totalFeesUsd = getTotalFeesUsdForDecrease({
|
||||
positionFeeUsd: values.positionFeeUsd,
|
||||
borrowingFeeUsd: values.borrowingFeeUsd,
|
||||
fundingFeeUsd: values.fundingFeeUsd,
|
||||
swapProfitFeeUsd: values.swapProfitFeeUsd,
|
||||
swapUiFeeUsd: values.swapUiFeeUsd,
|
||||
uiFeeUsd: values.uiFeeUsd,
|
||||
pnlUsd: values.realizedPnl,
|
||||
totalPendingImpactDeltaUsd: values.totalPendingImpactDeltaUsd,
|
||||
});
|
||||
|
||||
const payedInfo = payForCollateralCost({
|
||||
initialCostUsd: totalFeesUsd,
|
||||
collateralToken,
|
||||
collateralPrice: values.collateralPrice,
|
||||
outputAmount: profitAmount,
|
||||
remainingCollateralAmount: position.collateralAmount,
|
||||
});
|
||||
|
||||
values.payedOutputUsd = convertToUsd(payedInfo.paidOutputAmount, collateralToken.decimals, values.collateralPrice)!;
|
||||
values.payedRemainingCollateralAmount = payedInfo.paidRemainingCollateralAmount;
|
||||
values.payedRemainingCollateralUsd = convertToUsd(
|
||||
payedInfo.paidRemainingCollateralAmount,
|
||||
collateralToken.decimals,
|
||||
values.collateralPrice
|
||||
)!;
|
||||
|
||||
// Collateral delta
|
||||
if (values.isFullClose) {
|
||||
values.collateralDeltaUsd = estimatedCollateralUsd;
|
||||
values.collateralDeltaAmount = position.collateralAmount;
|
||||
values.receiveTokenAmount = payedInfo.outputAmount + payedInfo.remainingCollateralAmount;
|
||||
} else if (
|
||||
keepLeverage &&
|
||||
position.sizeInUsd > 0 &&
|
||||
estimatedCollateralUsd > 0 &&
|
||||
payedInfo.remainingCollateralAmount > 0
|
||||
) {
|
||||
const remainingCollateralUsd = convertToUsd(
|
||||
payedInfo.remainingCollateralAmount,
|
||||
collateralToken.decimals,
|
||||
values.collateralPrice
|
||||
)!;
|
||||
const nextSizeInUsd = position.sizeInUsd - values.sizeDeltaUsd;
|
||||
const leverageWithoutPnl = getLeverage({
|
||||
sizeInUsd: position.sizeInUsd,
|
||||
collateralUsd: estimatedCollateralUsd,
|
||||
pendingBorrowingFeesUsd: position.pendingBorrowingFeesUsd,
|
||||
pendingFundingFeesUsd: position.pendingFundingFeesUsd,
|
||||
pnl: undefined,
|
||||
});
|
||||
|
||||
values.collateralDeltaUsd =
|
||||
/**
|
||||
* 1. @see https://app.asana.com/0/1204313444805313/1207549197964321/f
|
||||
* 2. leverageWithoutPnl may be zero if sizeInUsd is defaulted to 0n when position not ready yet
|
||||
*/
|
||||
leverageWithoutPnl !== undefined && leverageWithoutPnl !== 0n
|
||||
? remainingCollateralUsd - bigMath.mulDiv(nextSizeInUsd, BASIS_POINTS_DIVISOR_BIGINT, leverageWithoutPnl)
|
||||
: 0n;
|
||||
values.collateralDeltaAmount = convertToTokenAmount(
|
||||
values.collateralDeltaUsd,
|
||||
collateralToken.decimals,
|
||||
values.collateralPrice
|
||||
)!;
|
||||
values.receiveTokenAmount = payedInfo.outputAmount + values.collateralDeltaAmount;
|
||||
} else {
|
||||
values.collateralDeltaUsd = 0n;
|
||||
values.collateralDeltaAmount = 0n;
|
||||
values.receiveTokenAmount = payedInfo.outputAmount;
|
||||
}
|
||||
|
||||
values.receiveUsd = convertToUsd(values.receiveTokenAmount, collateralToken.decimals, values.collateralPrice)!;
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
export function getIsFullClose(p: {
|
||||
position: PositionInfoLoaded;
|
||||
sizeDeltaUsd: bigint;
|
||||
indexPrice: bigint;
|
||||
remainingCollateralUsd: bigint;
|
||||
minCollateralUsd: bigint;
|
||||
minPositionSizeUsd: bigint;
|
||||
}) {
|
||||
const { position, sizeDeltaUsd, indexPrice, remainingCollateralUsd, minCollateralUsd, minPositionSizeUsd } = p;
|
||||
const { marketInfo, isLong } = position;
|
||||
|
||||
if (position.sizeInUsd - sizeDeltaUsd < expandDecimals(1, USD_DECIMALS)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const estimatedPnl = getPositionPnlUsd({
|
||||
marketInfo,
|
||||
sizeInUsd: position.sizeInUsd,
|
||||
sizeInTokens: position.sizeInTokens,
|
||||
markPrice: indexPrice,
|
||||
isLong,
|
||||
});
|
||||
|
||||
const realizedPnl = bigMath.mulDiv(estimatedPnl, sizeDeltaUsd, position.sizeInUsd);
|
||||
|
||||
const estimatedRemainingPnl = estimatedPnl - realizedPnl;
|
||||
|
||||
if (realizedPnl < 0) {
|
||||
const estimatedRemainingCollateralUsd = remainingCollateralUsd - realizedPnl;
|
||||
|
||||
let minCollateralFactor = isLong
|
||||
? marketInfo.minCollateralFactorForOpenInterestLong
|
||||
: marketInfo.minCollateralFactorForOpenInterestShort;
|
||||
|
||||
const minCollateralFactorForMarket = marketInfo.minCollateralFactor;
|
||||
|
||||
if (minCollateralFactorForMarket > minCollateralFactor) {
|
||||
minCollateralFactor = minCollateralFactorForMarket;
|
||||
}
|
||||
|
||||
const minCollateralUsdForLeverage = applyFactor(position.sizeInUsd, minCollateralFactor);
|
||||
const willCollateralBeSufficient = estimatedRemainingCollateralUsd >= minCollateralUsdForLeverage;
|
||||
|
||||
if (!willCollateralBeSufficient) {
|
||||
if (
|
||||
estimatedRemainingCollateralUsd + estimatedRemainingPnl < minCollateralUsd ||
|
||||
position.sizeInUsd - sizeDeltaUsd < minPositionSizeUsd
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getMinCollateralUsdForLeverage(position: PositionInfoLoaded, openInterestDelta: bigint) {
|
||||
const minCollateralFactor = getMinCollateralFactorForPosition(position, openInterestDelta);
|
||||
return applyFactor(position.sizeInUsd, minCollateralFactor);
|
||||
}
|
||||
|
||||
export function payForCollateralCost(p: {
|
||||
initialCostUsd: bigint;
|
||||
collateralToken: TokenData;
|
||||
collateralPrice: bigint;
|
||||
outputAmount: bigint;
|
||||
remainingCollateralAmount: bigint;
|
||||
}) {
|
||||
const { initialCostUsd, collateralToken, collateralPrice, outputAmount, remainingCollateralAmount } = p;
|
||||
|
||||
const values = {
|
||||
outputAmount: BigInt(outputAmount),
|
||||
remainingCollateralAmount: BigInt(remainingCollateralAmount),
|
||||
paidOutputAmount: 0n,
|
||||
paidRemainingCollateralAmount: 0n,
|
||||
};
|
||||
|
||||
let remainingCostAmount = convertToTokenAmount(initialCostUsd, collateralToken.decimals, collateralPrice)!;
|
||||
|
||||
if (remainingCostAmount == 0n) {
|
||||
return values;
|
||||
}
|
||||
|
||||
if (values.outputAmount > 0) {
|
||||
if (values.outputAmount > remainingCostAmount) {
|
||||
values.outputAmount = values.outputAmount - remainingCostAmount;
|
||||
values.paidOutputAmount = remainingCostAmount;
|
||||
remainingCostAmount = 0n;
|
||||
} else {
|
||||
remainingCostAmount = remainingCostAmount - values.outputAmount;
|
||||
values.paidOutputAmount = values.outputAmount;
|
||||
values.outputAmount = 0n;
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingCostAmount == 0n) {
|
||||
return values;
|
||||
}
|
||||
|
||||
if (values.remainingCollateralAmount > remainingCostAmount) {
|
||||
values.remainingCollateralAmount = values.remainingCollateralAmount - remainingCostAmount;
|
||||
values.paidRemainingCollateralAmount = remainingCostAmount;
|
||||
remainingCostAmount = 0n;
|
||||
} else {
|
||||
remainingCostAmount = remainingCostAmount - remainingCollateralAmount;
|
||||
values.paidRemainingCollateralAmount = values.remainingCollateralAmount;
|
||||
values.remainingCollateralAmount = 0n;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
function applyAcceptablePrice(p: {
|
||||
position: PositionInfoLoaded | undefined;
|
||||
marketInfo: MarketInfo;
|
||||
isLong: boolean;
|
||||
isTrigger: boolean;
|
||||
fixedAcceptablePriceImpactBps?: bigint;
|
||||
acceptablePriceImpactBuffer?: number;
|
||||
values: DecreasePositionAmounts;
|
||||
}) {
|
||||
const {
|
||||
position,
|
||||
marketInfo,
|
||||
isLong,
|
||||
values,
|
||||
isTrigger,
|
||||
fixedAcceptablePriceImpactBps,
|
||||
acceptablePriceImpactBuffer,
|
||||
} = p;
|
||||
|
||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: false,
|
||||
isLimit: false,
|
||||
isLong,
|
||||
indexPrice: values.indexPrice,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
});
|
||||
|
||||
values.acceptablePrice = acceptablePriceInfo.acceptablePrice;
|
||||
values.acceptablePriceDeltaBps = acceptablePriceInfo.acceptablePriceDeltaBps;
|
||||
values.balanceWasImproved = acceptablePriceInfo.balanceWasImproved;
|
||||
|
||||
const totalImpactValues = getNetPriceImpactDeltaUsdForDecrease({
|
||||
marketInfo,
|
||||
sizeInUsd: position?.sizeInUsd ?? 0n,
|
||||
pendingImpactAmount: position?.pendingImpactAmount ?? 0n,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: acceptablePriceInfo.priceImpactDeltaUsd,
|
||||
});
|
||||
|
||||
values.closePriceImpactDeltaUsd = acceptablePriceInfo.priceImpactDeltaUsd;
|
||||
values.totalPendingImpactDeltaUsd = totalImpactValues.totalImpactDeltaUsd;
|
||||
values.proportionalPendingImpactDeltaUsd = totalImpactValues.proportionalPendingImpactDeltaUsd;
|
||||
values.priceImpactDiffUsd = totalImpactValues.priceImpactDiffUsd;
|
||||
|
||||
if (isTrigger) {
|
||||
if (values.triggerOrderType === OrderType.StopLossDecrease) {
|
||||
if (isLong) {
|
||||
values.acceptablePrice = 0n;
|
||||
} else {
|
||||
values.acceptablePrice = MaxUint256;
|
||||
}
|
||||
} else {
|
||||
let maxNegativePriceImpactBps = fixedAcceptablePriceImpactBps;
|
||||
values.recommendedAcceptablePriceDeltaBps = getDefaultAcceptablePriceImpactBps({
|
||||
isIncrease: false,
|
||||
isLong,
|
||||
indexPrice: values.indexPrice,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: values.closePriceImpactDeltaUsd,
|
||||
acceptablePriceImapctBuffer: acceptablePriceImpactBuffer || DEFAULT_ACCEPTABLE_PRICE_IMPACT_BUFFER,
|
||||
});
|
||||
|
||||
if (maxNegativePriceImpactBps === undefined) {
|
||||
maxNegativePriceImpactBps = values.recommendedAcceptablePriceDeltaBps;
|
||||
}
|
||||
|
||||
const triggerAcceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: false,
|
||||
isLimit: false,
|
||||
isLong,
|
||||
indexPrice: values.indexPrice,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
maxNegativePriceImpactBps,
|
||||
});
|
||||
|
||||
values.acceptablePrice = triggerAcceptablePriceInfo.acceptablePrice;
|
||||
values.acceptablePriceDeltaBps = triggerAcceptablePriceInfo.acceptablePriceDeltaBps;
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
export function estimateCollateralCost(baseUsd: bigint, collateralToken: TokenData, collateralPrice: bigint) {
|
||||
const amount = convertToTokenAmount(baseUsd, collateralToken.decimals, collateralToken.prices.minPrice)!;
|
||||
const usd = convertToUsd(amount, collateralToken.decimals, collateralPrice)!;
|
||||
|
||||
return {
|
||||
amount,
|
||||
usd,
|
||||
};
|
||||
}
|
||||
|
||||
export function getTotalFeesUsdForDecrease({
|
||||
positionFeeUsd,
|
||||
borrowingFeeUsd,
|
||||
fundingFeeUsd,
|
||||
swapProfitFeeUsd,
|
||||
swapUiFeeUsd,
|
||||
uiFeeUsd,
|
||||
pnlUsd,
|
||||
totalPendingImpactDeltaUsd,
|
||||
}: {
|
||||
positionFeeUsd: bigint;
|
||||
borrowingFeeUsd: bigint;
|
||||
fundingFeeUsd: bigint;
|
||||
swapProfitFeeUsd: bigint;
|
||||
swapUiFeeUsd: bigint;
|
||||
uiFeeUsd: bigint;
|
||||
pnlUsd: bigint;
|
||||
totalPendingImpactDeltaUsd: bigint;
|
||||
}) {
|
||||
const negativePriceImpactUsd = totalPendingImpactDeltaUsd < 0 ? bigMath.abs(totalPendingImpactDeltaUsd) : 0n;
|
||||
const negativePnlUsd = pnlUsd < 0 ? bigMath.abs(pnlUsd) : 0n;
|
||||
|
||||
const totalFeesUsd =
|
||||
positionFeeUsd +
|
||||
borrowingFeeUsd +
|
||||
fundingFeeUsd +
|
||||
swapProfitFeeUsd +
|
||||
swapUiFeeUsd +
|
||||
uiFeeUsd +
|
||||
negativePnlUsd +
|
||||
negativePriceImpactUsd;
|
||||
|
||||
return totalFeesUsd;
|
||||
}
|
||||
|
||||
export function getNextPositionValuesForDecreaseTrade(p: {
|
||||
existingPosition?: PositionInfo;
|
||||
marketInfo: MarketInfo;
|
||||
collateralToken: TokenData;
|
||||
sizeDeltaUsd: bigint;
|
||||
sizeDeltaInTokens: bigint;
|
||||
realizedPnl: bigint;
|
||||
estimatedPnl: bigint;
|
||||
collateralDeltaUsd: bigint;
|
||||
collateralDeltaAmount: bigint;
|
||||
payedRemainingCollateralUsd: bigint;
|
||||
payedRemainingCollateralAmount: bigint;
|
||||
proportionalPendingImpactDeltaUsd: bigint;
|
||||
showPnlInLeverage: boolean;
|
||||
isLong: boolean;
|
||||
minCollateralUsd: bigint;
|
||||
userReferralInfo: UserReferralInfo | undefined;
|
||||
}): NextPositionValues {
|
||||
const {
|
||||
existingPosition,
|
||||
marketInfo,
|
||||
collateralToken,
|
||||
sizeDeltaUsd,
|
||||
sizeDeltaInTokens,
|
||||
realizedPnl,
|
||||
estimatedPnl,
|
||||
collateralDeltaUsd,
|
||||
collateralDeltaAmount,
|
||||
payedRemainingCollateralUsd,
|
||||
payedRemainingCollateralAmount,
|
||||
showPnlInLeverage,
|
||||
proportionalPendingImpactDeltaUsd,
|
||||
isLong,
|
||||
minCollateralUsd,
|
||||
userReferralInfo,
|
||||
} = p;
|
||||
|
||||
const nextSizeUsd = existingPosition ? existingPosition.sizeInUsd - sizeDeltaUsd : 0n;
|
||||
const nextSizeInTokens = existingPosition ? existingPosition.sizeInTokens - sizeDeltaInTokens : 0n;
|
||||
|
||||
let nextCollateralUsd = existingPosition
|
||||
? existingPosition.collateralUsd - collateralDeltaUsd - payedRemainingCollateralUsd
|
||||
: 0n;
|
||||
|
||||
if (nextCollateralUsd < 0) {
|
||||
nextCollateralUsd = 0n;
|
||||
}
|
||||
|
||||
let nextCollateralAmount = existingPosition
|
||||
? existingPosition.collateralAmount - collateralDeltaAmount - payedRemainingCollateralAmount
|
||||
: 0n;
|
||||
|
||||
if (nextCollateralAmount < 0) {
|
||||
nextCollateralAmount = 0n;
|
||||
}
|
||||
|
||||
const nextPnl = estimatedPnl ? estimatedPnl - realizedPnl : 0n;
|
||||
|
||||
const nextPnlPercentage = nextCollateralUsd !== 0n ? getBasisPoints(nextPnl, nextCollateralUsd) : 0n;
|
||||
|
||||
const nextLeverage = getLeverage({
|
||||
sizeInUsd: nextSizeUsd,
|
||||
collateralUsd: nextCollateralUsd,
|
||||
pnl: showPnlInLeverage ? nextPnl : undefined,
|
||||
pendingBorrowingFeesUsd: 0n, // deducted on order
|
||||
pendingFundingFeesUsd: 0n, // deducted on order
|
||||
});
|
||||
|
||||
const nextLiqPrice = getLiquidationPrice({
|
||||
marketInfo,
|
||||
collateralToken,
|
||||
sizeInTokens: nextSizeInTokens,
|
||||
sizeInUsd: nextSizeUsd,
|
||||
collateralUsd: nextCollateralUsd,
|
||||
collateralAmount: nextCollateralAmount,
|
||||
minCollateralUsd,
|
||||
userReferralInfo,
|
||||
pendingImpactAmount: existingPosition?.pendingImpactAmount ?? 0n,
|
||||
pendingBorrowingFeesUsd: 0n, // deducted on order
|
||||
pendingFundingFeesUsd: 0n, // deducted on order
|
||||
isLong: isLong,
|
||||
});
|
||||
|
||||
const nextPendingImpactDeltaUsd =
|
||||
existingPosition?.pendingImpactUsd !== undefined && proportionalPendingImpactDeltaUsd !== undefined
|
||||
? existingPosition.pendingImpactUsd - proportionalPendingImpactDeltaUsd
|
||||
: 0n;
|
||||
|
||||
return {
|
||||
nextSizeUsd,
|
||||
nextCollateralUsd,
|
||||
nextLiqPrice,
|
||||
nextPnl,
|
||||
nextPnlPercentage,
|
||||
nextLeverage,
|
||||
nextPendingImpactDeltaUsd,
|
||||
};
|
||||
}
|
||||
|
||||
function getDecreaseSwapType(pnlToken: TokenData, collateralToken: TokenData, receiveToken: TokenData) {
|
||||
if (getIsEquivalentTokens(pnlToken, collateralToken)) {
|
||||
return DecreasePositionSwapType.NoSwap;
|
||||
} else if (getIsEquivalentTokens(pnlToken, receiveToken)) {
|
||||
return DecreasePositionSwapType.SwapCollateralTokenToPnlToken;
|
||||
} else {
|
||||
return DecreasePositionSwapType.SwapPnlTokenToCollateralToken;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,45 @@
|
||||
import { ExternalSwapQuote, FindSwapPath, TriggerThresholdType } from "../../types/trade.js";
|
||||
import {maxUint256} from "viem";
|
||||
|
||||
import { TokenData, TokensRatio } from "../../types/tokens.js";
|
||||
|
||||
import { BASIS_POINTS_DIVISOR_BIGINT } from "../../configs/factors.js";
|
||||
import { MarketInfo } from "../../types/markets.js";
|
||||
import { OrderType } from "../../types/orders.js";
|
||||
import { PositionInfo } from "../../types/positions.js";
|
||||
import { UserReferralInfo } from "../../types/referrals.js";
|
||||
import { IncreasePositionAmounts } from "../../types/trade.js";
|
||||
import { bigMath } from "../bigmath.js";
|
||||
import { getPositionFee, getPriceImpactForPosition, getTotalSwapVolumeFromSwapStats } from "../fees/index.js";
|
||||
import { applyFactor } from "../numbers.js";
|
||||
import { getLeverage } from "../positions.js";
|
||||
import {BASIS_POINTS_DIVISOR_BIGINT, DEFAULT_ACCEPTABLE_PRICE_IMPACT_BUFFER} from "../../configs/factors.js";
|
||||
import {MarketInfo, MarketsInfoData} from "../../types/markets.js";
|
||||
import {OrderType} from "../../types/orders.js";
|
||||
import {PositionInfo} from "../../types/positions.js";
|
||||
import {UserReferralInfo} from "../../types/referrals.js";
|
||||
import {ExternalSwapStrategy, NoSwapStrategy} from "../../types/swapStrategy.js";
|
||||
import {TokenData, TokensRatio} from "../../types/tokens.js";
|
||||
import {
|
||||
getAcceptablePriceInfo,
|
||||
getDefaultAcceptablePriceImpactBps,
|
||||
getMarkPrice,
|
||||
getOrderThresholdType,
|
||||
} from "../prices.js";
|
||||
import { getSwapAmountsByFromValue, getSwapAmountsByToValue } from "../swap/index.js";
|
||||
import { convertToTokenAmount, convertToUsd, getIsEquivalentTokens, getTokensRatioByPrice } from "../tokens.js";
|
||||
import { maxUint256 } from "viem";
|
||||
ExternalSwapQuote,
|
||||
ExternalSwapQuoteParams,
|
||||
FindSwapPath,
|
||||
IncreasePositionAmounts,
|
||||
NextPositionValues,
|
||||
SwapOptimizationOrderArray,
|
||||
TriggerThresholdType,
|
||||
} from "../../types/trade.js";
|
||||
import {bigMath} from "../../utils/bigmath.js";
|
||||
import {
|
||||
capPositionImpactUsdByMaxImpactPool,
|
||||
capPositionImpactUsdByMaxPriceImpactFactor,
|
||||
getPositionFee,
|
||||
getPriceImpactForPosition,
|
||||
getTotalSwapVolumeFromSwapStats,
|
||||
} from "../../utils/fees/index.js";
|
||||
import {applyFactor} from "../../utils/numbers.js";
|
||||
import {
|
||||
getEntryPrice,
|
||||
getLeverage,
|
||||
getLiquidationPrice,
|
||||
getPositionPnlUsd,
|
||||
getPriceImpactDiffUsd,
|
||||
} from "../../utils/positions.js";
|
||||
import {
|
||||
getAcceptablePriceInfo,
|
||||
getDefaultAcceptablePriceImpactBps,
|
||||
getMarkPrice,
|
||||
getOrderThresholdType,
|
||||
} from "../../utils/prices.js";
|
||||
import {getSwapAmountsByFromValue, getSwapAmountsByToValue} from "../../utils/swap/index.js";
|
||||
import {convertToTokenAmount, convertToUsd, getIsEquivalentTokens, getTokensRatioByPrice} from "../../utils/tokens.js";
|
||||
|
||||
type IncreasePositionParams = {
|
||||
marketInfo: MarketInfo;
|
||||
@@ -41,6 +60,9 @@ type IncreasePositionParams = {
|
||||
strategy: "leverageBySize" | "leverageByCollateral" | "independent";
|
||||
findSwapPath: FindSwapPath;
|
||||
uiFeeFactor: bigint;
|
||||
marketsInfoData: MarketsInfoData | undefined;
|
||||
chainId: number;
|
||||
externalSwapQuoteParams: ExternalSwapQuoteParams | undefined;
|
||||
};
|
||||
|
||||
export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreasePositionAmounts {
|
||||
@@ -63,8 +85,24 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
userReferralInfo,
|
||||
uiFeeFactor,
|
||||
strategy,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
} = p;
|
||||
|
||||
const swapStrategy: NoSwapStrategy = {
|
||||
type: "noSwap",
|
||||
externalSwapQuote: undefined,
|
||||
swapPathStats: undefined,
|
||||
amountIn: 0n,
|
||||
amountOut: 0n,
|
||||
usdIn: 0n,
|
||||
usdOut: 0n,
|
||||
priceIn: 0n,
|
||||
priceOut: 0n,
|
||||
feesUsd: 0n,
|
||||
};
|
||||
|
||||
const values: IncreasePositionAmounts = {
|
||||
initialCollateralAmount: 0n,
|
||||
initialCollateralUsd: 0n,
|
||||
@@ -72,8 +110,7 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
collateralDeltaAmount: 0n,
|
||||
collateralDeltaUsd: 0n,
|
||||
|
||||
swapPathStats: undefined,
|
||||
externalSwapQuote: undefined,
|
||||
swapStrategy,
|
||||
|
||||
indexTokenAmount: 0n,
|
||||
|
||||
@@ -88,6 +125,7 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
triggerPrice: 0n,
|
||||
acceptablePrice: 0n,
|
||||
acceptablePriceDeltaBps: 0n,
|
||||
recommendedAcceptablePriceDeltaBps: 0n,
|
||||
|
||||
positionFeeUsd: 0n,
|
||||
uiFeeUsd: 0n,
|
||||
@@ -103,7 +141,7 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
|
||||
const isLimit = limitOrderType !== undefined;
|
||||
|
||||
const swapOptimizationOrder: Parameters<FindSwapPath>[1]["order"] = isLimit ? ["length", "liquidity"] : undefined;
|
||||
const swapOptimizationOrder: SwapOptimizationOrderArray | undefined = isLimit ? ["length", "liquidity"] : undefined;
|
||||
|
||||
const prices = getIncreasePositionPrices({
|
||||
triggerPrice,
|
||||
@@ -143,32 +181,39 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
values.initialCollateralPrice
|
||||
)!;
|
||||
|
||||
values.externalSwapQuote = externalSwapQuote;
|
||||
const swapAmounts = getSwapAmountsByFromValue({
|
||||
tokenIn: initialCollateralToken,
|
||||
tokenOut: collateralToken,
|
||||
amountIn: initialCollateralAmount,
|
||||
isLimit: false,
|
||||
findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
});
|
||||
values.swapPathStats = swapAmounts.swapPathStats;
|
||||
if (externalSwapQuote) {
|
||||
const swapStrategy: ExternalSwapStrategy = {
|
||||
type: "externalSwap",
|
||||
externalSwapQuote,
|
||||
swapPathStats: undefined,
|
||||
...externalSwapQuote,
|
||||
};
|
||||
|
||||
const swapAmountOut = values.externalSwapQuote?.amountOut ?? swapAmounts.amountOut;
|
||||
values.swapStrategy = swapStrategy;
|
||||
} else {
|
||||
const swapAmounts = getSwapAmountsByFromValue({
|
||||
tokenIn: initialCollateralToken,
|
||||
tokenOut: collateralToken,
|
||||
amountIn: initialCollateralAmount,
|
||||
isLimit: false,
|
||||
findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
});
|
||||
|
||||
values.swapStrategy = swapAmounts.swapStrategy;
|
||||
}
|
||||
|
||||
const swapAmountOut = values.swapStrategy.amountOut;
|
||||
const baseCollateralUsd = convertToUsd(swapAmountOut, collateralToken.decimals, values.collateralPrice)!;
|
||||
const baseSizeDeltaUsd = bigMath.mulDiv(baseCollateralUsd, leverage, BASIS_POINTS_DIVISOR_BIGINT);
|
||||
const basePriceImpactDeltaUsd = getPriceImpactForPosition(marketInfo, baseSizeDeltaUsd, isLong);
|
||||
const basePositionFeeInfo = getPositionFee(
|
||||
marketInfo,
|
||||
baseSizeDeltaUsd,
|
||||
basePriceImpactDeltaUsd > 0,
|
||||
userReferralInfo
|
||||
);
|
||||
const { balanceWasImproved } = getPriceImpactForPosition(marketInfo, baseSizeDeltaUsd, isLong);
|
||||
const basePositionFeeInfo = getPositionFee(marketInfo, baseSizeDeltaUsd, balanceWasImproved, userReferralInfo);
|
||||
const baseUiFeeUsd = applyFactor(baseSizeDeltaUsd, uiFeeFactor);
|
||||
const totalSwapVolumeUsd = !values.externalSwapQuote
|
||||
? getTotalSwapVolumeFromSwapStats(values.swapPathStats?.swapSteps)
|
||||
: 0n;
|
||||
const totalSwapVolumeUsd = getTotalSwapVolumeFromSwapStats(values.swapStrategy.swapPathStats?.swapSteps);
|
||||
values.swapUiFeeUsd = applyFactor(totalSwapVolumeUsd, uiFeeFactor);
|
||||
|
||||
values.sizeDeltaUsd = bigMath.mulDiv(
|
||||
@@ -179,12 +224,7 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
|
||||
values.indexTokenAmount = convertToTokenAmount(values.sizeDeltaUsd, indexToken.decimals, values.indexPrice)!;
|
||||
|
||||
const positionFeeInfo = getPositionFee(
|
||||
marketInfo,
|
||||
values.sizeDeltaUsd,
|
||||
basePriceImpactDeltaUsd > 0,
|
||||
userReferralInfo
|
||||
);
|
||||
const positionFeeInfo = getPositionFee(marketInfo, values.sizeDeltaUsd, balanceWasImproved, userReferralInfo);
|
||||
values.positionFeeUsd = positionFeeInfo.positionFeeUsd;
|
||||
values.feeDiscountUsd = positionFeeInfo.discountUsd;
|
||||
values.uiFeeUsd = applyFactor(values.sizeDeltaUsd, uiFeeFactor);
|
||||
@@ -212,14 +252,9 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
values.indexTokenAmount = indexTokenAmount;
|
||||
values.sizeDeltaUsd = convertToUsd(indexTokenAmount, indexToken.decimals, values.indexPrice)!;
|
||||
|
||||
const basePriceImpactDeltaUsd = getPriceImpactForPosition(marketInfo, values.sizeDeltaUsd, isLong);
|
||||
const { balanceWasImproved } = getPriceImpactForPosition(marketInfo, values.sizeDeltaUsd, isLong);
|
||||
|
||||
const positionFeeInfo = getPositionFee(
|
||||
marketInfo,
|
||||
values.sizeDeltaUsd,
|
||||
basePriceImpactDeltaUsd > 0,
|
||||
userReferralInfo
|
||||
);
|
||||
const positionFeeInfo = getPositionFee(marketInfo, values.sizeDeltaUsd, balanceWasImproved, userReferralInfo);
|
||||
|
||||
values.positionFeeUsd = positionFeeInfo.positionFeeUsd;
|
||||
values.feeDiscountUsd = positionFeeInfo.discountUsd;
|
||||
@@ -241,18 +276,31 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
values.collateralDeltaUsd = collateralDeltaUsd;
|
||||
values.collateralDeltaAmount = collateralDeltaAmount;
|
||||
|
||||
values.externalSwapQuote = externalSwapQuote;
|
||||
if (externalSwapQuote) {
|
||||
const swapStrategy: ExternalSwapStrategy = {
|
||||
type: "externalSwap",
|
||||
externalSwapQuote,
|
||||
swapPathStats: undefined,
|
||||
...externalSwapQuote,
|
||||
};
|
||||
|
||||
const swapAmounts = getSwapAmountsByToValue({
|
||||
tokenIn: initialCollateralToken,
|
||||
tokenOut: collateralToken,
|
||||
amountOut: baseCollateralAmount,
|
||||
isLimit: false,
|
||||
findSwapPath,
|
||||
uiFeeFactor,
|
||||
});
|
||||
values.swapPathStats = swapAmounts.swapPathStats;
|
||||
const swapAmountIn = values.externalSwapQuote?.amountIn ?? swapAmounts.amountIn;
|
||||
values.swapStrategy = swapStrategy;
|
||||
} else {
|
||||
const swapAmounts = getSwapAmountsByToValue({
|
||||
tokenIn: initialCollateralToken,
|
||||
tokenOut: collateralToken,
|
||||
amountOut: baseCollateralAmount,
|
||||
isLimit: false,
|
||||
findSwapPath,
|
||||
uiFeeFactor,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
});
|
||||
values.swapStrategy = swapAmounts.swapStrategy;
|
||||
}
|
||||
|
||||
const swapAmountIn = values.swapStrategy.amountIn;
|
||||
|
||||
values.initialCollateralAmount = swapAmountIn;
|
||||
values.initialCollateralUsd = convertToUsd(
|
||||
@@ -265,15 +313,9 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
values.indexTokenAmount = indexTokenAmount;
|
||||
values.sizeDeltaUsd = convertToUsd(indexTokenAmount, indexToken.decimals, values.indexPrice)!;
|
||||
|
||||
const basePriceImpactDeltaUsd = getPriceImpactForPosition(marketInfo, values.sizeDeltaUsd, isLong);
|
||||
|
||||
const positionFeeInfo = getPositionFee(
|
||||
marketInfo,
|
||||
values.sizeDeltaUsd,
|
||||
basePriceImpactDeltaUsd > 0,
|
||||
userReferralInfo
|
||||
);
|
||||
const { balanceWasImproved } = getPriceImpactForPosition(marketInfo, values.sizeDeltaUsd, isLong);
|
||||
|
||||
const positionFeeInfo = getPositionFee(marketInfo, values.sizeDeltaUsd, balanceWasImproved, userReferralInfo);
|
||||
values.positionFeeUsd = positionFeeInfo.positionFeeUsd;
|
||||
values.feeDiscountUsd = positionFeeInfo.discountUsd;
|
||||
values.uiFeeUsd = applyFactor(values.sizeDeltaUsd, uiFeeFactor);
|
||||
@@ -287,21 +329,37 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
values.initialCollateralPrice
|
||||
)!;
|
||||
|
||||
values.externalSwapQuote = externalSwapQuote;
|
||||
if (externalSwapQuote) {
|
||||
const swapStrategy: ExternalSwapStrategy = {
|
||||
type: "externalSwap",
|
||||
externalSwapQuote,
|
||||
swapPathStats: undefined,
|
||||
...externalSwapQuote,
|
||||
};
|
||||
|
||||
const swapAmounts = getSwapAmountsByFromValue({
|
||||
tokenIn: initialCollateralToken,
|
||||
tokenOut: collateralToken,
|
||||
amountIn: initialCollateralAmount,
|
||||
isLimit: false,
|
||||
findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
});
|
||||
values.swapStrategy = swapStrategy;
|
||||
} else {
|
||||
const swapAmounts = getSwapAmountsByFromValue({
|
||||
tokenIn: initialCollateralToken,
|
||||
tokenOut: collateralToken,
|
||||
amountIn: initialCollateralAmount,
|
||||
isLimit: false,
|
||||
findSwapPath,
|
||||
uiFeeFactor,
|
||||
swapOptimizationOrder,
|
||||
marketsInfoData,
|
||||
chainId,
|
||||
externalSwapQuoteParams,
|
||||
});
|
||||
values.swapStrategy = swapAmounts.swapStrategy;
|
||||
}
|
||||
|
||||
values.swapPathStats = swapAmounts.swapPathStats;
|
||||
const swapAmountIn = values.externalSwapQuote?.amountIn ?? swapAmounts.amountIn;
|
||||
const baseCollateralUsd = convertToUsd(swapAmountIn, collateralToken.decimals, values.collateralPrice)!;
|
||||
const swapAmountIn = values.swapStrategy.amountIn;
|
||||
const baseCollateralUsd = convertToUsd(
|
||||
swapAmountIn,
|
||||
initialCollateralToken.decimals,
|
||||
values.initialCollateralPrice
|
||||
)!;
|
||||
|
||||
values.collateralDeltaUsd =
|
||||
baseCollateralUsd -
|
||||
@@ -330,12 +388,14 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
const acceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: true,
|
||||
isLimit,
|
||||
isLong,
|
||||
indexPrice: values.indexPrice,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
});
|
||||
|
||||
values.positionPriceImpactDeltaUsd = acceptablePriceInfo.priceImpactDeltaUsd;
|
||||
|
||||
values.acceptablePrice = acceptablePriceInfo.acceptablePrice;
|
||||
values.acceptablePriceDeltaBps = acceptablePriceInfo.acceptablePriceDeltaBps;
|
||||
|
||||
@@ -348,20 +408,23 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
}
|
||||
} else {
|
||||
let maxNegativePriceImpactBps = fixedAcceptablePriceImpactBps;
|
||||
values.recommendedAcceptablePriceDeltaBps = getDefaultAcceptablePriceImpactBps({
|
||||
isIncrease: true,
|
||||
isLong,
|
||||
indexPrice: values.indexPrice,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: values.positionPriceImpactDeltaUsd,
|
||||
acceptablePriceImapctBuffer: acceptablePriceImpactBuffer || DEFAULT_ACCEPTABLE_PRICE_IMPACT_BUFFER,
|
||||
});
|
||||
|
||||
if (maxNegativePriceImpactBps === undefined) {
|
||||
maxNegativePriceImpactBps = getDefaultAcceptablePriceImpactBps({
|
||||
isIncrease: true,
|
||||
isLong,
|
||||
indexPrice: values.indexPrice,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
priceImpactDeltaUsd: values.positionPriceImpactDeltaUsd,
|
||||
acceptablePriceImapctBuffer: acceptablePriceImpactBuffer,
|
||||
});
|
||||
maxNegativePriceImpactBps = values.recommendedAcceptablePriceDeltaBps;
|
||||
}
|
||||
|
||||
const limitAcceptablePriceInfo = getAcceptablePriceInfo({
|
||||
marketInfo,
|
||||
isIncrease: true,
|
||||
isLimit,
|
||||
isLong,
|
||||
indexPrice: values.indexPrice,
|
||||
sizeDeltaUsd: values.sizeDeltaUsd,
|
||||
@@ -373,24 +436,8 @@ export function getIncreasePositionAmounts(p: IncreasePositionParams): IncreaseP
|
||||
}
|
||||
}
|
||||
|
||||
let priceImpactAmount = 0n;
|
||||
|
||||
if (values.positionPriceImpactDeltaUsd > 0) {
|
||||
const price = triggerPrice !== undefined && triggerPrice > 0 ? triggerPrice : indexToken.prices.maxPrice;
|
||||
priceImpactAmount = convertToTokenAmount(values.positionPriceImpactDeltaUsd, indexToken.decimals, price)!;
|
||||
} else {
|
||||
const price = triggerPrice !== undefined && triggerPrice > 0 ? triggerPrice : indexToken.prices.minPrice;
|
||||
priceImpactAmount = convertToTokenAmount(values.positionPriceImpactDeltaUsd, indexToken.decimals, price)!;
|
||||
}
|
||||
|
||||
values.sizeDeltaInTokens = convertToTokenAmount(values.sizeDeltaUsd, indexToken.decimals, values.indexPrice)!;
|
||||
|
||||
if (isLong) {
|
||||
values.sizeDeltaInTokens = values.sizeDeltaInTokens + priceImpactAmount;
|
||||
} else {
|
||||
values.sizeDeltaInTokens = values.sizeDeltaInTokens - priceImpactAmount;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -515,3 +562,115 @@ export function getIncreasePositionPrices({
|
||||
triggerPrice,
|
||||
};
|
||||
}
|
||||
|
||||
export function getNextPositionValuesForIncreaseTrade(p: {
|
||||
existingPosition?: PositionInfo;
|
||||
marketInfo: MarketInfo;
|
||||
collateralToken: TokenData;
|
||||
positionPriceImpactDeltaUsd: bigint;
|
||||
sizeDeltaUsd: bigint;
|
||||
sizeDeltaInTokens: bigint;
|
||||
collateralDeltaUsd: bigint;
|
||||
collateralDeltaAmount: bigint;
|
||||
indexPrice: bigint;
|
||||
isLong: boolean;
|
||||
showPnlInLeverage: boolean;
|
||||
minCollateralUsd: bigint;
|
||||
userReferralInfo: UserReferralInfo | undefined;
|
||||
}): NextPositionValues {
|
||||
const {
|
||||
existingPosition,
|
||||
marketInfo,
|
||||
collateralToken,
|
||||
sizeDeltaUsd,
|
||||
sizeDeltaInTokens,
|
||||
collateralDeltaUsd,
|
||||
collateralDeltaAmount,
|
||||
indexPrice,
|
||||
isLong,
|
||||
showPnlInLeverage,
|
||||
minCollateralUsd,
|
||||
userReferralInfo,
|
||||
positionPriceImpactDeltaUsd,
|
||||
} = p;
|
||||
|
||||
const nextCollateralUsd = existingPosition ? existingPosition.collateralUsd + collateralDeltaUsd : collateralDeltaUsd;
|
||||
|
||||
const nextCollateralAmount = existingPosition
|
||||
? existingPosition.collateralAmount + collateralDeltaAmount
|
||||
: collateralDeltaAmount;
|
||||
|
||||
const nextSizeUsd = existingPosition ? existingPosition.sizeInUsd + sizeDeltaUsd : sizeDeltaUsd;
|
||||
const nextSizeInTokens = existingPosition ? existingPosition.sizeInTokens + sizeDeltaInTokens : sizeDeltaInTokens;
|
||||
|
||||
const nextEntryPrice =
|
||||
getEntryPrice({
|
||||
sizeInUsd: nextSizeUsd,
|
||||
sizeInTokens: nextSizeInTokens,
|
||||
indexToken: marketInfo.indexToken,
|
||||
}) ?? indexPrice;
|
||||
|
||||
const nextPnl = existingPosition
|
||||
? getPositionPnlUsd({
|
||||
marketInfo,
|
||||
sizeInUsd: nextSizeUsd,
|
||||
sizeInTokens: nextSizeInTokens,
|
||||
markPrice: indexPrice,
|
||||
isLong,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const nextLeverage = getLeverage({
|
||||
sizeInUsd: nextSizeUsd,
|
||||
collateralUsd: nextCollateralUsd,
|
||||
pnl: showPnlInLeverage ? nextPnl : undefined,
|
||||
pendingBorrowingFeesUsd: 0n, // deducted on order
|
||||
pendingFundingFeesUsd: 0n, // deducted on order
|
||||
});
|
||||
|
||||
const nextLiqPrice = getLiquidationPrice({
|
||||
marketInfo,
|
||||
collateralToken,
|
||||
sizeInUsd: nextSizeUsd,
|
||||
sizeInTokens: nextSizeInTokens,
|
||||
collateralUsd: nextCollateralUsd,
|
||||
collateralAmount: nextCollateralAmount,
|
||||
minCollateralUsd,
|
||||
pendingBorrowingFeesUsd: 0n, // deducted on order
|
||||
pendingFundingFeesUsd: 0n, // deducted on order
|
||||
pendingImpactAmount: existingPosition?.pendingImpactAmount ?? 0n,
|
||||
isLong: isLong,
|
||||
userReferralInfo,
|
||||
});
|
||||
|
||||
let nextPendingImpactDeltaUsd =
|
||||
existingPosition?.pendingImpactUsd !== undefined
|
||||
? existingPosition.pendingImpactUsd + positionPriceImpactDeltaUsd
|
||||
: positionPriceImpactDeltaUsd;
|
||||
|
||||
const potentialPriceImpactDiffUsd = getPriceImpactDiffUsd({
|
||||
totalImpactDeltaUsd: nextPendingImpactDeltaUsd,
|
||||
marketInfo,
|
||||
sizeDeltaUsd: nextSizeUsd,
|
||||
});
|
||||
|
||||
if (nextPendingImpactDeltaUsd > 0) {
|
||||
nextPendingImpactDeltaUsd = capPositionImpactUsdByMaxPriceImpactFactor(
|
||||
marketInfo,
|
||||
nextSizeUsd,
|
||||
nextPendingImpactDeltaUsd
|
||||
);
|
||||
}
|
||||
|
||||
nextPendingImpactDeltaUsd = capPositionImpactUsdByMaxImpactPool(marketInfo, nextPendingImpactDeltaUsd);
|
||||
|
||||
return {
|
||||
nextSizeUsd,
|
||||
nextCollateralUsd,
|
||||
nextEntryPrice,
|
||||
nextLeverage,
|
||||
nextLiqPrice,
|
||||
nextPendingImpactDeltaUsd,
|
||||
potentialPriceImpactDiffUsd,
|
||||
};
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { BASIS_POINTS_DIVISOR, BASIS_POINTS_DIVISOR_BIGINT } from "../../configs/factors.js";
|
||||
import { DecreasePositionSwapType } from "../../types/orders.js";
|
||||
import {BASIS_POINTS_DIVISOR, BASIS_POINTS_DIVISOR_BIGINT} from "../../configs/factors.js";
|
||||
import {DecreasePositionSwapType} from "../../types/orders.js";
|
||||
import {
|
||||
DecreasePositionAmounts,
|
||||
IncreasePositionAmounts,
|
||||
SwapAmounts,
|
||||
TradeFlags,
|
||||
TradeMode,
|
||||
TradeType,
|
||||
DecreasePositionAmounts,
|
||||
IncreasePositionAmounts,
|
||||
SwapAmounts,
|
||||
TradeFlags,
|
||||
TradeMode,
|
||||
TradeType,
|
||||
} from "../../types/trade.js";
|
||||
import { bigMath } from "../bigmath.js";
|
||||
import { getShouldUseMaxPrice } from "../prices.js";
|
||||
|
||||
import {bigMath} from "../bigmath.js";
|
||||
import {getShouldUseMaxPrice} from "../prices.js";
|
||||
|
||||
export function applySlippageToPrice(allowedSlippage: number, price: bigint, isIncrease: boolean, isLong: boolean) {
|
||||
const shouldIncreasePrice = getShouldUseMaxPrice(isIncrease, isLong);
|
||||
@@ -42,10 +43,10 @@ export function getSwapCount({
|
||||
}) {
|
||||
if (isSwap) {
|
||||
if (!swapAmounts) return undefined;
|
||||
return swapAmounts.swapPathStats?.swapPath.length ?? 0;
|
||||
return swapAmounts.swapStrategy.swapPathStats?.swapPath.length ?? 0;
|
||||
} else if (isIncrease) {
|
||||
if (!increaseAmounts) return undefined;
|
||||
return increaseAmounts.swapPathStats?.swapPath.length ?? 0;
|
||||
return increaseAmounts.swapStrategy.swapPathStats?.swapPath.length ?? 0;
|
||||
} else {
|
||||
if (decreaseAmounts?.decreaseSwapType === undefined) return undefined;
|
||||
return decreaseAmounts.decreaseSwapType !== DecreasePositionSwapType.NoSwap ? 1 : 0;
|
||||
@@ -60,7 +61,8 @@ export const createTradeFlags = (tradeType: TradeType, tradeMode: TradeMode): Tr
|
||||
const isMarket = tradeMode === TradeMode.Market;
|
||||
const isLimit = tradeMode === TradeMode.Limit || tradeMode === TradeMode.StopMarket;
|
||||
const isTrigger = tradeMode === TradeMode.Trigger;
|
||||
const isIncrease = isPosition && (isMarket || isLimit);
|
||||
const isTwap = tradeMode === TradeMode.Twap;
|
||||
const isIncrease = isPosition && (isMarket || isLimit || isTwap);
|
||||
|
||||
const tradeFlags: TradeFlags = {
|
||||
isLong,
|
||||
@@ -71,6 +73,7 @@ export const createTradeFlags = (tradeType: TradeType, tradeMode: TradeMode): Tr
|
||||
isMarket,
|
||||
isLimit,
|
||||
isTrigger,
|
||||
isTwap,
|
||||
};
|
||||
|
||||
return tradeFlags;
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
import {getAddress} from "viem";
|
||||
|
||||
import type {MarketsInfoData} from "../types/markets.js";
|
||||
import type {TradeAction as SubsquidTradeAction} from "../types/subsquid.js";
|
||||
import {Token, TokensData} from "../types/tokens.js";
|
||||
import type {PositionTradeAction, SwapTradeAction} from "../types/tradeHistory.js";
|
||||
import {TradeActionType} from "../types/tradeHistory.js";
|
||||
|
||||
import {getByKey} from "./objects.js";
|
||||
import {isIncreaseOrderType, isSwapOrderType} from "./orders.js";
|
||||
import {getSwapPathOutputAddresses} from "./swap/swapStats.js";
|
||||
import {parseContractPrice} from "./tokens.js";
|
||||
|
||||
export function createRawTradeActionTransformer(
|
||||
marketsInfoData: MarketsInfoData,
|
||||
wrappedToken: Token,
|
||||
tokensData: TokensData
|
||||
): (
|
||||
value: SubsquidTradeAction,
|
||||
index: number,
|
||||
array: SubsquidTradeAction[]
|
||||
) => SwapTradeAction | PositionTradeAction | undefined {
|
||||
return (rawAction) => {
|
||||
const orderType = Number(rawAction.orderType);
|
||||
|
||||
if (isSwapOrderType(orderType)) {
|
||||
const initialCollateralTokenAddress = getAddress(rawAction.initialCollateralTokenAddress!);
|
||||
const swapPath = rawAction.swapPath!.map((address) => getAddress(address));
|
||||
|
||||
const swapPathOutputAddresses = getSwapPathOutputAddresses({
|
||||
marketsInfoData,
|
||||
swapPath,
|
||||
initialCollateralAddress: initialCollateralTokenAddress,
|
||||
wrappedNativeTokenAddress: wrappedToken.address,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
isIncrease: false,
|
||||
});
|
||||
|
||||
const initialCollateralToken = getByKey(tokensData, initialCollateralTokenAddress)!;
|
||||
const targetCollateralToken = getByKey(tokensData, swapPathOutputAddresses.outTokenAddress)!;
|
||||
|
||||
if (!initialCollateralToken || !targetCollateralToken) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const tradeAction: SwapTradeAction = {
|
||||
id: rawAction.id,
|
||||
srcChainId: rawAction.srcChainId ? Number(rawAction.srcChainId) : undefined,
|
||||
eventName: rawAction.eventName as TradeActionType,
|
||||
account: rawAction.account,
|
||||
swapPath,
|
||||
orderType,
|
||||
orderKey: rawAction.orderKey,
|
||||
initialCollateralTokenAddress: rawAction.initialCollateralTokenAddress!,
|
||||
initialCollateralDeltaAmount: bigNumberify(rawAction.initialCollateralDeltaAmount)!,
|
||||
minOutputAmount: bigNumberify(rawAction.minOutputAmount)!,
|
||||
executionAmountOut: rawAction.executionAmountOut ? bigNumberify(rawAction.executionAmountOut) : undefined,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
targetCollateralToken,
|
||||
initialCollateralToken,
|
||||
timestamp: rawAction.timestamp,
|
||||
transaction: rawAction.transaction,
|
||||
reason: rawAction.reason ?? undefined,
|
||||
reasonBytes: rawAction.reasonBytes ?? undefined,
|
||||
twapParams:
|
||||
rawAction.twapGroupId && rawAction.numberOfParts
|
||||
? {
|
||||
twapGroupId: rawAction.twapGroupId,
|
||||
numberOfParts: rawAction.numberOfParts,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return tradeAction;
|
||||
} else {
|
||||
const marketAddress = getAddress(rawAction.marketAddress!);
|
||||
const marketInfo = getByKey(marketsInfoData, marketAddress);
|
||||
const indexToken = marketInfo?.indexToken;
|
||||
const initialCollateralTokenAddress = getAddress(rawAction.initialCollateralTokenAddress!);
|
||||
const swapPath = rawAction.swapPath!.map((address) => getAddress(address));
|
||||
const swapPathOutputAddresses = getSwapPathOutputAddresses({
|
||||
marketsInfoData,
|
||||
swapPath,
|
||||
initialCollateralAddress: initialCollateralTokenAddress,
|
||||
wrappedNativeTokenAddress: wrappedToken.address,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
isIncrease: isIncreaseOrderType(rawAction.orderType),
|
||||
});
|
||||
const initialCollateralToken = getByKey(tokensData, initialCollateralTokenAddress);
|
||||
const targetCollateralToken = getByKey(tokensData, swapPathOutputAddresses.outTokenAddress);
|
||||
|
||||
if (!marketInfo || !indexToken || !initialCollateralToken || !targetCollateralToken) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const tradeAction: PositionTradeAction = {
|
||||
id: rawAction.id,
|
||||
eventName: rawAction.eventName as TradeActionType,
|
||||
account: rawAction.account,
|
||||
marketAddress,
|
||||
marketInfo,
|
||||
srcChainId: rawAction.srcChainId ? Number(rawAction.srcChainId) : undefined,
|
||||
indexToken,
|
||||
swapPath,
|
||||
initialCollateralTokenAddress,
|
||||
initialCollateralToken,
|
||||
targetCollateralToken,
|
||||
initialCollateralDeltaAmount: bigNumberify(rawAction.initialCollateralDeltaAmount)!,
|
||||
sizeDeltaUsd: bigNumberify(rawAction.sizeDeltaUsd)!,
|
||||
triggerPrice: rawAction.triggerPrice
|
||||
? parseContractPrice(bigNumberify(rawAction.triggerPrice)!, indexToken.decimals)
|
||||
: undefined,
|
||||
acceptablePrice: parseContractPrice(bigNumberify(rawAction.acceptablePrice)!, indexToken.decimals),
|
||||
executionPrice: rawAction.executionPrice
|
||||
? parseContractPrice(bigNumberify(rawAction.executionPrice)!, indexToken.decimals)
|
||||
: undefined,
|
||||
minOutputAmount: bigNumberify(rawAction.minOutputAmount)!,
|
||||
|
||||
collateralTokenPriceMax: rawAction.collateralTokenPriceMax
|
||||
? parseContractPrice(bigNumberify(rawAction.collateralTokenPriceMax)!, initialCollateralToken.decimals)
|
||||
: undefined,
|
||||
|
||||
collateralTokenPriceMin: rawAction.collateralTokenPriceMin
|
||||
? parseContractPrice(bigNumberify(rawAction.collateralTokenPriceMin)!, initialCollateralToken.decimals)
|
||||
: undefined,
|
||||
|
||||
indexTokenPriceMin: rawAction.indexTokenPriceMin
|
||||
? parseContractPrice(BigInt(rawAction.indexTokenPriceMin), indexToken.decimals)
|
||||
: undefined,
|
||||
indexTokenPriceMax: rawAction.indexTokenPriceMax
|
||||
? parseContractPrice(BigInt(rawAction.indexTokenPriceMax), indexToken.decimals)
|
||||
: undefined,
|
||||
|
||||
orderType,
|
||||
orderKey: rawAction.orderKey,
|
||||
isLong: rawAction.isLong!,
|
||||
pnlUsd: rawAction.pnlUsd ? BigInt(rawAction.pnlUsd) : undefined,
|
||||
basePnlUsd: rawAction.basePnlUsd ? BigInt(rawAction.basePnlUsd) : undefined,
|
||||
|
||||
priceImpactDiffUsd: rawAction.priceImpactDiffUsd ? BigInt(rawAction.priceImpactDiffUsd) : undefined,
|
||||
priceImpactUsd: rawAction.priceImpactUsd ? BigInt(rawAction.priceImpactUsd) : undefined,
|
||||
totalImpactUsd: rawAction.totalImpactUsd ? BigInt(rawAction.totalImpactUsd) : undefined,
|
||||
positionFeeAmount: rawAction.positionFeeAmount ? BigInt(rawAction.positionFeeAmount) : undefined,
|
||||
borrowingFeeAmount: rawAction.borrowingFeeAmount ? BigInt(rawAction.borrowingFeeAmount) : undefined,
|
||||
fundingFeeAmount: rawAction.fundingFeeAmount ? BigInt(rawAction.fundingFeeAmount) : undefined,
|
||||
liquidationFeeAmount: rawAction.liquidationFeeAmount ? BigInt(rawAction.liquidationFeeAmount) : undefined,
|
||||
|
||||
reason: rawAction.reason ?? undefined,
|
||||
reasonBytes: rawAction.reasonBytes ?? undefined,
|
||||
|
||||
transaction: rawAction.transaction,
|
||||
timestamp: rawAction.timestamp,
|
||||
shouldUnwrapNativeToken: rawAction.shouldUnwrapNativeToken!,
|
||||
twapParams:
|
||||
rawAction.twapGroupId && rawAction.numberOfParts
|
||||
? {
|
||||
twapGroupId: rawAction.twapGroupId,
|
||||
numberOfParts: rawAction.numberOfParts,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return tradeAction;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function bigNumberify(n?: bigint | string | null | undefined) {
|
||||
try {
|
||||
if (n === undefined) throw new Error("n is undefined");
|
||||
if (n === null) throw new Error("n is null");
|
||||
|
||||
return BigInt(n);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("bigNumberify error", e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,11 @@ import {GmxSdk} from '../../generated/gmxsdk/index.js'
|
||||
|
||||
import {arbitrum} from 'viem/chains';
|
||||
import {getTokenBySymbol} from '../../generated/gmxsdk/configs/tokens.js';
|
||||
import {
|
||||
Position,
|
||||
PositionStatus,
|
||||
Trade,
|
||||
TradeDirection,
|
||||
TradeStatus,
|
||||
TradeType
|
||||
} from '../../generated/ManagingApiTypes.js';
|
||||
import {createSwapOrderTxn} from '../../generated/gmxsdk/modules/orders/transactions/createSwapOrderTxn.js';
|
||||
|
||||
import {MarketInfo, MarketsInfoData} from '../../generated/gmxsdk/types/markets.js';
|
||||
import {MarketConfig, MARKETS} from '../../generated/gmxsdk/configs/markets.js'
|
||||
import {ARBITRUM} from '../../generated/gmxsdk/configs/chains.js'
|
||||
import {ARBITRUM, ContractsChainId} from '../../generated/gmxsdk/configs/chains.js'
|
||||
import {TokenData, TokensData} from '../../generated/gmxsdk/types/tokens.js';
|
||||
import {getByKey} from '../../generated/gmxsdk/utils/objects.js';
|
||||
import {GmxSdkConfig} from '../../generated/gmxsdk/types/sdk.js';
|
||||
@@ -35,9 +29,17 @@ import {handleError} from '../../utils/errorHandler.js';
|
||||
import {Abi, zeroHash} from 'viem';
|
||||
import {CLAIMABLE_FUNDING_AMOUNT} from '../../generated/gmxsdk/configs/dataStore.js';
|
||||
import {hashDataMap, hashString} from '../../generated/gmxsdk/utils/hash.js';
|
||||
import {getContract} from '../../generated/gmxsdk/configs/contracts.js';
|
||||
import {ContractName, getContract} from '../../generated/gmxsdk/configs/contracts.js';
|
||||
import {abis} from '../../generated/gmxsdk/abis/index.js';
|
||||
import {approveContractImpl, getTokenAllowance} from './privy.js';
|
||||
import {
|
||||
Position,
|
||||
PositionStatus,
|
||||
Trade,
|
||||
TradeDirection,
|
||||
TradeStatus,
|
||||
TradeType
|
||||
} from '../../generated/ManagingApiTypes.js';
|
||||
|
||||
// Cache implementation for markets info data
|
||||
interface CacheEntry {
|
||||
@@ -50,12 +52,53 @@ interface CacheEntry {
|
||||
|
||||
const CACHE_TTL = 30 * 60 * 1000; // 30 minutes in milliseconds
|
||||
const marketsCache = new Map<string, CacheEntry>();
|
||||
const MAX_CACHE_SIZE = 5; // Limit cache size to prevent memory issues
|
||||
const OPERATION_TIMEOUT = 30000; // 30 seconds timeout for operations
|
||||
|
||||
const MEMORY_WARNING_THRESHOLD = 0.8; // Warn when memory usage exceeds 80%
|
||||
|
||||
// Memory monitoring function
|
||||
function checkMemoryUsage() {
|
||||
const used = process.memoryUsage();
|
||||
const total = used.heapTotal;
|
||||
const usage = used.heapUsed / total;
|
||||
|
||||
if (usage > MEMORY_WARNING_THRESHOLD) {
|
||||
console.warn(`⚠️ High memory usage detected: ${(usage * 100).toFixed(1)}% (${Math.round(used.heapUsed / 1024 / 1024)}MB / ${Math.round(total / 1024 / 1024)}MB)`);
|
||||
|
||||
// Clear cache if memory usage is too high
|
||||
if (usage > 0.9) {
|
||||
console.warn(`🧹 Clearing markets cache due to high memory usage`);
|
||||
marketsCache.clear();
|
||||
pendingRequests.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback RPC configuration
|
||||
const FALLBACK_RPC_URL = "https://radial-shy-cherry.arbitrum-mainnet.quiknode.pro/098e57e961b05b24bcde008c4ca02fff6fb13b51/";
|
||||
const PRIMARY_RPC_URL = "https://arb1.arbitrum.io/rpc";
|
||||
const MAX_RETRY_ATTEMPTS = 2; // Only allow one retry to prevent infinite loops
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper function to add timeout to operations
|
||||
* @param operation The operation to execute
|
||||
* @param timeoutMs Timeout in milliseconds
|
||||
* @returns Promise that rejects if timeout is exceeded
|
||||
*/
|
||||
async function withTimeout<T>(
|
||||
operation: Promise<T>,
|
||||
timeoutMs: number
|
||||
): Promise<T> {
|
||||
return Promise.race([
|
||||
operation,
|
||||
new Promise<never>((_, reject) =>
|
||||
setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs)
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic fallback method for GMX operations that can retry with a fallback RPC
|
||||
* @param operation The operation function to execute
|
||||
@@ -70,13 +113,20 @@ async function executeWithFallback<T>(
|
||||
): Promise<T> {
|
||||
try {
|
||||
console.log(`🔄 Executing operation with retry count: ${retryCount}, RPC: ${retryCount === 0 ? 'Primary' : 'Fallback'}`);
|
||||
return await operation(sdk, retryCount);
|
||||
return await withTimeout(operation(sdk, retryCount), OPERATION_TIMEOUT);
|
||||
} catch (error) {
|
||||
// Check if this is a multicall timeout error and we haven't exceeded retry limit
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
const isMulticallTimeout = errorMessage.toLowerCase().includes('multicall timeout') ||
|
||||
errorMessage.toLowerCase().includes('timeout') ||
|
||||
errorMessage.toLowerCase().includes('rpc error');
|
||||
errorMessage.toLowerCase().includes('rpc error') ||
|
||||
errorMessage.toLowerCase().includes('network error') ||
|
||||
errorMessage.toLowerCase().includes('rate limit error') ||
|
||||
errorMessage.toLowerCase().includes('http request failed') ||
|
||||
errorMessage.toLowerCase().includes('503') ||
|
||||
errorMessage.toLowerCase().includes('allocation failed') ||
|
||||
errorMessage.toLowerCase().includes('heap out of memory') ||
|
||||
errorMessage.toLowerCase().includes('contract function execution error');
|
||||
|
||||
if (isMulticallTimeout && retryCount < MAX_RETRY_ATTEMPTS) {
|
||||
console.log(`🔄 Multicall timeout detected (attempt ${retryCount + 1}/${MAX_RETRY_ATTEMPTS + 1}), retrying with fallback RPC...`);
|
||||
@@ -113,7 +163,13 @@ async function executeWithFallback<T>(
|
||||
* @param sdk The GMX SDK client
|
||||
* @returns Markets info data and tokens data
|
||||
*/
|
||||
// Add a promise cache to prevent concurrent calls to the same endpoint
|
||||
const pendingRequests = new Map<string, Promise<{ marketsInfoData: MarketsInfoData; tokensData: TokensData }>>();
|
||||
|
||||
async function getMarketsInfoWithCache(sdk: GmxSdk): Promise<{ marketsInfoData: MarketsInfoData; tokensData: TokensData }> {
|
||||
// Check memory usage before proceeding
|
||||
checkMemoryUsage();
|
||||
|
||||
const cacheKey = `markets_${sdk.chainId}`;
|
||||
const now = Date.now();
|
||||
const cached = marketsCache.get(cacheKey);
|
||||
@@ -125,18 +181,118 @@ async function getMarketsInfoWithCache(sdk: GmxSdk): Promise<{ marketsInfoData:
|
||||
return cached.data as { marketsInfoData: MarketsInfoData; tokensData: TokensData };
|
||||
}
|
||||
|
||||
const data = await sdk.markets.getMarketsInfo();
|
||||
|
||||
if (!data.marketsInfoData || !data.tokensData) {
|
||||
throw new Error("Invalid response from GMX: missing markets or tokens info");
|
||||
// Check if there's already a pending request for this chain
|
||||
if (pendingRequests.has(cacheKey)) {
|
||||
return pendingRequests.get(cacheKey)!;
|
||||
}
|
||||
|
||||
marketsCache.set(cacheKey, {
|
||||
data: data as { marketsInfoData: MarketsInfoData; tokensData: TokensData },
|
||||
timestamp: now
|
||||
});
|
||||
// Create a new request and cache the promise
|
||||
const requestPromise = (async () => {
|
||||
try {
|
||||
console.log(`🔄 Fetching markets info for chain ${sdk.chainId}...`);
|
||||
const data = await withTimeout(sdk.markets.getMarketsInfo(), OPERATION_TIMEOUT);
|
||||
|
||||
return data as { marketsInfoData: MarketsInfoData; tokensData: TokensData };
|
||||
if (!data.marketsInfoData || !data.tokensData) {
|
||||
throw new Error("Invalid response from GMX: missing markets or tokens info");
|
||||
}
|
||||
|
||||
// Implement cache size limit to prevent memory issues
|
||||
if (marketsCache.size >= MAX_CACHE_SIZE) {
|
||||
// Remove the oldest entry
|
||||
const oldestKey = marketsCache.keys().next().value;
|
||||
marketsCache.delete(oldestKey);
|
||||
}
|
||||
|
||||
marketsCache.set(cacheKey, {
|
||||
data: data as { marketsInfoData: MarketsInfoData; tokensData: TokensData },
|
||||
timestamp: now
|
||||
});
|
||||
|
||||
console.log(`✅ Markets info cached for chain ${sdk.chainId}`);
|
||||
return data as { marketsInfoData: MarketsInfoData; tokensData: TokensData };
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to fetch markets info for chain ${sdk.chainId}:`, error);
|
||||
|
||||
// If RPC is failing, return empty data to prevent memory issues
|
||||
const emptyData = {
|
||||
marketsInfoData: {} as MarketsInfoData,
|
||||
tokensData: {} as TokensData
|
||||
};
|
||||
|
||||
// Cache the empty data for a shorter time to allow retries
|
||||
marketsCache.set(cacheKey, {
|
||||
data: emptyData,
|
||||
timestamp: now - (CACHE_TTL - 60000) // Cache for only 1 minute
|
||||
});
|
||||
|
||||
console.log(`⚠️ Returning empty markets data for chain ${sdk.chainId} due to RPC failure`);
|
||||
return emptyData;
|
||||
} finally {
|
||||
// Remove the pending request when done
|
||||
pendingRequests.delete(cacheKey);
|
||||
}
|
||||
})();
|
||||
|
||||
pendingRequests.set(cacheKey, requestPromise);
|
||||
return requestPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper to approve a contract for a token
|
||||
* @param sdk The GMX SDK client
|
||||
* @param fromTicker The token ticker symbol
|
||||
* @param fromTokenData The token data
|
||||
* @param fromTokenAmount The amount to approve
|
||||
* @param contractName The contract name to approve
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async function approveTokenForContract(
|
||||
sdk: GmxSdk,
|
||||
fromTicker: string,
|
||||
fromTokenData: TokenData,
|
||||
fromTokenAmount: bigint,
|
||||
contractName: string
|
||||
): Promise<void> {
|
||||
// If ticker ETH no need to check allowance
|
||||
if (fromTicker === "ETH") {
|
||||
console.log(`✅ ETH no need to check allowance`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const contractAddress = getContract(sdk.chainId, contractName as ContractName);
|
||||
|
||||
const currentAllowance = await getTokenAllowance(
|
||||
sdk.account,
|
||||
fromTokenData.address,
|
||||
contractAddress
|
||||
);
|
||||
|
||||
console.log(`Current allowance for ${fromTicker}:`, currentAllowance);
|
||||
console.log(`Required amount:`, fromTokenAmount);
|
||||
|
||||
if (currentAllowance < fromTokenAmount) {
|
||||
console.log(`🔧 Insufficient allowance for ${fromTicker}. Auto-approving ${contractName}...`);
|
||||
|
||||
// Approve maximum amount (2^256 - 1) to avoid future approval transactions
|
||||
const maxApprovalAmount = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
const approvalHash = await approveContractImpl(
|
||||
sdk.account,
|
||||
fromTokenData.address,
|
||||
contractAddress,
|
||||
sdk.chainId,
|
||||
maxApprovalAmount
|
||||
);
|
||||
|
||||
console.log(`✅ Token approval successful! Hash: ${approvalHash}`);
|
||||
console.log(`📝 Approved maximum amount ${fromTicker} for ${contractName}`);
|
||||
} else {
|
||||
console.log(`✅ Sufficient allowance already exists for ${fromTicker}`);
|
||||
}
|
||||
} catch (allowanceError) {
|
||||
console.warn('Could not check or approve token allowance:', allowanceError);
|
||||
throw new Error(`Failed to handle token allowance: ${allowanceError instanceof Error ? allowanceError.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,57 +406,13 @@ export async function createGmxClientWithRpc(
|
||||
oracleUrl: "https://arbitrum-api.gmxinfra.io",
|
||||
rpcUrl: rpcUrl,
|
||||
subsquidUrl: "https://gmx.squids.live/gmx-synthetics-arbitrum:prod/api/graphql",
|
||||
subgraphUrl: "https://subgraph.satsuma-prod.com/3b2ced13c8d9/gmx/synthetics-arbitrum-stats/api",
|
||||
settings: {
|
||||
uiFeeReceiverAccount: process.env.GMX_UI_FEE_RECEIVER
|
||||
},
|
||||
markets: {
|
||||
"0x4D3Eb91efd36C2b74181F34B111bc1E91a0d0cb4": {
|
||||
isListed: false,
|
||||
},
|
||||
"0xdf034cd3df9a80eABFA0556232a91E03Ca67D5Cb": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x9e79146b3A022Af44E0708c6794F03Ef798381A5": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x2523B89298908FEf4c5e5bd6F55F20926e22058f": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x7c54D547FAD72f8AFbf6E5b04403A0168b654C6f": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x39AC3C494950A4363D739201BA5A0861265C9ae5": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x0e46941F9bfF8d0784BFfa3d0D7883CDb82D7aE7": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x4C0Bb704529Fa49A26bD854802d70206982c6f1B": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x672fEA44f4583DdaD620d60C1Ac31021F47558Cb": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x8263bC3766a09f6dD4Bab04b4bf8D45F2B0973FF": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x6eb8f24C5870c543875E4D9518c971ccB804520F": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x40dAEAc02dCf6b3c51F9151f532C21DCEF2F7E63": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x3B7f4e4Cf2fa43df013d2B32673e6A01d29ab2Ac": {
|
||||
isListed: false,
|
||||
},
|
||||
"0x9f0849FB830679829d1FB759b11236D375D15C78": {
|
||||
isListed: false,
|
||||
},
|
||||
"0xa29FfE4152B65A0347512Ae5c6A4Bbc7a3d6d51B": {
|
||||
isListed: false,
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -392,6 +504,9 @@ export const openGmxPositionImpl = async (
|
||||
takeProfitPrice: takeProfitPrice ? numberToBigint(takeProfitPrice, 30) : undefined,
|
||||
acceptablePriceImpactBuffer: 150,
|
||||
limitPrice: limitPrice,
|
||||
// Add required fields from BaseOptionalParams
|
||||
marketsInfoData: marketsInfoData,
|
||||
tokensData: tokensData,
|
||||
};
|
||||
|
||||
console.log('📋 Position params:', {
|
||||
@@ -593,8 +708,22 @@ function getMarketInfoFromTicker(ticker: string, marketsInfoData: MarketsInfoDat
|
||||
}
|
||||
|
||||
export function getTokenDataFromTicker(ticker: string, tokensData: TokensData): TokenData {
|
||||
const token = getTokenBySymbol(arbitrum.id, ticker);
|
||||
return getByKey(tokensData, token.address);
|
||||
console.log(`🔍 Looking up token for ticker: ${ticker}`);
|
||||
const token = getTokenBySymbol(arbitrum.id, ticker, { version: "v2", isSynthetic: false });
|
||||
console.log(`📋 Token found:`, {
|
||||
symbol: token.symbol,
|
||||
address: token.address,
|
||||
decimals: token.decimals,
|
||||
isNative: token.isNative
|
||||
});
|
||||
const tokenData = getByKey(tokensData, token.address);
|
||||
console.log(`📊 Token data:`, {
|
||||
address: tokenData?.address,
|
||||
decimals: tokenData?.decimals,
|
||||
isNative: tokenData?.isNative,
|
||||
symbol: tokenData?.symbol
|
||||
});
|
||||
return tokenData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -681,7 +810,10 @@ export const closeGmxPositionImpl = async (
|
||||
borrowingFeeUsd: 0n,
|
||||
fundingFeeUsd: 0n,
|
||||
swapProfitFeeUsd: 0n,
|
||||
positionPriceImpactDeltaUsd: 0n,
|
||||
proportionalPendingImpactDeltaUsd: 0n,
|
||||
closePriceImpactDeltaUsd: 0n,
|
||||
totalPendingImpactDeltaUsd: 0n,
|
||||
balanceWasImproved: false,
|
||||
priceImpactDiffUsd: 0n,
|
||||
payedRemainingCollateralAmount: 0n,
|
||||
payedOutputUsd: 0n,
|
||||
@@ -696,7 +828,7 @@ export const closeGmxPositionImpl = async (
|
||||
triggerPrice: position.markPrice,
|
||||
}
|
||||
|
||||
const params2 = {
|
||||
const params = {
|
||||
marketInfo,
|
||||
marketsInfoData,
|
||||
tokensData,
|
||||
@@ -704,11 +836,11 @@ export const closeGmxPositionImpl = async (
|
||||
allowedSlippage: 30,
|
||||
decreaseAmounts,
|
||||
collateralToken: position.marketInfo.shortToken,
|
||||
referralCodeForTxn: encodeReferralCode("kaigen_ai"),
|
||||
referralCode: encodeReferralCode("kaigen_ai"),
|
||||
}
|
||||
|
||||
// Execute the close position order
|
||||
await sdk.orders.createDecreaseOrder(params2);
|
||||
await sdk.orders.createDecreaseOrder(params);
|
||||
|
||||
return "hash";
|
||||
}, sdk, 0
|
||||
@@ -833,7 +965,9 @@ export const getGmxTradeImpl = async (
|
||||
status: TradeStatus.PendingOpen, // Assuming these are pending orders
|
||||
tradeType: order.orderType == OrderType.LimitIncrease ? TradeType.Limit : TradeType.Market,
|
||||
date: new Date(Number(order.updatedAtTime) * 1000), // Convert BigInt to Number first
|
||||
exchangeOrderId: order.key
|
||||
exchangeOrderId: order.key,
|
||||
fee: 0, // Add missing fee property
|
||||
message: "" // Add missing message property
|
||||
} as Trade;
|
||||
});
|
||||
|
||||
@@ -884,7 +1018,8 @@ export const getGmxPositionsImpl = async (
|
||||
): Promise<Position[]> => {
|
||||
return executeWithFallback(
|
||||
async (sdk, retryCount) => {
|
||||
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo();
|
||||
// Fetch market info and tokens data once for all positions
|
||||
const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
|
||||
|
||||
const positionsInfo = await sdk.positions.getPositionsInfo({
|
||||
marketsInfoData,
|
||||
@@ -892,7 +1027,71 @@ export const getGmxPositionsImpl = async (
|
||||
showPnlInLeverage: true,
|
||||
});
|
||||
|
||||
const positions = Object.values(positionsInfo).map(async (pos) => {
|
||||
// Fetch all orders once for all positions to avoid multiple API calls
|
||||
const orders = await sdk.orders.getOrders({
|
||||
account: sdk.account,
|
||||
marketsInfoData,
|
||||
tokensData
|
||||
});
|
||||
|
||||
// Group orders by ticker for efficient lookup
|
||||
const ordersByTicker = new Map<string, Trade[]>();
|
||||
Object.values(orders.ordersInfoData).forEach(order => {
|
||||
if ('marketInfo' in order) {
|
||||
const ticker = order.marketInfo.indexToken.symbol.toUpperCase();
|
||||
if (!ordersByTicker.has(ticker)) {
|
||||
ordersByTicker.set(ticker, []);
|
||||
}
|
||||
|
||||
const positionOrder = order as PositionOrderInfo;
|
||||
const priceDecimals = calculateDisplayDecimals(
|
||||
positionOrder.indexToken.prices?.minPrice,
|
||||
undefined,
|
||||
positionOrder.indexToken.visualMultiplier
|
||||
);
|
||||
|
||||
const triggerPrice = order.contractTriggerPrice ?? 0n;
|
||||
const sizeDelta = order.sizeDeltaUsd ?? 0n;
|
||||
const initialCollateral = order.initialCollateralDeltaAmount ?? 0n;
|
||||
|
||||
let leverage = 2;
|
||||
if (initialCollateral !== 0n) {
|
||||
const size = Number(sizeDelta) / 1e30;
|
||||
const collateral = Number(initialCollateral) / 1e6;
|
||||
if (collateral > 0) {
|
||||
leverage = Math.round(size / collateral);
|
||||
}
|
||||
}
|
||||
|
||||
const displayPrice = formatUsd(positionOrder.triggerPrice, {
|
||||
displayDecimals: priceDecimals,
|
||||
visualMultiplier: positionOrder.indexToken?.visualMultiplier,
|
||||
});
|
||||
|
||||
const numericPrice = Number(displayPrice.replace(/[^0-9.]/g, ''));
|
||||
const sizeInTokens = Number(sizeDelta) / Number(triggerPrice);
|
||||
const scaledTokenSize = sizeInTokens * 1e-30;
|
||||
|
||||
const trade: Trade = {
|
||||
id: order.key,
|
||||
ticker: ticker,
|
||||
direction: order.isLong ? TradeDirection.Short : TradeDirection.Long,
|
||||
price: numericPrice,
|
||||
quantity: scaledTokenSize,
|
||||
leverage: leverage,
|
||||
status: TradeStatus.PendingOpen,
|
||||
tradeType: order.orderType == OrderType.LimitIncrease ? TradeType.Limit : TradeType.Market,
|
||||
date: new Date(Number(order.updatedAtTime) * 1000),
|
||||
exchangeOrderId: order.key,
|
||||
fee: 0,
|
||||
message: ""
|
||||
} as Trade;
|
||||
|
||||
ordersByTicker.get(ticker)!.push(trade);
|
||||
}
|
||||
});
|
||||
|
||||
const positions = Object.values(positionsInfo).map((pos) => {
|
||||
// Fix leverage calculation to avoid exponential notation issues
|
||||
let leverage = 1; // Default to 1x leverage
|
||||
|
||||
@@ -914,8 +1113,8 @@ export const getGmxPositionsImpl = async (
|
||||
const collateralDecimals = pos.isLong ? pos.marketInfo.shortToken.decimals : pos.marketInfo.longToken.decimals;
|
||||
|
||||
const ticker = pos.marketInfo.indexToken.symbol;
|
||||
// get SL and TP from the position
|
||||
const trades = await getGmxTradeImpl(sdk, ticker);
|
||||
// Get trades from the pre-fetched orders by ticker
|
||||
const trades = ordersByTicker.get(ticker.toUpperCase()) || [];
|
||||
|
||||
// Calculate proper price display decimals
|
||||
const priceDecimals = calculateDisplayDecimals(
|
||||
@@ -983,11 +1182,11 @@ export const getGmxPositionsImpl = async (
|
||||
} as unknown as Position; // Use unknown assertion due to missing fields in target Position type
|
||||
|
||||
if (stopLoss) {
|
||||
position.stopLoss = stopLoss;
|
||||
position.StopLoss = stopLoss;
|
||||
}
|
||||
|
||||
if (takeProfit) {
|
||||
position.takeProfit1 = takeProfit;
|
||||
position.TakeProfit1 = takeProfit;
|
||||
}
|
||||
|
||||
// build the open trade base on the current position object
|
||||
@@ -996,15 +1195,15 @@ export const getGmxPositionsImpl = async (
|
||||
price: numericEntryPrice,
|
||||
quantity: tokenSize,
|
||||
leverage: leverage,
|
||||
status: TradeStatus.PendingOpen,
|
||||
status: TradeStatus.Filled,
|
||||
} as Trade;
|
||||
|
||||
position.open = open;
|
||||
position.Open = open;
|
||||
|
||||
return position;
|
||||
});
|
||||
|
||||
return Promise.all(positions);
|
||||
return positions;
|
||||
}, sdk, retryCount
|
||||
);
|
||||
};
|
||||
@@ -1073,25 +1272,30 @@ export default fp(async (fastify) => {
|
||||
fastify.decorateRequest('swapGmxTokens', swapGmxTokens)
|
||||
fastify.decorateRequest('checkGmxTokenAllowances', checkGmxTokenAllowances)
|
||||
|
||||
// Pre-populate and refresh the markets cache on startup
|
||||
fastify.addHook('onReady', async () => {
|
||||
// Set up cache refresh without blocking plugin registration
|
||||
setupCacheRefresh();
|
||||
|
||||
// Initialize cache asynchronously without blocking
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
await getMarketsData();
|
||||
setupCacheRefresh();
|
||||
} catch (error) {
|
||||
console.error('Initial cache population failed:', error);
|
||||
process.exit(1);
|
||||
console.warn('Initial cache population failed, but continuing:', error);
|
||||
// The cache will be populated when first needed
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const sdk = await getClientForAddress("0x0000000000000000000000000000000000000000");
|
||||
fastify.log.info('GMX client initialized successfully');
|
||||
fastify.log.info('GMX UI Fee Receiver:', sdk.config.settings.uiFeeReceiverAccount);
|
||||
} catch (error) {
|
||||
fastify.log.error('Failed to initialize GMX client:', error);
|
||||
throw error;
|
||||
}
|
||||
// Initialize GMX client asynchronously without blocking
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
const sdk = await getClientForAddress("0x0000000000000000000000000000000000000000");
|
||||
fastify.log.info('GMX client initialized successfully');
|
||||
fastify.log.info('GMX UI Fee Receiver:', sdk.config.settings.uiFeeReceiverAccount);
|
||||
} catch (error) {
|
||||
fastify.log.warn('GMX client initialization failed, but continuing:', error);
|
||||
// The client will be initialized when actually needed
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export const getGmxRebateStatsImpl = async (
|
||||
@@ -1759,57 +1963,47 @@ export const swapGmxTokensImpl = async (
|
||||
const fromTokenData = getTokenDataFromTicker(fromTicker, tokensData);
|
||||
const toTokenData = getTokenDataFromTicker(toTicker, tokensData);
|
||||
|
||||
console.log("fromTokenData", fromTokenData);
|
||||
console.log(`🔄 Swap details:`, {
|
||||
fromTicker,
|
||||
toTicker,
|
||||
fromTokenAddress: fromTokenData?.address,
|
||||
fromTokenIsNative: fromTokenData?.isNative,
|
||||
toTokenAddress: toTokenData?.address,
|
||||
toTokenIsNative: toTokenData?.isNative
|
||||
});
|
||||
|
||||
if (!fromTokenData || !toTokenData) {
|
||||
throw new Error(`Token data not found for ${fromTicker} or ${toTicker}`);
|
||||
}
|
||||
|
||||
// Check for zero addresses (but allow native tokens like ETH)
|
||||
if (fromTokenData.address === "0x0000000000000000000000000000000000000000" && !fromTokenData.isNative) {
|
||||
throw new Error(`From token ${fromTicker} has zero address - token not found or invalid`);
|
||||
}
|
||||
if (toTokenData.address === "0x0000000000000000000000000000000000000000" && !toTokenData.isNative) {
|
||||
throw new Error(`To token ${toTicker} has zero address - token not found or invalid`);
|
||||
}
|
||||
|
||||
// Calculate the from token amount with proper decimals
|
||||
const fromTokenAmount = BigInt(Math.floor(amount * Math.pow(10, fromTokenData.decimals)));
|
||||
|
||||
// Check and handle token allowance for ExchangeRouter contract
|
||||
const syntheticsRouterRouterAddress = getContract(sdk.chainId, "SyntheticsRouter");
|
||||
|
||||
// If ticker ETH no need to check allowance
|
||||
if (fromTicker === "ETH") {
|
||||
console.log(`✅ ETH no need to check allowance`);
|
||||
}else {
|
||||
try {
|
||||
const currentAllowance = await getTokenAllowance(
|
||||
sdk.account,
|
||||
fromTokenData.address,
|
||||
syntheticsRouterRouterAddress
|
||||
);
|
||||
// Check and handle token allowance for SyntheticsRouter contract
|
||||
await approveTokenForContract(
|
||||
sdk,
|
||||
fromTicker,
|
||||
fromTokenData,
|
||||
fromTokenAmount,
|
||||
"SyntheticsRouter"
|
||||
);
|
||||
|
||||
console.log(`Current allowance for ${fromTicker}:`, currentAllowance.toString());
|
||||
console.log(`Required amount:`, fromTokenAmount.toString());
|
||||
|
||||
if (currentAllowance < fromTokenAmount) {
|
||||
console.log(`🔧 Insufficient allowance for ${fromTicker}. Auto-approving ExchangeRouter...`);
|
||||
|
||||
// Calculate a reasonable approval amount (use the larger of required amount or 1000 tokens)
|
||||
const tokenUnit = BigInt(Math.pow(10, fromTokenData.decimals));
|
||||
const minApprovalAmount = tokenUnit * 1000n; // 1000 tokens
|
||||
const approvalAmount = fromTokenAmount > minApprovalAmount ? fromTokenAmount : minApprovalAmount;
|
||||
|
||||
const approvalHash = await approveContractImpl(
|
||||
sdk.account,
|
||||
fromTokenData.address,
|
||||
syntheticsRouterRouterAddress,
|
||||
sdk.chainId,
|
||||
approvalAmount
|
||||
);
|
||||
|
||||
console.log(`✅ Token approval successful! Hash: ${approvalHash}`);
|
||||
console.log(`📝 Approved ${approvalAmount.toString()} ${fromTicker} for ExchangeRouter`);
|
||||
} else {
|
||||
console.log(`✅ Sufficient allowance already exists for ${fromTicker}`);
|
||||
}
|
||||
} catch (allowanceError) {
|
||||
console.warn('Could not check or approve token allowance:', allowanceError);
|
||||
throw new Error(`Failed to handle token allowance: ${allowanceError instanceof Error ? allowanceError.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
await approveTokenForContract(
|
||||
sdk,
|
||||
fromTicker,
|
||||
fromTokenData,
|
||||
fromTokenAmount,
|
||||
"ExchangeRouter"
|
||||
);
|
||||
|
||||
// Calculate trigger price for limit orders
|
||||
let triggerPrice: bigint | undefined;
|
||||
@@ -1821,23 +2015,44 @@ export const swapGmxTokensImpl = async (
|
||||
// Convert slippage percentage to basis points
|
||||
const allowedSlippageBps = Math.floor(allowedSlippage * 100); // Convert percentage to basis points
|
||||
|
||||
// Create SwapParams for the SDK
|
||||
const swapParams = {
|
||||
fromAmount: fromTokenAmount,
|
||||
fromTokenAddress: fromTokenData.address,
|
||||
toTokenAddress: toTokenData.address,
|
||||
allowedSlippageBps: allowedSlippageBps,
|
||||
referralCodeForTxn: encodeReferralCode("kaigen_ai"),
|
||||
skipSimulation: true,
|
||||
} as SwapParams;
|
||||
|
||||
console.log("swapParams", swapParams);
|
||||
// Try using the SDK's built-in swap method first
|
||||
try {
|
||||
const swapParams = {
|
||||
fromAmount: fromTokenAmount,
|
||||
fromTokenAddress: fromTokenData.address,
|
||||
toTokenAddress: toTokenData.address,
|
||||
allowedSlippageBps: allowedSlippageBps,
|
||||
referralCodeForTxn: encodeReferralCode("kaigen_ai"),
|
||||
skipSimulation: true,
|
||||
} as SwapParams;
|
||||
|
||||
swapParams.marketsInfoData = marketsInfoData;
|
||||
swapParams.tokensData = tokensData;
|
||||
swapParams.marketsInfoData = marketsInfoData;
|
||||
swapParams.tokensData = tokensData;
|
||||
|
||||
// Use the SDK's built-in swap method
|
||||
await sdk.orders.swap(swapParams);
|
||||
console.log('🔄 Attempting SDK swap...');
|
||||
await sdk.orders.swap(swapParams);
|
||||
console.log('✅ SDK swap successful!');
|
||||
} catch (sdkError) {
|
||||
console.log('❌ SDK swap failed, trying alternative approach:', sdkError);
|
||||
|
||||
// Fall back to createSwapOrderTxn directly with simpler parameters
|
||||
console.log('🔄 Attempting direct createSwapOrderTxn...');
|
||||
await createSwapOrderTxn(sdk, {
|
||||
fromTokenAddress: fromTokenData.address,
|
||||
fromTokenAmount: fromTokenAmount,
|
||||
toTokenAddress: toTokenData.address,
|
||||
swapPath: [fromTokenData.address, toTokenData.address],
|
||||
orderType: OrderType.MarketSwap,
|
||||
minOutputAmount: BigInt(Math.floor(amount * Math.pow(10, toTokenData.decimals) * (1 - allowedSlippage / 100))),
|
||||
referralCode: encodeReferralCode("kaigen_ai"),
|
||||
executionFee: 100000000000000000n, // 0.1 ETH as execution fee
|
||||
allowedSlippage: allowedSlippageBps,
|
||||
tokensData: tokensData,
|
||||
skipSimulation: true,
|
||||
});
|
||||
console.log('✅ Direct createSwapOrderTxn successful!');
|
||||
}
|
||||
|
||||
return "swap_order_created";
|
||||
}, sdk, 0
|
||||
@@ -1925,7 +2140,7 @@ export async function swapGmxTokens(
|
||||
export const checkGmxTokenAllowances = async (
|
||||
account: string,
|
||||
tokenAddress: string,
|
||||
chainId: number = ARBITRUM
|
||||
chainId: ContractsChainId = ARBITRUM
|
||||
): Promise<{
|
||||
exchangeRouter: bigint;
|
||||
orderVault: bigint;
|
||||
|
||||
@@ -11,8 +11,8 @@ import {ARBITRUM} from '../../generated/gmxsdk/configs/chains.js'
|
||||
import {TOKENS} from '../../generated/gmxsdk/configs/tokens.js'
|
||||
import {CONTRACTS} from '../../generated/gmxsdk/configs/contracts.js'
|
||||
import {getClientForAddress, getTokenDataFromTicker} from './gmx.js'
|
||||
import {Balance, Chain, Ticker} from '../../generated/ManagingApiTypes.js'
|
||||
import {Address} from 'viem'
|
||||
import {Balance, Chain, Ticker} from '../../generated/ManagingApiTypes.js'
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config()
|
||||
@@ -59,10 +59,10 @@ export const getPrivyClient = (fastify?: FastifyInstance): PrivyClient => {
|
||||
const authKey = fastify?.config?.PRIVY_AUTHORIZATION_KEY || process.env.PRIVY_AUTHORIZATION_KEY;
|
||||
|
||||
if (!appId || !appSecret || !authKey) {
|
||||
console.error('Missing Privy environment variables:');
|
||||
console.error('PRIVY_APP_ID:', appId ? 'present' : 'missing');
|
||||
console.error('PRIVY_APP_SECRET:', appSecret ? 'present' : 'missing');
|
||||
console.error('PRIVY_AUTHORIZATION_KEY:', authKey ? 'present' : 'missing');
|
||||
console.warn('Missing Privy environment variables:');
|
||||
console.warn('PRIVY_APP_ID:', appId ? 'present' : 'missing');
|
||||
console.warn('PRIVY_APP_SECRET:', appSecret ? 'present' : 'missing');
|
||||
console.warn('PRIVY_AUTHORIZATION_KEY:', authKey ? 'present' : 'missing');
|
||||
throw new Error('Missing required Privy environment variables');
|
||||
}
|
||||
|
||||
@@ -822,7 +822,7 @@ export const sendTokenImpl = async (
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
ticker: string,
|
||||
amount: bigint,
|
||||
amount: string,
|
||||
chainId?: number,
|
||||
): Promise<string> => {
|
||||
try {
|
||||
@@ -830,6 +830,30 @@ export const sendTokenImpl = async (
|
||||
const networkName = getChainName(chainId);
|
||||
const privy = getPrivyClient();
|
||||
|
||||
// Handle decimal numbers with comma separators and convert to proper token units
|
||||
// Replace comma with period for decimal parsing
|
||||
const normalizedAmount = amount.replace(',', '.');
|
||||
|
||||
// Parse as float first
|
||||
const amountFloat = parseFloat(normalizedAmount);
|
||||
|
||||
if (isNaN(amountFloat)) {
|
||||
throw new Error(`Invalid amount format: ${amount}`);
|
||||
}
|
||||
|
||||
// Get token decimals from GMX configuration based on chain ID
|
||||
const chainTokens = TOKENS[chainId];
|
||||
const token = chainTokens?.find(t => t.symbol === ticker);
|
||||
|
||||
if (!token) {
|
||||
throw new Error(`Token not found: ${ticker} on chain ${chainId}`);
|
||||
}
|
||||
|
||||
const tokenDecimals = token.decimals;
|
||||
const amountInSmallestUnit = Math.floor(amountFloat * Math.pow(10, tokenDecimals));
|
||||
|
||||
const amountBigInt = BigInt(amountInSmallestUnit);
|
||||
|
||||
if (ticker === 'ETH') {
|
||||
// Native ETH transfer: no allowance, no data, value is amount as hex string
|
||||
const { hash } = await privy.walletApi.ethereum.sendTransaction({
|
||||
@@ -838,7 +862,7 @@ export const sendTokenImpl = async (
|
||||
caip2: networkName as string,
|
||||
transaction: {
|
||||
to: recipientAddress as Address,
|
||||
value: '0x' + amount.toString(16), // value in wei as hex string
|
||||
value: '0x' + amountBigInt.toString(16), // value in wei as hex string
|
||||
chainId: chainId,
|
||||
},
|
||||
} as any);
|
||||
@@ -854,21 +878,21 @@ export const sendTokenImpl = async (
|
||||
// Check if sender has sufficient allowance for the token transfer
|
||||
const senderAllowance = await getTokenAllowance(senderAddress, tokenData.address, senderAddress);
|
||||
// If insufficient allowance, approve the token first
|
||||
if (senderAllowance < amount) {
|
||||
console.log(`Insufficient allowance (${senderAllowance}). Approving token for amount: ${amount}`);
|
||||
if (senderAllowance < amountBigInt) {
|
||||
console.log(`Insufficient allowance (${senderAllowance}). Approving token for amount: ${amountBigInt}`);
|
||||
await approveContractImpl(
|
||||
senderAddress,
|
||||
tokenData.address,
|
||||
senderAddress, // Approve self to spend tokens
|
||||
chainId,
|
||||
amount
|
||||
amountBigInt
|
||||
);
|
||||
console.log('Token approval completed');
|
||||
}
|
||||
// Create contract interface for ERC20 token
|
||||
const contractInterface = new ethers.Interface(Token.abi);
|
||||
// Amount is already in the smallest units (wei), so we don't need to convert it
|
||||
const transferAmount = amount;
|
||||
const transferAmount = amountBigInt;
|
||||
// Encode the transfer function call
|
||||
const data = contractInterface.encodeFunctionData("transfer", [recipientAddress, transferAmount]);
|
||||
// Send the transaction
|
||||
@@ -1123,7 +1147,7 @@ export async function sendToken(
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
ticker: string,
|
||||
amount: bigint,
|
||||
amount: string,
|
||||
chainId?: number
|
||||
) {
|
||||
try {
|
||||
@@ -1148,7 +1172,13 @@ export async function sendToken(
|
||||
throw new Error('Token ticker is required for token transfer');
|
||||
}
|
||||
|
||||
if (!amount || amount <= 0n) {
|
||||
if (!amount) {
|
||||
throw new Error('Amount is required for token transfer');
|
||||
}
|
||||
|
||||
// Validate amount
|
||||
const amountFloat = parseFloat(amount.replace(',', '.'));
|
||||
if (isNaN(amountFloat) || amountFloat <= 0) {
|
||||
throw new Error('Valid amount is required for token transfer');
|
||||
}
|
||||
|
||||
@@ -1191,7 +1221,7 @@ export default fp(async (fastify) => {
|
||||
return initAddress.call(this, reply, address);
|
||||
});
|
||||
|
||||
fastify.decorateRequest('sendToken', async function(this: FastifyRequest, reply: FastifyReply, senderAddress: string, recipientAddress: string, ticker: string, amount: bigint, chainId?: number) {
|
||||
fastify.decorateRequest('sendToken', async function(this: FastifyRequest, reply: FastifyReply, senderAddress: string, recipientAddress: string, ticker: string, amount: string, chainId?: number) {
|
||||
return sendToken.call(this, reply, senderAddress, recipientAddress, ticker, amount, chainId);
|
||||
});
|
||||
|
||||
@@ -1202,7 +1232,7 @@ export default fp(async (fastify) => {
|
||||
// Test the Privy client initialization
|
||||
try {
|
||||
const testClient = getPrivyClient(fastify);
|
||||
fastify.log.info('Privy client initialized successfully');
|
||||
fastify.log.info('Privy client initialized successfully', (await testClient.getAppSettings()).id);
|
||||
} catch (error) {
|
||||
fastify.log.error('Failed to initialize Privy client:', error);
|
||||
throw error;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {FastifyPluginAsyncTypebox} from '@fastify/type-provider-typebox'
|
||||
import {Type} from '@sinclair/typebox'
|
||||
import {TradeDirection} from '../../../generated/ManagingApiTypes'
|
||||
import {TradeDirection} from '../../../generated/ManagingApiTypes.js'
|
||||
import {
|
||||
getClaimableFundingFeesImpl,
|
||||
getClaimableUiFeesImpl,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {FastifyPluginAsyncTypebox, Type} from '@fastify/type-provider-typebox'
|
||||
import {handleError} from '../../../utils/errorHandler.js'
|
||||
import {TOKENS} from '../../../generated/gmxsdk/configs/tokens.js'
|
||||
import {ARBITRUM} from '../../../generated/gmxsdk/configs/chains.js'
|
||||
import {Ticker} from '../../../generated/ManagingApiTypes.js'
|
||||
|
||||
const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
||||
@@ -112,37 +110,8 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
||||
try {
|
||||
const { senderAddress, recipientAddress, ticker, amount, chainId } = request.body;
|
||||
|
||||
// Handle decimal numbers with comma separators and convert to proper token units
|
||||
let amountBigInt: bigint;
|
||||
|
||||
if (typeof amount === 'string') {
|
||||
// Replace comma with period for decimal parsing
|
||||
const normalizedAmount = amount.replace(',', '.');
|
||||
|
||||
// Parse as float first
|
||||
const amountFloat = parseFloat(normalizedAmount);
|
||||
|
||||
if (isNaN(amountFloat)) {
|
||||
throw new Error(`Invalid amount format: ${amount}`);
|
||||
}
|
||||
|
||||
// Get token decimals from GMX configuration
|
||||
const chainTokens = TOKENS[ARBITRUM];
|
||||
const token = chainTokens.find(t => t.symbol === ticker);
|
||||
|
||||
if (!token) {
|
||||
throw new Error(`Token not found: ${ticker}`);
|
||||
}
|
||||
|
||||
const tokenDecimals = token.decimals;
|
||||
const amountInSmallestUnit = Math.floor(amountFloat * Math.pow(10, tokenDecimals));
|
||||
|
||||
amountBigInt = BigInt(amountInSmallestUnit);
|
||||
} else {
|
||||
amountBigInt = BigInt(amount);
|
||||
}
|
||||
|
||||
return await request.sendToken(reply, senderAddress, recipientAddress, ticker, amountBigInt, chainId);
|
||||
// Pass amount directly to sendTokenImpl - it will handle decimal conversion internally
|
||||
return await request.sendToken(reply, senderAddress, recipientAddress, ticker, amount, chainId);
|
||||
} catch (error) {
|
||||
return handleError(request, reply, error, 'privy/send-token');
|
||||
}
|
||||
|
||||
@@ -136,7 +136,8 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
||||
message: 'GMX SDK successfully initialized',
|
||||
data: {
|
||||
responseTimeMs: responseTime,
|
||||
uiFeeFactor: uiFeeFactor.toString()
|
||||
uiFeeFactor: uiFeeFactor.toString(),
|
||||
uiFeeReceiverAccount: sdk.config.settings?.uiFeeReceiverAccount
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import assert from 'node:assert';
|
||||
import {claimGmxFundingFeesImpl, getClientForAddress} from '../../src/plugins/custom/gmx.js';
|
||||
|
||||
test('GMX Claim Funding Fees', async (t) => {
|
||||
const testAccount = '0xbBA4eaA534cbD0EcAed5E2fD6036Aec2E7eE309f';
|
||||
const testAccount = '0x0af60b5c1c349744ef8fa8c4ed78ee1a0d392fe9';
|
||||
|
||||
await t.test('should claim funding fees for valid account', async () => {
|
||||
try {
|
||||
|
||||
@@ -5,11 +5,11 @@ import {TradeDirection} from '../../src/generated/ManagingApiTypes'
|
||||
|
||||
test('GMX Position Closing', async (t) => {
|
||||
await t.test('should close a long position for BTC', async () => {
|
||||
const sdk = await getClientForAddress('0x0b4A132cb6ed8fa66953bf61a53D0B91DaCaAd78')
|
||||
const sdk = await getClientForAddress('0x932167388dD9aad41149b3cA23eBD489E2E2DD78')
|
||||
|
||||
const result = await closeGmxPositionImpl(
|
||||
sdk,
|
||||
"BTC",
|
||||
"ETH",
|
||||
TradeDirection.Long
|
||||
)
|
||||
console.log('Position closing result:', result)
|
||||
|
||||
48
src/Managing.Web3Proxy/test/plugins/send-token.test.ts
Normal file
48
src/Managing.Web3Proxy/test/plugins/send-token.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {describe, it} from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import {sendTokenImpl} from '../../src/plugins/custom/privy.js'
|
||||
import {Ticker} from '../../src/generated/ManagingApiTypes.js'
|
||||
import {ARBITRUM} from '../../src/generated/gmxsdk/configs/chains.js'
|
||||
|
||||
describe('send token implementation', () => {
|
||||
|
||||
it('should send USDC token successfully', async () => {
|
||||
try {
|
||||
// Test addresses - you can replace these with actual addresses
|
||||
const senderAddress = '0x0af60b5c1c349744ef8fa8c4ed78ee1a0d392fe9'
|
||||
const recipientAddress = '0xeba3adc12481db743884bbd11f2a75e0c4cffcf6'
|
||||
|
||||
// Amount as string (will be converted to smallest units internally)
|
||||
const amount = "0.000547" // ETH amount
|
||||
const ticker = Ticker.ETH
|
||||
|
||||
console.log('Testing token transfer:')
|
||||
console.log('Sender:', senderAddress)
|
||||
console.log('Recipient:', recipientAddress)
|
||||
console.log('Amount:', amount.toString())
|
||||
console.log('Ticker:', ticker)
|
||||
|
||||
const result = await sendTokenImpl(
|
||||
senderAddress,
|
||||
recipientAddress,
|
||||
ticker,
|
||||
amount,
|
||||
ARBITRUM
|
||||
)
|
||||
|
||||
// Verify the result is a transaction hash (string starting with 0x)
|
||||
assert.strictEqual(typeof result, 'string')
|
||||
assert.strictEqual(result.startsWith('0x'), true)
|
||||
assert.strictEqual(result.length, 66) // Standard transaction hash length
|
||||
|
||||
console.log('Transaction hash:', result)
|
||||
|
||||
} catch (error) {
|
||||
console.log('Error during token transfer:', error)
|
||||
|
||||
// Test that the error is related to actual execution rather than parameter validation
|
||||
// This could be due to insufficient balance, allowance, or network issues
|
||||
assert.fail(`Token transfer failed: ${error.message}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,29 +1,30 @@
|
||||
import {describe, it} from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import {getClientForAddress, swapGmxTokensImpl} from '../../src/plugins/custom/gmx.js'
|
||||
import {Ticker} from '../../src/generated/ManagingApiTypes'
|
||||
import {Ticker} from '../../src/generated/ManagingApiTypes.js'
|
||||
|
||||
describe('swap tokens implementation', () => {
|
||||
|
||||
it('should swap USDC to ETH successfully', async () => {
|
||||
it('should swap SOL to USDC successfully', async () => {
|
||||
try {
|
||||
const testAccount = '0x0AF60B5C1c349744Ef8fa8c4ed78Ee1A0d392Fe9'
|
||||
const testAccount = '0xbBA4eaA534cbD0EcAed5E2fD6036Aec2E7eE309f'
|
||||
const sdk = await getClientForAddress(testAccount)
|
||||
|
||||
console.log('Account', sdk.account)
|
||||
const result = await swapGmxTokensImpl(
|
||||
sdk,
|
||||
Ticker.SOL, // fromTicker
|
||||
Ticker.USDC, // toTicker
|
||||
0.001 // amount
|
||||
)
|
||||
const result = await swapGmxTokensImpl(
|
||||
sdk,
|
||||
Ticker.PENDLE,
|
||||
Ticker.USDC,
|
||||
13.339559522
|
||||
)
|
||||
|
||||
assert.strictEqual(typeof result, 'string')
|
||||
assert.strictEqual(result, 'swap_order_created')
|
||||
} catch (error) {
|
||||
console.log('error', error)
|
||||
|
||||
// Test that the error is related to actual execution rather than parameter validation
|
||||
assert.ok(error instanceof Error)
|
||||
console.log('Expected error during test execution:', error.message)
|
||||
assert.fail(error.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -332,8 +332,8 @@ export class AccountClient extends AuthorizedApiBase {
|
||||
return Promise.resolve<SwapInfos>(null as any);
|
||||
}
|
||||
|
||||
account_GetExchangeApprovalStatus(): Promise<ExchangeApprovalStatus[]> {
|
||||
let url_ = this.baseUrl + "/Account/exchange-approval-status";
|
||||
account_GetExchangeApprovalStatus(): Promise<ExchangeInitializedStatus[]> {
|
||||
let url_ = this.baseUrl + "/Account/exchanges-initialized-status";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
@@ -350,13 +350,13 @@ export class AccountClient extends AuthorizedApiBase {
|
||||
});
|
||||
}
|
||||
|
||||
protected processAccount_GetExchangeApprovalStatus(response: Response): Promise<ExchangeApprovalStatus[]> {
|
||||
protected processAccount_GetExchangeApprovalStatus(response: Response): Promise<ExchangeInitializedStatus[]> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ExchangeApprovalStatus[];
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ExchangeInitializedStatus[];
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
@@ -364,7 +364,7 @@ export class AccountClient extends AuthorizedApiBase {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<ExchangeApprovalStatus[]>(null as any);
|
||||
return Promise.resolve<ExchangeInitializedStatus[]>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3771,9 +3771,9 @@ export interface SendTokenRequest {
|
||||
chainId?: number | null;
|
||||
}
|
||||
|
||||
export interface ExchangeApprovalStatus {
|
||||
export interface ExchangeInitializedStatus {
|
||||
exchange?: TradingExchanges;
|
||||
isApproved?: boolean;
|
||||
isInitialized?: boolean;
|
||||
}
|
||||
|
||||
export interface Backtest {
|
||||
|
||||
@@ -219,9 +219,9 @@ export interface SendTokenRequest {
|
||||
chainId?: number | null;
|
||||
}
|
||||
|
||||
export interface ExchangeApprovalStatus {
|
||||
export interface ExchangeInitializedStatus {
|
||||
exchange?: TradingExchanges;
|
||||
isApproved?: boolean;
|
||||
isInitialized?: boolean;
|
||||
}
|
||||
|
||||
export interface Backtest {
|
||||
|
||||
@@ -206,12 +206,12 @@ const AccountRowDetails: React.FC<IAccountRowDetailProps> = ({
|
||||
<div
|
||||
key={status.exchange}
|
||||
className={`badge ${
|
||||
status.isApproved
|
||||
status.isInitialized
|
||||
? 'badge-success'
|
||||
: 'badge-outline'
|
||||
}`}
|
||||
>
|
||||
{status.exchange}: {status.isApproved ? 'Approved' : 'Not Approved'}
|
||||
{status.exchange}: {status.isInitialized ? 'Initialized' : 'Not Initialized'}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user