@continuumdao/ctm-mpc-defi 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2251 @@
1
+ 'use strict';
2
+
3
+ var module$1 = require('module');
4
+ var url = require('url');
5
+ var viem = require('viem');
6
+ var continuumNodeSdk = require('@continuumdao/continuum-node-sdk');
7
+
8
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
9
+ // src/core/registry.ts
10
+ var modules = [];
11
+ function registerProtocolModule(mod) {
12
+ const existing = modules.findIndex((m) => m.id === mod.id);
13
+ if (existing >= 0) {
14
+ modules[existing] = mod;
15
+ } else {
16
+ modules.push(mod);
17
+ }
18
+ }
19
+
20
+ // src/protocols/evm/gmx/support.ts
21
+ var GMX_SUPPORTED_CHAIN_IDS = [42161, 43114];
22
+ function isGmxChainSupported(chainId) {
23
+ return GMX_SUPPORTED_CHAIN_IDS.includes(chainId);
24
+ }
25
+ var require2 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
26
+ var { GmxApiSdk } = require2("@gmx-io/sdk/v2");
27
+ var GMX_HTTP_TIMEOUT_MS = 3e4;
28
+ var GMX_API_BASE_URLS = {
29
+ 42161: ["https://arbitrum.gmxapi.io/v1", "https://arbitrum.gmxapi.ai/v1"],
30
+ 43114: ["https://avalanche.gmxapi.io/v1", "https://avalanche.gmxapi.ai/v1"]
31
+ };
32
+ function buildGmxUrl(baseUrl, path, query) {
33
+ const base = baseUrl.replace(/\/$/, "");
34
+ if (!query) return `${base}${path}`;
35
+ const params = new URLSearchParams();
36
+ for (const [key, value] of Object.entries(query)) {
37
+ if (value !== void 0 && value !== null) params.set(key, String(value));
38
+ }
39
+ const qs = params.toString();
40
+ return qs ? `${base}${path}?${qs}` : `${base}${path}`;
41
+ }
42
+ function bigintReplacer(_key, value) {
43
+ return typeof value === "bigint" ? value.toString() : value;
44
+ }
45
+ async function gmxHttpFetchJson(baseUrl, path, opts) {
46
+ const url = buildGmxUrl(baseUrl, path, opts?.query);
47
+ const controller = new AbortController();
48
+ const timeoutId = setTimeout(() => controller.abort(), GMX_HTTP_TIMEOUT_MS);
49
+ try {
50
+ const response = await fetch(url, {
51
+ headers: { Accept: "application/json" },
52
+ signal: controller.signal
53
+ });
54
+ if (!response.ok) {
55
+ let message = `HTTP ${response.status}: ${response.statusText}`;
56
+ try {
57
+ const body = await response.json();
58
+ if (body?.message) message = `HTTP ${response.status}: ${body.message}`;
59
+ } catch {
60
+ }
61
+ throw new Error(`${message} (${url})`);
62
+ }
63
+ const json = await response.json();
64
+ return opts?.transform ? opts.transform(json) : json;
65
+ } finally {
66
+ clearTimeout(timeoutId);
67
+ }
68
+ }
69
+ async function gmxHttpPostJson(baseUrl, path, body, opts) {
70
+ const url = buildGmxUrl(baseUrl, path);
71
+ const controller = new AbortController();
72
+ const timeoutId = setTimeout(() => controller.abort(), GMX_HTTP_TIMEOUT_MS);
73
+ try {
74
+ const response = await fetch(url, {
75
+ method: "POST",
76
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
77
+ body: JSON.stringify(body, bigintReplacer),
78
+ signal: controller.signal
79
+ });
80
+ if (!response.ok) {
81
+ let message = `HTTP ${response.status}: ${response.statusText}`;
82
+ try {
83
+ const errBody = await response.json();
84
+ if (errBody?.message) message = `HTTP ${response.status}: ${errBody.message}`;
85
+ } catch {
86
+ }
87
+ throw new Error(`${message} (${url})`);
88
+ }
89
+ const json = await response.json();
90
+ return opts?.transform ? opts.transform(json) : json;
91
+ } finally {
92
+ clearTimeout(timeoutId);
93
+ }
94
+ }
95
+ function createIoFirstGmxHttpApi(chainId) {
96
+ const urls = GMX_API_BASE_URLS[chainId];
97
+ if (!urls) {
98
+ throw new Error(`GMX API URLs are not configured for chain ${chainId}.`);
99
+ }
100
+ async function withIoFirstFallback(fn) {
101
+ let lastError;
102
+ for (const baseUrl of urls) {
103
+ try {
104
+ return await fn(baseUrl);
105
+ } catch (e) {
106
+ lastError = e;
107
+ }
108
+ }
109
+ const detail = lastError instanceof Error ? lastError.message : String(lastError);
110
+ throw new Error(
111
+ `GMX API request failed on all endpoints (${urls.join(", ")}). Last error: ${detail}`
112
+ );
113
+ }
114
+ return {
115
+ fetchJson: (path, opts) => withIoFirstFallback((baseUrl) => gmxHttpFetchJson(baseUrl, path, opts)),
116
+ postJson: (path, body, opts) => withIoFirstFallback((baseUrl) => gmxHttpPostJson(baseUrl, path, body, opts))
117
+ };
118
+ }
119
+ var sdkByChain = /* @__PURE__ */ new Map();
120
+ function getGmxApiSdk(chainId) {
121
+ if (!isGmxChainSupported(chainId)) {
122
+ throw new Error(`GMX is not supported on chain ${chainId}. Supported: 42161 (Arbitrum), 43114 (Avalanche).`);
123
+ }
124
+ let sdk = sdkByChain.get(chainId);
125
+ if (!sdk) {
126
+ sdk = new GmxApiSdk({ chainId, api: createIoFirstGmxHttpApi(chainId) });
127
+ sdkByChain.set(chainId, sdk);
128
+ }
129
+ return sdk;
130
+ }
131
+ function parseGmxUsd30(amountHuman) {
132
+ const trimmed = amountHuman.trim();
133
+ if (!trimmed || !/^\d+(\.\d+)?$/.test(trimmed)) {
134
+ throw new Error("USD amount must be a positive decimal number.");
135
+ }
136
+ const [whole, frac = ""] = trimmed.split(".");
137
+ const fracPadded = (frac + "0".repeat(30)).slice(0, 30);
138
+ const wholeN = BigInt(whole);
139
+ const fracN = BigInt(fracPadded || "0");
140
+ const value = wholeN * 10n ** 30n + fracN;
141
+ if (value <= 0n) throw new Error("USD amount must be greater than zero.");
142
+ return value;
143
+ }
144
+ function parseGmxTokenAmount(amountHuman, decimals) {
145
+ return viem.parseUnits(amountHuman, decimals);
146
+ }
147
+ function gmxFormatApyPercent(apy) {
148
+ if (apy == null || !Number.isFinite(apy)) return null;
149
+ const pct = apy * 100;
150
+ if (pct <= 0) return null;
151
+ return `${pct.toLocaleString("en-US", { maximumFractionDigits: 2 })}%`;
152
+ }
153
+ function gmxLookupMarketApy(apyByMarket, marketTokenAddress) {
154
+ try {
155
+ const checksum = viem.getAddress(marketTokenAddress);
156
+ return apyByMarket[checksum] ?? apyByMarket[checksum.toLowerCase()] ?? null;
157
+ } catch {
158
+ const lower = marketTokenAddress.trim().toLowerCase();
159
+ return apyByMarket[lower] ?? null;
160
+ }
161
+ }
162
+ function gmxEnrichGmMarketRowWithApy(row, apyByMarket) {
163
+ const entry = gmxLookupMarketApy(apyByMarket, row.marketTokenAddress);
164
+ const apy = entry?.apy ?? null;
165
+ return {
166
+ ...row,
167
+ apy,
168
+ baseApy: entry?.baseApy ?? null,
169
+ bonusApr: entry?.bonusApr ?? null,
170
+ apyPercentLabel: gmxFormatApyPercent(apy)
171
+ };
172
+ }
173
+ async function gmxFetchMarkets(chainId) {
174
+ const sdk = getGmxApiSdk(chainId);
175
+ const markets = await sdk.fetchMarkets();
176
+ return markets.filter((m) => !m.isSpotOnly && m.isListed !== false).sort((a, b) => a.symbol.localeCompare(b.symbol, void 0, { sensitivity: "base" }));
177
+ }
178
+ async function gmxFetchTokens(chainId) {
179
+ const sdk = getGmxApiSdk(chainId);
180
+ return sdk.fetchTokens();
181
+ }
182
+ async function gmxResolveTokenBySymbol(chainId, symbol) {
183
+ const sym = symbol.trim().toUpperCase();
184
+ const tokens = await gmxFetchTokens(chainId);
185
+ const row = tokens.find((t) => t.symbol?.toUpperCase() === sym);
186
+ if (!row) throw new Error(`GMX token symbol not found on chain ${chainId}: ${symbol}`);
187
+ return row;
188
+ }
189
+ async function gmxIsCollateralTokenSupported(chainId, tokenAddress) {
190
+ try {
191
+ const addr = viem.getAddress(tokenAddress).toLowerCase();
192
+ const tokens = await gmxFetchTokens(chainId);
193
+ return tokens.some((t) => t.address?.toLowerCase() === addr);
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+ function gmxMarketCollateralTokens(marketSymbol) {
199
+ const m = marketSymbol.match(/\[([^[\]]+)\]/);
200
+ if (!m?.[1]) return [];
201
+ return m[1].split("-").map((s) => s.trim()).filter(Boolean);
202
+ }
203
+ function gmxMarketAcceptsCollateral(marketSymbol, collateralSymbol) {
204
+ const col = collateralSymbol.trim().toUpperCase();
205
+ if (!col) return true;
206
+ const tokens = gmxMarketCollateralTokens(marketSymbol);
207
+ if (tokens.length === 0) return true;
208
+ return tokens.some((t) => t.toUpperCase() === col);
209
+ }
210
+ async function gmxMarketsForCollateralSymbol(chainId, collateralSymbol) {
211
+ const markets = await gmxFetchMarkets(chainId);
212
+ return markets.filter((m) => gmxMarketAcceptsCollateral(m.symbol, collateralSymbol));
213
+ }
214
+ async function gmxResolveMarketBySymbol(chainId, symbol) {
215
+ const sym = symbol.trim();
216
+ const markets = await gmxFetchMarkets(chainId);
217
+ const row = markets.find((m) => m.symbol === sym);
218
+ if (!row) throw new Error(`GMX market symbol not found on chain ${chainId}: ${symbol}`);
219
+ if (!row.longTokenAddress || !row.shortTokenAddress) {
220
+ throw new Error(`GMX market "${symbol}" is missing long/short token addresses.`);
221
+ }
222
+ return row;
223
+ }
224
+ async function gmxResolveMarketByGmTokenAddress(chainId, gmTokenAddress) {
225
+ const addr = viem.getAddress(gmTokenAddress).toLowerCase();
226
+ const markets = await gmxFetchMarkets(chainId);
227
+ return markets.find((m) => m.marketTokenAddress?.toLowerCase() === addr) ?? null;
228
+ }
229
+ async function gmxFetchGmMarketsSummary(chainId) {
230
+ const markets = await gmxFetchMarkets(chainId);
231
+ const tokens = await gmxFetchTokens(chainId);
232
+ const tokenByAddr = new Map(tokens.map((t) => [t.address.toLowerCase(), t.symbol]));
233
+ const sdk = getGmxApiSdk(chainId);
234
+ const emptyApyMarkets = {};
235
+ const apyRes = await sdk.fetchApy().catch(() => ({ markets: emptyApyMarkets }));
236
+ const apyByMarket = apyRes.markets ?? emptyApyMarkets;
237
+ return markets.map(
238
+ (m) => gmxEnrichGmMarketRowWithApy(
239
+ {
240
+ symbol: m.symbol,
241
+ marketTokenAddress: m.marketTokenAddress,
242
+ longTokenSymbol: tokenByAddr.get((m.longTokenAddress ?? "").toLowerCase()) ?? null,
243
+ shortTokenSymbol: tokenByAddr.get((m.shortTokenAddress ?? "").toLowerCase()) ?? null
244
+ },
245
+ apyByMarket
246
+ )
247
+ );
248
+ }
249
+ function formatGmxUsd30(value) {
250
+ if (value == null) return null;
251
+ let n;
252
+ try {
253
+ n = typeof value === "bigint" ? value : BigInt(String(value));
254
+ } catch {
255
+ return null;
256
+ }
257
+ if (n <= 0n) return null;
258
+ const negative = n < 0n;
259
+ const abs = negative ? -n : n;
260
+ const whole = abs / 10n ** 30n;
261
+ const frac = abs % 10n ** 30n;
262
+ const fracStr = frac.toString().padStart(30, "0").replace(/0+$/, "");
263
+ const num = fracStr ? `${whole}.${fracStr}` : whole.toString();
264
+ const parsed = Number(num);
265
+ if (!Number.isFinite(parsed)) return null;
266
+ const formatted = Math.abs(parsed).toLocaleString("en-US", {
267
+ minimumFractionDigits: 0,
268
+ maximumFractionDigits: parsed >= 100 ? 2 : 4
269
+ });
270
+ return negative ? `-$${formatted}` : `$${formatted}`;
271
+ }
272
+ function formatGmxUsd30HumanInput(value) {
273
+ if (value == null) return null;
274
+ let n;
275
+ try {
276
+ n = typeof value === "bigint" ? value : BigInt(String(value));
277
+ } catch {
278
+ return null;
279
+ }
280
+ if (n <= 0n) return null;
281
+ const negative = n < 0n;
282
+ const abs = negative ? -n : n;
283
+ const whole = abs / 10n ** 30n;
284
+ const frac = abs % 10n ** 30n;
285
+ const fracStr = frac.toString().padStart(30, "0").replace(/0+$/, "");
286
+ const raw = fracStr ? `${whole}.${fracStr}` : whole.toString();
287
+ return negative ? `-${raw}` : raw;
288
+ }
289
+ function formatGmxTokenAmountHuman(amountWei, decimals) {
290
+ const raw = viem.formatUnits(amountWei, decimals);
291
+ if (!raw.includes(".")) return raw;
292
+ return raw.replace(/\.?0+$/, "");
293
+ }
294
+ function formatGmxLeverageBps(value) {
295
+ if (value == null) return null;
296
+ let n;
297
+ try {
298
+ n = typeof value === "bigint" ? value : BigInt(String(value));
299
+ } catch {
300
+ return null;
301
+ }
302
+ if (n <= 0n) return null;
303
+ const x = Number(n) / 1e4;
304
+ if (!Number.isFinite(x) || x <= 0) return null;
305
+ return `${x.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}\xD7`;
306
+ }
307
+ var require3 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
308
+ var { hydrateMarketInfo, getMarketAddressByName } = require3("@gmx-io/sdk/utils/markets");
309
+ var GMX_DEFAULT_MIN_COLLATERAL_USD = 10n * 10n ** 30n;
310
+ var contextByChain = /* @__PURE__ */ new Map();
311
+ function tokensArrayToMap(tokensArr) {
312
+ const tokensData = {};
313
+ for (const t of tokensArr) {
314
+ const address = String(t.address ?? "");
315
+ if (address) tokensData[address] = t;
316
+ }
317
+ return tokensData;
318
+ }
319
+ async function loadGmxMarketContext(chainId) {
320
+ let pending = contextByChain.get(chainId);
321
+ if (!pending) {
322
+ pending = (async () => {
323
+ const sdk = getGmxApiSdk(chainId);
324
+ const [marketsInfoArr, tokensArr] = await Promise.all([
325
+ sdk.fetchMarketsInfo(),
326
+ sdk.fetchTokensData()
327
+ ]);
328
+ const tokensData = tokensArrayToMap(tokensArr);
329
+ const marketsInfoData = {};
330
+ for (const raw of marketsInfoArr) {
331
+ const hydrated = hydrateMarketInfo({ chainId, rawMarketInfo: raw, tokensData });
332
+ if (hydrated?.marketTokenAddress) {
333
+ marketsInfoData[String(hydrated.marketTokenAddress)] = hydrated;
334
+ }
335
+ }
336
+ return {
337
+ marketsInfoData,
338
+ tokensData,
339
+ minCollateralUsd: GMX_DEFAULT_MIN_COLLATERAL_USD
340
+ };
341
+ })();
342
+ contextByChain.set(chainId, pending);
343
+ }
344
+ return pending;
345
+ }
346
+ async function resolveGmxMarketBySymbol(chainId, symbol) {
347
+ const ctx = await loadGmxMarketContext(chainId);
348
+ const trimmed = symbol.trim();
349
+ if (!trimmed) return void 0;
350
+ const address = getMarketAddressByName(ctx.marketsInfoData, trimmed);
351
+ if (!address) return void 0;
352
+ return ctx.marketsInfoData[address];
353
+ }
354
+ async function resolveGmxTokenBySymbol(chainId, symbol) {
355
+ const ctx = await loadGmxMarketContext(chainId);
356
+ const sym = symbol.trim().toUpperCase();
357
+ return Object.values(ctx.tokensData).find((t) => String(t.symbol ?? "").toUpperCase() === sym);
358
+ }
359
+
360
+ // src/protocols/evm/gmx/positionDisplay.ts
361
+ var require4 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
362
+ var { getPositionInfo } = require4("@gmx-io/sdk/utils/positions");
363
+ function asBigint(v) {
364
+ if (v == null) return void 0;
365
+ try {
366
+ return typeof v === "bigint" ? v : BigInt(String(v));
367
+ } catch {
368
+ return void 0;
369
+ }
370
+ }
371
+ function pickIndexName(raw, enriched) {
372
+ const name = String(enriched?.indexName ?? raw.indexName ?? "").trim();
373
+ if (name) return name;
374
+ const market = String(raw.marketAddress ?? "").trim();
375
+ return market ? `Market ${market.slice(0, 6)}\u2026` : "Position";
376
+ }
377
+ async function gmxFormatPositionsForDisplay(chainId, positions) {
378
+ if (positions.length === 0) return [];
379
+ const ctx = await loadGmxMarketContext(chainId);
380
+ return positions.map((raw, i) => {
381
+ const marketAddress = String(raw.marketAddress ?? "");
382
+ const marketInfo = marketAddress ? ctx.marketsInfoData[marketAddress] : void 0;
383
+ let enriched;
384
+ if (marketInfo) {
385
+ try {
386
+ enriched = getPositionInfo({
387
+ position: raw,
388
+ marketInfo,
389
+ minCollateralUsd: ctx.minCollateralUsd,
390
+ userReferralInfo: void 0,
391
+ showPnlInLeverage: false,
392
+ uiFeeFactor: 0n
393
+ });
394
+ } catch {
395
+ enriched = void 0;
396
+ }
397
+ }
398
+ const source = enriched ?? raw;
399
+ const collateralToken = source.collateralToken;
400
+ return {
401
+ key: String(raw.key ?? raw.contractKey ?? `${marketAddress}-${i}`),
402
+ indexName: pickIndexName(raw, enriched),
403
+ isLong: Boolean(source.isLong ?? raw.isLong),
404
+ sizeUsd: formatGmxUsd30(asBigint(source.sizeInUsd ?? raw.sizeInUsd)),
405
+ collateralUsd: formatGmxUsd30(asBigint(source.collateralUsd ?? raw.collateralUsd)),
406
+ collateralSymbol: collateralToken?.symbol ? String(collateralToken.symbol) : null,
407
+ entryPriceUsd: formatGmxUsd30(asBigint(source.entryPrice)),
408
+ markPriceUsd: formatGmxUsd30(asBigint(source.markPrice)),
409
+ liquidationPriceUsd: formatGmxUsd30(asBigint(source.liquidationPrice)),
410
+ leverageLabel: formatGmxLeverageBps(asBigint(source.leverage ?? source.leverageWithoutPnl)),
411
+ pnlUsd: formatGmxUsd30(asBigint(source.pnl))
412
+ };
413
+ });
414
+ }
415
+ var require5 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
416
+ var { getMaxAllowedLeverage } = require5("@gmx-io/sdk/utils/markets");
417
+ function gmxMarketMaxLeverageBps(marketInfo) {
418
+ return getMaxAllowedLeverage({
419
+ minCollateralFactor: marketInfo.minCollateralFactor,
420
+ minCollateralFactorForLiquidation: marketInfo.minCollateralFactorForLiquidation,
421
+ positionFeeFactorForBalanceWasNotImproved: marketInfo.positionFeeFactorForBalanceWasNotImproved
422
+ });
423
+ }
424
+ function gmxMarketMaxLeverageLabel(marketInfo) {
425
+ const bps = gmxMarketMaxLeverageBps(marketInfo);
426
+ if (!Number.isFinite(bps) || bps <= 0) return null;
427
+ return formatGmxLeverageBps(BigInt(bps));
428
+ }
429
+ var require6 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
430
+ var { getContract } = require6("@gmx-io/sdk/configs/contracts");
431
+ function gmxContractAddress(chainId, name) {
432
+ const raw = getContract(chainId, name);
433
+ if (!raw || raw === "0x0000000000000000000000000000000000000000") {
434
+ throw new Error(`GMX contract "${name}" is not configured for chain ${chainId}.`);
435
+ }
436
+ return raw;
437
+ }
438
+
439
+ // src/protocols/evm/gmx/reads.ts
440
+ async function gmxFetchPositionsForExecutor(args) {
441
+ const sdk = getGmxApiSdk(args.chainId);
442
+ const positions = await sdk.fetchPositionsInfo({
443
+ address: args.executorAddress,
444
+ includeRelatedOrders: true
445
+ });
446
+ return {
447
+ positions
448
+ };
449
+ }
450
+ async function gmxFetchPositionDisplayRows(args) {
451
+ const { positions } = await gmxFetchPositionsForExecutor(args);
452
+ return gmxFormatPositionsForDisplay(args.chainId, positions);
453
+ }
454
+ async function gmxFetchOrdersForExecutor(args) {
455
+ const sdk = getGmxApiSdk(args.chainId);
456
+ return sdk.fetchOrders({ address: args.executorAddress });
457
+ }
458
+ async function gmxFetchMarketsSummary(args) {
459
+ const [markets, ctx] = await Promise.all([gmxFetchMarkets(args.chainId), loadGmxMarketContext(args.chainId)]);
460
+ return {
461
+ markets: markets.map((m) => {
462
+ const addr = String(m.marketTokenAddress ?? "");
463
+ const info = ctx.marketsInfoData[addr] ?? ctx.marketsInfoData[addr.toLowerCase()] ?? Object.values(ctx.marketsInfoData).find(
464
+ (row) => String(row.marketTokenAddress ?? "").toLowerCase() === addr.toLowerCase()
465
+ );
466
+ return {
467
+ symbol: m.symbol,
468
+ marketTokenAddress: m.marketTokenAddress,
469
+ maxLeverageLabel: info ? gmxMarketMaxLeverageLabel(info) : null
470
+ };
471
+ })
472
+ };
473
+ }
474
+ async function gmxFetchCollateralTokens(args) {
475
+ const tokens = await gmxFetchTokens(args.chainId);
476
+ return tokens.map((t) => ({
477
+ symbol: t.symbol,
478
+ address: t.address,
479
+ decimals: t.decimals
480
+ }));
481
+ }
482
+ async function gmxFetchGmMarkets(args) {
483
+ const markets = await gmxFetchGmMarketsSummary(args.chainId);
484
+ return { markets };
485
+ }
486
+ async function gmxFetchGmApy(args) {
487
+ const { markets } = await gmxFetchGmMarkets(args);
488
+ const sorted = [...markets].sort((a, b) => (b.apy ?? 0) - (a.apy ?? 0));
489
+ return { markets: sorted };
490
+ }
491
+ async function gmxFetchStakingPower(args) {
492
+ const sdk = getGmxApiSdk(args.chainId);
493
+ const power = await sdk.fetchStakingPower({ address: args.executorAddress });
494
+ return {
495
+ stakingPower: JSON.parse(JSON.stringify(power, (_, v) => typeof v === "bigint" ? v.toString() : v))
496
+ };
497
+ }
498
+ async function gmxFetchGmMarketForToken(args) {
499
+ const market = await gmxResolveMarketByGmTokenAddress(args.chainId, args.gmTokenAddress);
500
+ if (!market) return { market: null };
501
+ const tokens = await gmxFetchTokens(args.chainId);
502
+ const tokenByAddr = new Map(tokens.map((t) => [t.address.toLowerCase(), t]));
503
+ const long = tokenByAddr.get((market.longTokenAddress ?? "").toLowerCase());
504
+ const short = tokenByAddr.get((market.shortTokenAddress ?? "").toLowerCase());
505
+ return {
506
+ market: {
507
+ symbol: market.symbol,
508
+ marketTokenAddress: market.marketTokenAddress,
509
+ longTokenSymbol: long?.symbol ?? null,
510
+ shortTokenSymbol: short?.symbol ?? null,
511
+ gmDecimals: 18
512
+ }
513
+ };
514
+ }
515
+ function gmxGmxTokenAddress(chainId) {
516
+ return gmxContractAddress(chainId, "GMX");
517
+ }
518
+ async function gmxIsGmMarketTokenAddress(chainId, tokenAddress) {
519
+ try {
520
+ const market = await gmxResolveMarketByGmTokenAddress(chainId, tokenAddress);
521
+ return market != null;
522
+ } catch {
523
+ return false;
524
+ }
525
+ }
526
+ function gmxIsGmxStakeTokenAddress(chainId, tokenAddress) {
527
+ try {
528
+ return viem.getAddress(tokenAddress).toLowerCase() === gmxGmxTokenAddress(chainId).toLowerCase();
529
+ } catch {
530
+ return false;
531
+ }
532
+ }
533
+
534
+ // src/core/purpose.ts
535
+ function mergePurposeText(purposeText, purposeSuffix) {
536
+ const t = purposeText.trim();
537
+ const suffix = (purposeSuffix ?? "").trim();
538
+ if (!suffix) return t;
539
+ return t ? `${t}
540
+
541
+ ${suffix}` : suffix;
542
+ }
543
+
544
+ // src/core/envelope.ts
545
+ function finalizeMultisign(input) {
546
+ const { keyGen, destinationChainID, legs } = input;
547
+ if (legs.length === 0) {
548
+ throw new Error("finalizeMultisign requires at least one leg");
549
+ }
550
+ const ph = (keyGen.pubkeyhex ?? "").trim();
551
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
552
+ const keyList = keyGen.keylist ?? [];
553
+ const clientId = continuumNodeSdk.getClientIdFromKeyGenResult(keyGen);
554
+ const first = legs[0];
555
+ const messageHashes = legs.map((l) => l.msgHash);
556
+ const messageRawBatch = legs.map((l) => l.msgRaw);
557
+ const batchMeta = legs.map((l) => ({
558
+ destinationAddress: l.destinationAddress,
559
+ signatureText: l.signatureText,
560
+ ...l.audit
561
+ }));
562
+ const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
563
+ const extraPayload = {
564
+ batchMeta,
565
+ ...input.extraJSON ?? {}
566
+ };
567
+ const extraJSON = JSON.stringify(extraPayload);
568
+ const bodyForSign = {
569
+ keyList,
570
+ pubKey: ph,
571
+ msgHash: messageHashes[0],
572
+ msgRaw: first.msgRaw,
573
+ destinationChainID,
574
+ destinationAddress: input.destinationAddress ?? first.destinationAddress,
575
+ extraJSON,
576
+ signatureText: first.signatureText,
577
+ purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
578
+ ...first.feeSnapshot
579
+ };
580
+ if (legs.length > 1) {
581
+ bodyForSign.messageHashes = messageHashes;
582
+ bodyForSign.messageRawBatch = messageRawBatch;
583
+ }
584
+ if (proposalTxParams.length > 0) {
585
+ bodyForSign.proposalTxParams = proposalTxParams;
586
+ }
587
+ const valueWei = first.valueWei;
588
+ if (valueWei != null && valueWei > 0n) {
589
+ bodyForSign.value = valueWei.toString();
590
+ }
591
+ if (clientId) bodyForSign.clientId = clientId;
592
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
593
+ }
594
+ function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
595
+ if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
596
+ return continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
597
+ }
598
+ return (estimatedGas * 12n + 9n) / 10n;
599
+ }
600
+
601
+ // src/chains/evm/buildBatch.ts
602
+ async function buildEvmMultisignBatch(args) {
603
+ const { context, steps } = args;
604
+ const {
605
+ chainId,
606
+ rpcUrl,
607
+ executorAddress,
608
+ chainDetail,
609
+ useCustomGas,
610
+ customGasChainDetails,
611
+ keyGen,
612
+ purposeText
613
+ } = context;
614
+ if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
615
+ const ch = viem.defineChain({
616
+ id: chainId,
617
+ name: "Destination",
618
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
619
+ rpcUrls: { default: { http: [rpcUrl] } }
620
+ });
621
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
622
+ const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
623
+ const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
624
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
625
+ const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
626
+ const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
627
+ const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
628
+ const executor = viem.getAddress(executorAddress);
629
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
630
+ const legs = [];
631
+ for (let i = 0; i < steps.length; i++) {
632
+ const step = steps[i];
633
+ const currentNonce = baseNonce + i;
634
+ let estimatedGas;
635
+ if (args.estimateGasForStep) {
636
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
637
+ } else {
638
+ try {
639
+ estimatedGas = await publicClient.estimateGas({
640
+ to: step.to,
641
+ data: step.data,
642
+ value: step.value,
643
+ account: executor
644
+ });
645
+ } catch {
646
+ estimatedGas = step.fallbackGas ?? 100000n;
647
+ }
648
+ }
649
+ let gasLimitI;
650
+ if (args.resolveGasLimit) {
651
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
652
+ } else if (step.routerSwap) {
653
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
654
+ } else {
655
+ gasLimitI = useCustomGas ? continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
656
+ }
657
+ let proposalTxParams;
658
+ let feeSnapshot;
659
+ let serialized;
660
+ if (legacy) {
661
+ let gasPriceWei = await publicClient.getGasPrice();
662
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
663
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
664
+ }
665
+ if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
666
+ const configured = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(Number(chainDetail.gasPrice)));
667
+ if (configured > gasPriceWei) gasPriceWei = configured;
668
+ }
669
+ serialized = viem.serializeTransaction({
670
+ type: "legacy",
671
+ to: step.to,
672
+ data: step.data,
673
+ value: step.value,
674
+ gas: gasLimitI,
675
+ gasPrice: gasPriceWei,
676
+ nonce: currentNonce,
677
+ chainId
678
+ });
679
+ proposalTxParams = {
680
+ nonce: currentNonce,
681
+ gasLimit: gasLimitI.toString(),
682
+ txType: "legacy",
683
+ gasPrice: gasPriceWei.toString()
684
+ };
685
+ feeSnapshot = continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams);
686
+ } else {
687
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
688
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
689
+ const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
690
+ const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
691
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
692
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
693
+ const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
694
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
695
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
696
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(continuumNodeSdk.gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
697
+ let maxFeePerGas = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(maxFeePerGasGwei));
698
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
699
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
700
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
701
+ }
702
+ ({ maxFeePerGas, maxPriorityFeePerGas } = continuumNodeSdk.alignEip1559FeesWithLatestBase(
703
+ maxFeePerGas,
704
+ maxPriorityFeePerGas,
705
+ latestBaseFeeWei
706
+ ));
707
+ serialized = viem.serializeTransaction({
708
+ type: "eip1559",
709
+ to: step.to,
710
+ data: step.data,
711
+ value: step.value,
712
+ gas: gasLimitI,
713
+ maxFeePerGas,
714
+ maxPriorityFeePerGas,
715
+ nonce: currentNonce,
716
+ chainId
717
+ });
718
+ proposalTxParams = {
719
+ nonce: currentNonce,
720
+ gasLimit: gasLimitI.toString(),
721
+ txType: "eip1559",
722
+ maxFeePerGas: maxFeePerGas.toString(),
723
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
724
+ };
725
+ feeSnapshot = i === 0 ? continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
726
+ }
727
+ const h = viem.keccak256(serialized);
728
+ const msgHash = h.startsWith("0x") ? h.slice(2) : h;
729
+ const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
730
+ legs.push({
731
+ msgHash,
732
+ msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
733
+ destinationAddress: step.to,
734
+ signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
735
+ audit: batchMetaExtra,
736
+ feeSnapshot: i === 0 ? feeSnapshot : {},
737
+ proposalTxParams,
738
+ valueWei: i === 0 ? step.value : void 0
739
+ });
740
+ if (i === 0 && args.firstMsgRawNo0x != null) {
741
+ legs[0].msgRaw = args.firstMsgRawNo0x;
742
+ }
743
+ }
744
+ const extraJSON = {};
745
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
746
+ extraJSON.customGasChainDetails = customGasChainDetails;
747
+ }
748
+ const result = finalizeMultisign({
749
+ keyGen,
750
+ purposeText,
751
+ purposeSuffix: args.purposeSuffix,
752
+ destinationChainID: String(chainId),
753
+ destinationAddress: args.destinationAddress ?? steps[0].to,
754
+ legs,
755
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
756
+ });
757
+ const pv = args.payableValueWei;
758
+ if (pv != null && pv > 0n) {
759
+ result.bodyForSign.value = pv.toString();
760
+ }
761
+ return result;
762
+ }
763
+ var GMX_WRAPPED_NATIVE_SYMBOL_BY_CHAIN = {
764
+ 42161: "WETH",
765
+ 43114: "WAVAX"
766
+ };
767
+ var GMX_NATIVE_GAS_SYMBOL_BY_CHAIN = {
768
+ 42161: "ETH",
769
+ 43114: "AVAX"
770
+ };
771
+ function gmxWrappedNativeSymbolForChain(chainId) {
772
+ if (!isGmxChainSupported(chainId)) return null;
773
+ return GMX_WRAPPED_NATIVE_SYMBOL_BY_CHAIN[chainId] ?? null;
774
+ }
775
+ function gmxNativeGasSymbolForChain(chainId) {
776
+ if (!isGmxChainSupported(chainId)) return null;
777
+ return GMX_NATIVE_GAS_SYMBOL_BY_CHAIN[chainId] ?? null;
778
+ }
779
+ function gmxFindWrappedNativeToken(tokens, chainId) {
780
+ const sym = gmxWrappedNativeSymbolForChain(chainId);
781
+ if (!sym) return null;
782
+ return tokens.find((t) => t.symbol?.toUpperCase() === sym.toUpperCase()) ?? null;
783
+ }
784
+ function gmxKeyForNodeAssetRow(args) {
785
+ const raw = (args.contractAddress ?? "").trim();
786
+ try {
787
+ const a = viem.getAddress(raw);
788
+ if (a.toLowerCase() === "0x0000000000000000000000000000000000000000" && args.nativeWrapped) {
789
+ return viem.getAddress(args.nativeWrapped.trim()).toLowerCase();
790
+ }
791
+ return a.toLowerCase();
792
+ } catch {
793
+ return null;
794
+ }
795
+ }
796
+ var require7 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
797
+ var { getIncreasePositionAmounts, getNextPositionValuesForIncreaseTrade } = require7("@gmx-io/sdk/utils/trade");
798
+ var { getDecreasePositionAmounts, getNextPositionValuesForDecreaseTrade } = require7("@gmx-io/sdk/utils/trade");
799
+ var { createFindSwapPath } = require7("@gmx-io/sdk/utils/swap");
800
+ var { SwapPricingType } = require7("@gmx-io/sdk/utils/orders");
801
+ var { convertToTokenAmount } = require7("@gmx-io/sdk/utils/tokens");
802
+ var { getPositionInfo: getPositionInfo2 } = require7("@gmx-io/sdk/utils/positions");
803
+ function asBigint2(v) {
804
+ if (v == null) return void 0;
805
+ try {
806
+ return typeof v === "bigint" ? v : BigInt(String(v));
807
+ } catch {
808
+ return void 0;
809
+ }
810
+ }
811
+ function findSwapPathForCollateral(args) {
812
+ return createFindSwapPath({
813
+ chainId: args.chainId,
814
+ fromTokenAddress: args.fromToken.address,
815
+ toTokenAddress: args.collateralToken.address,
816
+ marketsInfoData: args.marketsInfoData,
817
+ gasEstimationParams: { gasLimits: {}, gasPrice: 1n, tokensData: args.tokensData },
818
+ swapPricingType: SwapPricingType.Swap
819
+ });
820
+ }
821
+ async function loadIncreaseSimContext(args) {
822
+ const market = await resolveGmxMarketBySymbol(args.chainId, args.symbol);
823
+ if (!market) throw new Error(`GMX market not found: ${args.symbol}`);
824
+ const collateralToken = await resolveGmxTokenBySymbol(args.chainId, args.collateralToken);
825
+ if (!collateralToken) throw new Error(`GMX collateral token not found: ${args.collateralToken}`);
826
+ const ctx = await loadGmxMarketContext(args.chainId);
827
+ const fromToken = collateralToken;
828
+ const isLong = args.direction === "long";
829
+ const sizeUsd = parseGmxUsd30(args.sizeUsdHuman);
830
+ const indexToken = market.indexToken;
831
+ const indexPrices = indexToken.prices;
832
+ const indexPrice = asBigint2(indexPrices?.minPrice);
833
+ if (!indexPrice || indexPrice <= 0n) throw new Error("GMX index price unavailable for estimate.");
834
+ const indexTokenAmount = convertToTokenAmount(sizeUsd, Number(indexToken.decimals ?? 18), indexPrice);
835
+ const findSwapPath = findSwapPathForCollateral({
836
+ chainId: args.chainId,
837
+ fromToken,
838
+ collateralToken,
839
+ marketsInfoData: ctx.marketsInfoData,
840
+ tokensData: ctx.tokensData
841
+ });
842
+ let existingPosition;
843
+ if (args.executorAddress?.trim()) {
844
+ const sdk = getGmxApiSdk(args.chainId);
845
+ const positions = await sdk.fetchPositionsInfo({ address: args.executorAddress.trim() });
846
+ existingPosition = positions.find(
847
+ (p) => String(p.marketAddress ?? "").toLowerCase() === String(market.marketTokenAddress ?? "").toLowerCase() && Boolean(p.isLong) === isLong
848
+ );
849
+ if (existingPosition && ctx.marketsInfoData[String(market.marketTokenAddress)]) {
850
+ existingPosition = getPositionInfo2({
851
+ position: existingPosition,
852
+ marketInfo: market,
853
+ minCollateralUsd: ctx.minCollateralUsd,
854
+ userReferralInfo: void 0,
855
+ showPnlInLeverage: false,
856
+ uiFeeFactor: 0n
857
+ });
858
+ }
859
+ }
860
+ const maxLeverageBps = gmxMarketMaxLeverageBps(market);
861
+ return {
862
+ chainId: args.chainId,
863
+ market,
864
+ collateralToken,
865
+ fromToken,
866
+ isLong,
867
+ sizeUsd,
868
+ indexToken,
869
+ indexTokenAmount,
870
+ findSwapPath,
871
+ existingPosition,
872
+ ctx,
873
+ maxLeverageBps,
874
+ maxLeverageLabel: gmxMarketMaxLeverageLabel(market),
875
+ collateralDecimals: Number(collateralToken.decimals ?? 18)
876
+ };
877
+ }
878
+ function simulateIncreaseAtCollateralWei(sim, collateralAmount) {
879
+ const increaseAmounts = getIncreasePositionAmounts({
880
+ marketInfo: sim.market,
881
+ indexToken: sim.indexToken,
882
+ initialCollateralToken: sim.fromToken,
883
+ collateralToken: sim.collateralToken,
884
+ isLong: sim.isLong,
885
+ initialCollateralAmount: collateralAmount,
886
+ position: sim.existingPosition,
887
+ indexTokenAmount: sim.indexTokenAmount,
888
+ strategy: "independent",
889
+ findSwapPath: sim.findSwapPath,
890
+ uiFeeFactor: 0n,
891
+ marketsInfoData: sim.ctx.marketsInfoData,
892
+ chainId: sim.chainId,
893
+ externalSwapQuote: void 0,
894
+ externalSwapQuoteParams: void 0,
895
+ isSetAcceptablePriceImpactEnabled: false,
896
+ userReferralInfo: void 0
897
+ });
898
+ const next = getNextPositionValuesForIncreaseTrade({
899
+ existingPosition: sim.existingPosition,
900
+ marketInfo: sim.market,
901
+ collateralToken: sim.collateralToken,
902
+ positionPriceImpactDeltaUsd: increaseAmounts.positionPriceImpactDeltaUsd,
903
+ sizeDeltaUsd: increaseAmounts.sizeDeltaUsd,
904
+ sizeDeltaInTokens: increaseAmounts.sizeDeltaInTokens,
905
+ collateralDeltaUsd: increaseAmounts.collateralDeltaUsd,
906
+ collateralDeltaAmount: increaseAmounts.collateralDeltaAmount,
907
+ indexPrice: increaseAmounts.indexPrice,
908
+ isLong: sim.isLong,
909
+ showPnlInLeverage: false,
910
+ minCollateralUsd: sim.ctx.minCollateralUsd,
911
+ userReferralInfo: void 0
912
+ });
913
+ return {
914
+ nextLeverageBps: asBigint2(next.nextLeverage) ?? null,
915
+ nextEntryPrice: asBigint2(next.nextEntryPrice) ?? null,
916
+ nextLiqPrice: asBigint2(next.nextLiqPrice) ?? null
917
+ };
918
+ }
919
+ function findMinCollateralWeiForIncrease(sim) {
920
+ const maxBps = BigInt(sim.maxLeverageBps);
921
+ const collateralPrices = sim.collateralToken.prices;
922
+ const collateralPrice = asBigint2(collateralPrices?.minPrice);
923
+ if (!collateralPrice || collateralPrice <= 0n) return null;
924
+ let hi = convertToTokenAmount(sim.sizeUsd * 2n, sim.collateralDecimals, collateralPrice);
925
+ if (hi < 1n) hi = 1n;
926
+ const hiSim = simulateIncreaseAtCollateralWei(sim, hi);
927
+ if (hiSim.nextLeverageBps == null || hiSim.nextLeverageBps > maxBps) {
928
+ return null;
929
+ }
930
+ let lo = 0n;
931
+ while (lo < hi) {
932
+ const mid = (lo + hi) / 2n;
933
+ const probe = mid === 0n ? 1n : mid;
934
+ const { nextLeverageBps } = simulateIncreaseAtCollateralWei(sim, probe);
935
+ if (nextLeverageBps != null && nextLeverageBps <= maxBps) {
936
+ hi = probe;
937
+ } else {
938
+ lo = probe + 1n;
939
+ }
940
+ }
941
+ return lo > 0n ? lo : 1n;
942
+ }
943
+ async function gmxEstimateIncreasePreview(args) {
944
+ const sim = await loadIncreaseSimContext(args);
945
+ const collateralAmount = parseGmxTokenAmount(args.collateralAmountHuman, sim.collateralDecimals);
946
+ const { nextLeverageBps, nextEntryPrice, nextLiqPrice } = simulateIncreaseAtCollateralWei(sim, collateralAmount);
947
+ const entry = formatGmxUsd30(nextEntryPrice);
948
+ const liq = formatGmxUsd30(nextLiqPrice);
949
+ const lev = formatGmxLeverageBps(nextLeverageBps);
950
+ const maxBps = BigInt(sim.maxLeverageBps);
951
+ const minWei = findMinCollateralWeiForIncrease(sim);
952
+ const minCollateralAmountHuman = minWei != null ? formatGmxTokenAmountHuman(minWei, sim.collateralDecimals) : null;
953
+ const exceedsMaxLeverage = nextLeverageBps != null && nextLeverageBps > maxBps || minWei != null && collateralAmount < minWei;
954
+ return {
955
+ direction: args.direction,
956
+ marketSymbol: args.symbol.trim(),
957
+ sizeUsdHuman: args.sizeUsdHuman.trim(),
958
+ collateralSymbol: args.collateralToken.trim(),
959
+ collateralAmountHuman: args.collateralAmountHuman.trim(),
960
+ openPositionSizeUsdHuman: null,
961
+ openPositionSizeUsdLabel: null,
962
+ entryPriceUsd: entry,
963
+ liquidationPriceUsd: liq,
964
+ leverageLabel: lev,
965
+ maxLeverageLabel: sim.maxLeverageLabel,
966
+ minCollateralAmountHuman,
967
+ exceedsMaxLeverage,
968
+ note: sim.existingPosition ? "Estimated after adding to your open position." : "Estimated for a new position."
969
+ };
970
+ }
971
+ async function gmxFindRawClosePosition(args) {
972
+ const market = await resolveGmxMarketBySymbol(args.chainId, args.symbol);
973
+ if (!market) return void 0;
974
+ const isLong = args.direction === "long";
975
+ const sdk = getGmxApiSdk(args.chainId);
976
+ const rawPositions = await sdk.fetchPositionsInfo({ address: args.executorAddress.trim() });
977
+ return rawPositions.find(
978
+ (p) => String(p.marketAddress ?? "").toLowerCase() === String(market.marketTokenAddress ?? "").toLowerCase() && Boolean(p.isLong) === isLong
979
+ );
980
+ }
981
+ async function gmxFetchClosePositionCap(args) {
982
+ const raw = await gmxFindRawClosePosition(args);
983
+ if (!raw) {
984
+ return { openPositionSizeUsdHuman: null, openPositionSizeUsdLabel: null };
985
+ }
986
+ const sizeInUsd = raw.sizeInUsd;
987
+ return {
988
+ openPositionSizeUsdHuman: formatGmxUsd30HumanInput(sizeInUsd),
989
+ openPositionSizeUsdLabel: formatGmxUsd30(sizeInUsd)
990
+ };
991
+ }
992
+ async function gmxEstimateDecreasePreview(args) {
993
+ const market = await resolveGmxMarketBySymbol(args.chainId, args.symbol);
994
+ if (!market) throw new Error(`GMX market not found: ${args.symbol}`);
995
+ const collateralToken = await resolveGmxTokenBySymbol(args.chainId, args.collateralToken);
996
+ if (!collateralToken) throw new Error(`GMX collateral token not found: ${args.collateralToken}`);
997
+ const ctx = await loadGmxMarketContext(args.chainId);
998
+ const isLong = args.direction === "long";
999
+ const closeSizeUsd = parseGmxUsd30(args.sizeUsdHuman);
1000
+ const raw = await gmxFindRawClosePosition({
1001
+ chainId: args.chainId,
1002
+ symbol: args.symbol,
1003
+ direction: args.direction,
1004
+ executorAddress: args.executorAddress
1005
+ });
1006
+ if (!raw) {
1007
+ return {
1008
+ direction: args.direction,
1009
+ marketSymbol: args.symbol.trim(),
1010
+ sizeUsdHuman: args.sizeUsdHuman.trim(),
1011
+ collateralSymbol: args.collateralToken.trim(),
1012
+ collateralAmountHuman: null,
1013
+ openPositionSizeUsdHuman: null,
1014
+ openPositionSizeUsdLabel: null,
1015
+ entryPriceUsd: null,
1016
+ liquidationPriceUsd: null,
1017
+ leverageLabel: null,
1018
+ maxLeverageLabel: gmxMarketMaxLeverageLabel(market),
1019
+ minCollateralAmountHuman: null,
1020
+ exceedsMaxLeverage: false,
1021
+ note: "No matching open position found for this market and direction."
1022
+ };
1023
+ }
1024
+ const sizeInUsd = raw.sizeInUsd;
1025
+ const openPositionSizeUsdHuman = formatGmxUsd30HumanInput(sizeInUsd);
1026
+ const openPositionSizeUsdLabel = formatGmxUsd30(sizeInUsd);
1027
+ const position = getPositionInfo2({
1028
+ position: raw,
1029
+ marketInfo: market,
1030
+ minCollateralUsd: ctx.minCollateralUsd,
1031
+ userReferralInfo: void 0,
1032
+ showPnlInLeverage: false,
1033
+ uiFeeFactor: 0n
1034
+ });
1035
+ const decreaseAmounts = getDecreasePositionAmounts({
1036
+ marketInfo: market,
1037
+ collateralToken: position.collateralToken ?? collateralToken,
1038
+ isLong,
1039
+ position,
1040
+ closeSizeUsd,
1041
+ keepLeverage: false,
1042
+ userReferralInfo: void 0,
1043
+ minCollateralUsd: ctx.minCollateralUsd,
1044
+ minPositionSizeUsd: 1n,
1045
+ uiFeeFactor: 0n,
1046
+ isSetAcceptablePriceImpactEnabled: false
1047
+ });
1048
+ const next = getNextPositionValuesForDecreaseTrade({
1049
+ existingPosition: position,
1050
+ marketInfo: market,
1051
+ collateralToken: position.collateralToken ?? collateralToken,
1052
+ sizeDeltaUsd: decreaseAmounts.sizeDeltaUsd,
1053
+ sizeDeltaInTokens: decreaseAmounts.sizeDeltaInTokens,
1054
+ realizedPnl: decreaseAmounts.realizedPnl,
1055
+ estimatedPnl: decreaseAmounts.estimatedPnl,
1056
+ collateralDeltaUsd: decreaseAmounts.collateralDeltaUsd,
1057
+ collateralDeltaAmount: decreaseAmounts.collateralDeltaAmount,
1058
+ payedRemainingCollateralUsd: decreaseAmounts.payedRemainingCollateralUsd,
1059
+ payedRemainingCollateralAmount: decreaseAmounts.payedRemainingCollateralAmount,
1060
+ proportionalPendingImpactDeltaUsd: decreaseAmounts.proportionalPendingImpactDeltaUsd,
1061
+ showPnlInLeverage: false,
1062
+ isLong,
1063
+ minCollateralUsd: ctx.minCollateralUsd,
1064
+ userReferralInfo: void 0
1065
+ });
1066
+ const nextSize = asBigint2(next.nextSizeUsd) ?? 0n;
1067
+ const liq = nextSize > 0n ? formatGmxUsd30(asBigint2(next.nextLiqPrice)) : null;
1068
+ const lev = nextSize > 0n ? formatGmxLeverageBps(asBigint2(next.nextLeverage)) : null;
1069
+ const fullClose = nextSize <= 0n;
1070
+ return {
1071
+ direction: args.direction,
1072
+ marketSymbol: args.symbol.trim(),
1073
+ sizeUsdHuman: args.sizeUsdHuman.trim(),
1074
+ collateralSymbol: args.collateralToken.trim(),
1075
+ collateralAmountHuman: null,
1076
+ openPositionSizeUsdHuman,
1077
+ openPositionSizeUsdLabel,
1078
+ entryPriceUsd: formatGmxUsd30(asBigint2(position.entryPrice)),
1079
+ liquidationPriceUsd: liq,
1080
+ leverageLabel: lev,
1081
+ maxLeverageLabel: gmxMarketMaxLeverageLabel(market),
1082
+ minCollateralAmountHuman: null,
1083
+ exceedsMaxLeverage: false,
1084
+ note: fullClose ? "Full close \u2014 no remaining position after this order." : "Estimated remaining position after this close."
1085
+ };
1086
+ }
1087
+ async function gmxAssertIncreaseOrderWithinMaxLeverage(args) {
1088
+ const estimate = await gmxEstimateIncreasePreview(args);
1089
+ if (!estimate.exceedsMaxLeverage) return;
1090
+ const maxLev = estimate.maxLeverageLabel ?? "market max";
1091
+ const minCol = estimate.minCollateralAmountHuman;
1092
+ const sym = estimate.collateralSymbol;
1093
+ if (minCol) {
1094
+ throw new Error(
1095
+ `Collateral is too low for ${maxLev} max leverage on ${estimate.marketSymbol}. Need at least ${minCol} ${sym}.`
1096
+ );
1097
+ }
1098
+ throw new Error(
1099
+ `Collateral is too low for ${maxLev} max leverage on ${estimate.marketSymbol}. Increase collateral or reduce size.`
1100
+ );
1101
+ }
1102
+ function gmxIsCloseSizeWithinPosition(args) {
1103
+ try {
1104
+ const close = parseGmxUsd30(args.closeSizeUsdHuman);
1105
+ const open = parseGmxUsd30(args.openPositionSizeUsdHuman);
1106
+ return close > 0n && close <= open;
1107
+ } catch {
1108
+ return false;
1109
+ }
1110
+ }
1111
+
1112
+ // src/protocols/evm/gmx/multisign.ts
1113
+ var GMX_ORDER_GAS_FALLBACK = 2500000n;
1114
+ var GMX_WETH_DEPOSIT_FALLBACK = 120000n;
1115
+ var GMX_ERC20_APPROVE_FALLBACK = 100000n;
1116
+ var wethDepositAbi = viem.parseAbi(["function deposit() payable"]);
1117
+ var erc20AllowanceAbi = viem.parseAbi([
1118
+ "function allowance(address owner, address spender) view returns (uint256)"
1119
+ ]);
1120
+ var erc20ApproveAbi = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
1121
+ function payloadToStep(payload) {
1122
+ if (!payload?.to || !payload?.data) {
1123
+ throw new Error("GMX prepareOrder did not return classic transaction calldata (to/data).");
1124
+ }
1125
+ const to = viem.getAddress(payload.to.trim());
1126
+ const raw = payload.data.toString();
1127
+ const data = raw.startsWith("0x") ? raw : `0x${raw}`;
1128
+ let value = 0n;
1129
+ if (payload.value != null) {
1130
+ value = typeof payload.value === "bigint" ? payload.value : BigInt(String(payload.value));
1131
+ }
1132
+ return { to, data, value, fallbackGas: GMX_ORDER_GAS_FALLBACK };
1133
+ }
1134
+ async function buildNativeCollateralPrepSteps(args) {
1135
+ const weth = viem.getAddress(args.nativeWrapped);
1136
+ const executor = viem.getAddress(args.executorAddress);
1137
+ const router = viem.getAddress(args.exchangeRouter);
1138
+ const token = await gmxResolveTokenBySymbol(args.chainId, args.collateralToken);
1139
+ const amountWei = parseGmxTokenAmount(args.collateralAmountHuman, token.decimals);
1140
+ if (amountWei === 0n) throw new Error("Collateral amount is zero after converting with token decimals.");
1141
+ const ch = viem.defineChain({
1142
+ id: args.chainId,
1143
+ name: "Destination",
1144
+ nativeCurrency: { decimals: 18, name: "Native", symbol: "N" },
1145
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1146
+ });
1147
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1148
+ const steps = [];
1149
+ const dataDeposit = viem.encodeFunctionData({ abi: wethDepositAbi, functionName: "deposit", args: [] });
1150
+ steps.push({ to: weth, data: dataDeposit, value: amountWei, fallbackGas: GMX_WETH_DEPOSIT_FALLBACK });
1151
+ const allowance = await publicClient.readContract({
1152
+ address: weth,
1153
+ abi: erc20AllowanceAbi,
1154
+ functionName: "allowance",
1155
+ args: [executor, router]
1156
+ });
1157
+ if (allowance < amountWei) {
1158
+ if (allowance > 0n) {
1159
+ steps.push({
1160
+ to: weth,
1161
+ data: viem.encodeFunctionData({ abi: erc20ApproveAbi, functionName: "approve", args: [router, 0n] }),
1162
+ value: 0n,
1163
+ fallbackGas: GMX_ERC20_APPROVE_FALLBACK
1164
+ });
1165
+ }
1166
+ steps.push({
1167
+ to: weth,
1168
+ data: viem.encodeFunctionData({ abi: erc20ApproveAbi, functionName: "approve", args: [router, amountWei] }),
1169
+ value: 0n,
1170
+ fallbackGas: GMX_ERC20_APPROVE_FALLBACK
1171
+ });
1172
+ }
1173
+ return steps;
1174
+ }
1175
+ async function buildGmxClassicPreparedMultisignBatch(common, prepared, signatureMeta, prepSteps = []) {
1176
+ const executor = viem.getAddress(common.executorAddress);
1177
+ const orderStep = payloadToStep(prepared.payload);
1178
+ const steps = [...prepSteps, orderStep];
1179
+ const firstStep = steps[0];
1180
+ const dataNo0x = firstStep.data.startsWith("0x") ? firstStep.data.slice(2) : firstStep.data;
1181
+ return buildEvmMultisignBatch({
1182
+ context: {
1183
+ chainCategory: "evm",
1184
+ keyGen: common.keyGen,
1185
+ purposeText: common.purposeText,
1186
+ chainId: common.chainId,
1187
+ rpcUrl: common.rpcUrl,
1188
+ executorAddress: executor,
1189
+ chainDetail: common.chainDetail,
1190
+ useCustomGas: common.useCustomGas,
1191
+ customGasChainDetails: common.customGasChainDetails
1192
+ },
1193
+ steps,
1194
+ purposeSuffix: "GMX: classic on-chain order (keeper-executed asynchronously).",
1195
+ firstMsgRawNo0x: dataNo0x,
1196
+ destinationAddress: firstStep.to,
1197
+ resolveGasLimit: async ({ estimatedGas, index }) => {
1198
+ const step = steps[index];
1199
+ const fallback = step?.fallbackGas ?? GMX_ORDER_GAS_FALLBACK;
1200
+ return estimatedGas > fallback ? estimatedGas : fallback;
1201
+ },
1202
+ buildBatchMeta: ({ index }) => {
1203
+ if (index < prepSteps.length) {
1204
+ const kind = index === 0 && prepSteps[0]?.value > 0n ? "weth_deposit" : "approve";
1205
+ return {
1206
+ signatureText: JSON.stringify({ kind: "GMX", step: kind, ...signatureMeta }),
1207
+ evm: { type: kind === "weth_deposit" ? "gmx_weth_deposit" : "gmx_erc20_approve", version: 1, chainId: String(common.chainId) }
1208
+ };
1209
+ }
1210
+ return {
1211
+ signatureText: JSON.stringify({
1212
+ kind: "GMX",
1213
+ mode: "classic",
1214
+ ...signatureMeta
1215
+ }),
1216
+ evm: { type: "gmx_classic_order", version: 1, chainId: String(common.chainId) }
1217
+ };
1218
+ }
1219
+ });
1220
+ }
1221
+ async function collateralToPay(collateralToken, collateralAmountHuman, chainId) {
1222
+ const token = await gmxResolveTokenBySymbol(chainId, collateralToken);
1223
+ const amount = parseGmxTokenAmount(collateralAmountHuman, token.decimals);
1224
+ return { amount, token: token.symbol };
1225
+ }
1226
+ function resolveIncreaseCollateralToken(args) {
1227
+ if (args.isNativeIn) {
1228
+ const wrapped = gmxWrappedNativeSymbolForChain(args.chainId);
1229
+ if (!wrapped) throw new Error("Wrapped native collateral symbol is not configured for this chain.");
1230
+ return wrapped;
1231
+ }
1232
+ return args.collateralToken.trim();
1233
+ }
1234
+ async function buildEvmMultisignBodyGmxIncreaseBatch(args) {
1235
+ const sdk = getGmxApiSdk(args.chainId);
1236
+ const executor = viem.getAddress(args.executorAddress);
1237
+ const size = parseGmxUsd30(args.sizeUsdHuman);
1238
+ const collateralSymbol = resolveIncreaseCollateralToken(args);
1239
+ await gmxAssertIncreaseOrderWithinMaxLeverage({
1240
+ chainId: args.chainId,
1241
+ symbol: args.symbol.trim(),
1242
+ direction: args.direction,
1243
+ sizeUsdHuman: args.sizeUsdHuman,
1244
+ collateralToken: collateralSymbol,
1245
+ collateralAmountHuman: args.collateralAmountHuman,
1246
+ executorAddress: executor
1247
+ });
1248
+ const collateralToPayArg = await collateralToPay(collateralSymbol, args.collateralAmountHuman, args.chainId);
1249
+ if (args.isNativeIn) {
1250
+ if (!args.nativeWrapped) throw new Error("nativeWrapped is required when isNativeIn is true.");
1251
+ const wrapped = gmxWrappedNativeSymbolForChain(args.chainId);
1252
+ if (collateralSymbol.toUpperCase() !== wrapped?.toUpperCase()) {
1253
+ throw new Error("Native GMX collateral path requires the chain wrapped native token.");
1254
+ }
1255
+ }
1256
+ const prepareArgs = {
1257
+ kind: "increase",
1258
+ symbol: args.symbol.trim(),
1259
+ direction: args.direction,
1260
+ orderType: args.orderType,
1261
+ size,
1262
+ collateralToken: collateralSymbol,
1263
+ collateralToPay: collateralToPayArg,
1264
+ mode: "classic",
1265
+ from: executor
1266
+ };
1267
+ if (args.orderType === "limit") {
1268
+ if (!args.triggerPriceUsdHuman?.trim()) throw new Error("Limit orders require triggerPriceUsdHuman.");
1269
+ prepareArgs.triggerPrice = parseGmxUsd30(args.triggerPriceUsdHuman);
1270
+ }
1271
+ if (args.slippageBps != null) prepareArgs.slippage = args.slippageBps;
1272
+ if (args.executionFeeBufferBps != null) prepareArgs.executionFeeBufferBps = args.executionFeeBufferBps;
1273
+ const prepared = await sdk.prepareOrder(prepareArgs);
1274
+ if (prepared.payloadType !== "transaction") {
1275
+ throw new Error("GMX prepareOrder returned typed-data; classic mode required for MPC multi-sign.");
1276
+ }
1277
+ const orderStep = payloadToStep(prepared.payload);
1278
+ let prepSteps = [];
1279
+ if (args.isNativeIn && args.nativeWrapped) {
1280
+ prepSteps = await buildNativeCollateralPrepSteps({
1281
+ chainId: args.chainId,
1282
+ rpcUrl: args.rpcUrl,
1283
+ executorAddress: executor,
1284
+ nativeWrapped: args.nativeWrapped,
1285
+ collateralToken: collateralSymbol,
1286
+ collateralAmountHuman: args.collateralAmountHuman,
1287
+ exchangeRouter: orderStep.to
1288
+ });
1289
+ }
1290
+ return buildGmxClassicPreparedMultisignBatch(
1291
+ args,
1292
+ prepared,
1293
+ {
1294
+ action: "increase",
1295
+ symbol: args.symbol,
1296
+ direction: args.direction,
1297
+ orderType: args.orderType,
1298
+ sizeUsdHuman: args.sizeUsdHuman,
1299
+ collateralToken: collateralSymbol,
1300
+ collateralAmountHuman: args.collateralAmountHuman,
1301
+ isNativeIn: Boolean(args.isNativeIn)
1302
+ },
1303
+ prepSteps
1304
+ );
1305
+ }
1306
+ async function buildEvmMultisignBodyGmxDecreaseBatch(args) {
1307
+ const sdk = getGmxApiSdk(args.chainId);
1308
+ const executor = viem.getAddress(args.executorAddress);
1309
+ const size = parseGmxUsd30(args.sizeUsdHuman);
1310
+ const prepareArgs = {
1311
+ kind: "decrease",
1312
+ symbol: args.symbol.trim(),
1313
+ direction: args.direction,
1314
+ orderType: args.orderType,
1315
+ size,
1316
+ collateralToken: args.collateralToken.trim(),
1317
+ mode: "classic",
1318
+ from: executor
1319
+ };
1320
+ if (args.isNativeOut) {
1321
+ const nativeSym = gmxNativeGasSymbolForChain(args.chainId);
1322
+ if (!nativeSym) throw new Error("Native receive symbol is not configured for this chain.");
1323
+ prepareArgs.receiveToken = nativeSym;
1324
+ } else if (args.receiveToken?.trim()) {
1325
+ prepareArgs.receiveToken = args.receiveToken.trim();
1326
+ }
1327
+ if (args.keepLeverage != null) prepareArgs.keepLeverage = args.keepLeverage;
1328
+ if (args.orderType === "limit") {
1329
+ if (!args.triggerPriceUsdHuman?.trim()) throw new Error("Limit orders require triggerPriceUsdHuman.");
1330
+ prepareArgs.triggerPrice = parseGmxUsd30(args.triggerPriceUsdHuman);
1331
+ }
1332
+ if (args.slippageBps != null) prepareArgs.slippage = args.slippageBps;
1333
+ if (args.executionFeeBufferBps != null) prepareArgs.executionFeeBufferBps = args.executionFeeBufferBps;
1334
+ const prepared = await sdk.prepareOrder(prepareArgs);
1335
+ if (prepared.payloadType !== "transaction") {
1336
+ throw new Error("GMX prepareOrder returned typed-data; classic mode required for MPC multi-sign.");
1337
+ }
1338
+ return buildGmxClassicPreparedMultisignBatch(args, prepared, {
1339
+ action: "decrease",
1340
+ symbol: args.symbol,
1341
+ direction: args.direction,
1342
+ orderType: args.orderType,
1343
+ sizeUsdHuman: args.sizeUsdHuman,
1344
+ collateralToken: args.collateralToken,
1345
+ isNativeOut: Boolean(args.isNativeOut)
1346
+ });
1347
+ }
1348
+ var buildEvmMultisignBodyGmxLimitIncreaseBatch = buildEvmMultisignBodyGmxIncreaseBatch;
1349
+ var buildEvmMultisignBodyGmxLimitDecreaseBatch = buildEvmMultisignBodyGmxDecreaseBatch;
1350
+ async function buildEvmMultisignBodyGmxCancelBatch(args) {
1351
+ const sdk = getGmxApiSdk(args.chainId);
1352
+ const executor = viem.getAddress(args.executorAddress);
1353
+ const orderId = args.orderId.trim();
1354
+ if (!orderId) throw new Error("orderId is required to cancel a GMX order.");
1355
+ const prepared = await sdk.prepareCancelOrder({
1356
+ orderId,
1357
+ mode: "classic",
1358
+ from: executor
1359
+ });
1360
+ if (prepared.payloadType !== "transaction") {
1361
+ throw new Error("GMX prepareCancelOrder returned typed-data; classic mode required for MPC multi-sign.");
1362
+ }
1363
+ return buildGmxClassicPreparedMultisignBatch(args, prepared, {
1364
+ action: "cancel",
1365
+ orderId
1366
+ });
1367
+ }
1368
+ var require8 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
1369
+ var {
1370
+ depositGasLimitKey,
1371
+ withdrawalGasLimitKey,
1372
+ singleSwapGasLimitKey,
1373
+ ESTIMATED_GAS_FEE_BASE_AMOUNT_V2_1,
1374
+ ESTIMATED_GAS_FEE_MULTIPLIER_FACTOR,
1375
+ ESTIMATED_GAS_FEE_PER_ORACLE_PRICE
1376
+ } = require8("@gmx-io/sdk/configs/dataStore");
1377
+ var { applyFactor } = require8("@gmx-io/sdk/utils/numbers");
1378
+ var {
1379
+ estimateExecuteDepositGasLimit,
1380
+ estimateExecuteWithdrawalGasLimit
1381
+ } = require8("@gmx-io/sdk/utils/fees");
1382
+ var dataStoreAbi = viem.parseAbi(["function getUint(bytes32 key) view returns (uint256)"]);
1383
+ var gasLimitsCache = /* @__PURE__ */ new Map();
1384
+ async function gmxLoadGasLimits(chainId, rpcUrl) {
1385
+ const cached = gasLimitsCache.get(chainId);
1386
+ if (cached) return cached;
1387
+ const dataStore = gmxContractAddress(chainId, "DataStore");
1388
+ const ch = viem.defineChain({
1389
+ id: chainId,
1390
+ name: "Destination",
1391
+ nativeCurrency: { decimals: 18, name: "Native", symbol: "N" },
1392
+ rpcUrls: { default: { http: [rpcUrl] } }
1393
+ });
1394
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
1395
+ const keys = [
1396
+ ["depositToken", depositGasLimitKey()],
1397
+ ["withdrawalMultiToken", withdrawalGasLimitKey()],
1398
+ ["singleSwap", singleSwapGasLimitKey()],
1399
+ ["estimatedGasFeeBaseAmount", ESTIMATED_GAS_FEE_BASE_AMOUNT_V2_1],
1400
+ ["estimatedGasFeePerOraclePrice", ESTIMATED_GAS_FEE_PER_ORACLE_PRICE],
1401
+ ["estimatedFeeMultiplierFactor", ESTIMATED_GAS_FEE_MULTIPLIER_FACTOR]
1402
+ ];
1403
+ const limits = {
1404
+ depositToken: 0n,
1405
+ withdrawalMultiToken: 0n,
1406
+ singleSwap: 0n,
1407
+ estimatedGasFeeBaseAmount: 0n,
1408
+ estimatedGasFeePerOraclePrice: 0n,
1409
+ estimatedFeeMultiplierFactor: 0n
1410
+ };
1411
+ await Promise.all(
1412
+ keys.map(async ([field, key]) => {
1413
+ const value = await publicClient.readContract({
1414
+ address: dataStore,
1415
+ abi: dataStoreAbi,
1416
+ functionName: "getUint",
1417
+ args: [key]
1418
+ });
1419
+ limits[field] = value;
1420
+ })
1421
+ );
1422
+ gasLimitsCache.set(chainId, limits);
1423
+ return limits;
1424
+ }
1425
+ function applyGmxExecutionFeeFormula(args) {
1426
+ const { gasLimits, estimatedGasLimit, gasPrice, oraclePriceCount, bufferBps } = args;
1427
+ let gasLimit = gasLimits.estimatedGasFeeBaseAmount;
1428
+ gasLimit += gasLimits.estimatedGasFeePerOraclePrice * oraclePriceCount;
1429
+ const multiplier = gasLimits.estimatedFeeMultiplierFactor;
1430
+ gasLimit += applyFactor(estimatedGasLimit, multiplier);
1431
+ const buffered = gasLimit + gasLimit * BigInt(bufferBps) / 10000n;
1432
+ return buffered * gasPrice;
1433
+ }
1434
+ var DEFAULT_EXECUTION_FEE_BUFFER_BPS = 3e3;
1435
+ async function gmxEstimateGmExecutionFee(args) {
1436
+ const bufferBps = args.executionFeeBufferBps ?? DEFAULT_EXECUTION_FEE_BUFFER_BPS;
1437
+ const gasLimits = await gmxLoadGasLimits(args.chainId, args.rpcUrl);
1438
+ const ch = viem.defineChain({
1439
+ id: args.chainId,
1440
+ name: "Destination",
1441
+ nativeCurrency: { decimals: 18, name: "Native", symbol: "N" },
1442
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1443
+ });
1444
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1445
+ const gasPrice = await publicClient.getGasPrice();
1446
+ const oraclePriceCount = 3n;
1447
+ const estimatedGasLimit = args.operation === "deposit" ? estimateExecuteDepositGasLimit(gasLimits, { swapsCount: 0, callbackGasLimit: 0n }) : estimateExecuteWithdrawalGasLimit(gasLimits, { swapsCount: 0n, callbackGasLimit: 0n });
1448
+ return applyGmxExecutionFeeFormula({
1449
+ gasLimits,
1450
+ estimatedGasLimit,
1451
+ gasPrice,
1452
+ oraclePriceCount,
1453
+ bufferBps
1454
+ });
1455
+ }
1456
+ function gmxZeroAddress() {
1457
+ return "0x0000000000000000000000000000000000000000";
1458
+ }
1459
+
1460
+ // src/protocols/evm/gmx/gmLiquidityMultisign.ts
1461
+ var require9 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
1462
+ var ExchangeRouterAbi = require9("@gmx-io/sdk/abis/ExchangeRouter").default;
1463
+ var GMX_GM_MULTICALL_FALLBACK = 2800000n;
1464
+ var GMX_WETH_DEPOSIT_FALLBACK2 = 120000n;
1465
+ var GMX_ERC20_APPROVE_FALLBACK2 = 100000n;
1466
+ var wethDepositAbi2 = viem.parseAbi(["function deposit() payable"]);
1467
+ var erc20AllowanceAbi2 = viem.parseAbi([
1468
+ "function allowance(address owner, address spender) view returns (uint256)"
1469
+ ]);
1470
+ var erc20ApproveAbi2 = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
1471
+ async function buildApproveStepsIfNeeded(args) {
1472
+ const ch = viem.defineChain({
1473
+ id: args.chainId,
1474
+ name: "Destination",
1475
+ nativeCurrency: { decimals: 18, name: "Native", symbol: "N" },
1476
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1477
+ });
1478
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1479
+ const allowance = await publicClient.readContract({
1480
+ address: args.token,
1481
+ abi: erc20AllowanceAbi2,
1482
+ functionName: "allowance",
1483
+ args: [args.executorAddress, args.spender]
1484
+ });
1485
+ if (allowance >= args.amountWei) return [];
1486
+ const steps = [];
1487
+ if (allowance > 0n) {
1488
+ steps.push({
1489
+ to: args.token,
1490
+ data: viem.encodeFunctionData({ abi: erc20ApproveAbi2, functionName: "approve", args: [args.spender, 0n] }),
1491
+ value: 0n,
1492
+ fallbackGas: GMX_ERC20_APPROVE_FALLBACK2
1493
+ });
1494
+ }
1495
+ steps.push({
1496
+ to: args.token,
1497
+ data: viem.encodeFunctionData({ abi: erc20ApproveAbi2, functionName: "approve", args: [args.spender, args.amountWei] }),
1498
+ value: 0n,
1499
+ fallbackGas: GMX_ERC20_APPROVE_FALLBACK2
1500
+ });
1501
+ return steps;
1502
+ }
1503
+ async function buildNativeWrapPrepSteps(args) {
1504
+ const weth = viem.getAddress(args.nativeWrapped);
1505
+ const router = viem.getAddress(args.exchangeRouter);
1506
+ const steps = [];
1507
+ steps.push({
1508
+ to: weth,
1509
+ data: viem.encodeFunctionData({ abi: wethDepositAbi2, functionName: "deposit", args: [] }),
1510
+ value: args.amountWei,
1511
+ fallbackGas: GMX_WETH_DEPOSIT_FALLBACK2
1512
+ });
1513
+ const approveSteps = await buildApproveStepsIfNeeded({
1514
+ chainId: args.chainId,
1515
+ rpcUrl: args.rpcUrl,
1516
+ executorAddress: args.executorAddress,
1517
+ token: weth,
1518
+ spender: router,
1519
+ amountWei: args.amountWei
1520
+ });
1521
+ steps.push(...approveSteps);
1522
+ return steps;
1523
+ }
1524
+ function resolveGmCollateralAddress(args) {
1525
+ if (args.isNativeIn) {
1526
+ if (!args.nativeWrapped) throw new Error("nativeWrapped is required when isNativeIn is true.");
1527
+ const wrapped = gmxWrappedNativeSymbolForChain(args.chainId);
1528
+ if (!wrapped) throw new Error("Wrapped native symbol is not configured for this chain.");
1529
+ if (args.collateralToken.trim().toUpperCase() !== wrapped.toUpperCase()) {
1530
+ throw new Error("Native GM deposit requires the chain wrapped native collateral symbol.");
1531
+ }
1532
+ return Promise.resolve(viem.getAddress(args.nativeWrapped));
1533
+ }
1534
+ return gmxResolveTokenBySymbol(args.chainId, args.collateralToken).then((t) => viem.getAddress(t.address));
1535
+ }
1536
+ async function buildGmLiquidityMultisignBatch(common, steps, signatureMeta, purposeSuffix) {
1537
+ const executor = viem.getAddress(common.executorAddress);
1538
+ const firstStep = steps[0];
1539
+ const dataNo0x = firstStep.data.startsWith("0x") ? firstStep.data.slice(2) : firstStep.data;
1540
+ return buildEvmMultisignBatch({
1541
+ context: {
1542
+ chainCategory: "evm",
1543
+ keyGen: common.keyGen,
1544
+ purposeText: common.purposeText,
1545
+ chainId: common.chainId,
1546
+ rpcUrl: common.rpcUrl,
1547
+ executorAddress: executor,
1548
+ chainDetail: common.chainDetail,
1549
+ useCustomGas: common.useCustomGas,
1550
+ customGasChainDetails: common.customGasChainDetails
1551
+ },
1552
+ steps,
1553
+ purposeSuffix,
1554
+ firstMsgRawNo0x: dataNo0x,
1555
+ destinationAddress: firstStep.to,
1556
+ resolveGasLimit: async ({ estimatedGas, index }) => {
1557
+ const step = steps[index];
1558
+ const fallback = step?.fallbackGas ?? GMX_GM_MULTICALL_FALLBACK;
1559
+ return estimatedGas > fallback ? estimatedGas : fallback;
1560
+ },
1561
+ buildBatchMeta: ({ index }) => {
1562
+ const step = steps[index];
1563
+ const isApprove = step?.fallbackGas === GMX_ERC20_APPROVE_FALLBACK2;
1564
+ const isWrap = step?.value > 0n && step?.fallbackGas === GMX_WETH_DEPOSIT_FALLBACK2;
1565
+ if (isWrap) {
1566
+ return {
1567
+ signatureText: JSON.stringify({ kind: "GMX", step: "weth_deposit", ...signatureMeta }),
1568
+ evm: { type: "gmx_weth_deposit", version: 1, chainId: String(common.chainId) }
1569
+ };
1570
+ }
1571
+ if (isApprove) {
1572
+ return {
1573
+ signatureText: JSON.stringify({ kind: "GMX", step: "approve", ...signatureMeta }),
1574
+ evm: { type: "gmx_erc20_approve", version: 1, chainId: String(common.chainId) }
1575
+ };
1576
+ }
1577
+ return {
1578
+ signatureText: JSON.stringify({ kind: "GMX", ...signatureMeta }),
1579
+ evm: { type: "gmx_gm_liquidity", version: 1, chainId: String(common.chainId) }
1580
+ };
1581
+ }
1582
+ });
1583
+ }
1584
+ async function buildEvmMultisignBodyGmxGmDepositBatch(args) {
1585
+ const executor = viem.getAddress(args.executorAddress);
1586
+ const market = await gmxResolveMarketBySymbol(args.chainId, args.marketSymbol);
1587
+ const collateralAddr = await resolveGmCollateralAddress(args);
1588
+ const collateralRow = await gmxResolveTokenBySymbol(args.chainId, args.collateralToken);
1589
+ const amountWei = parseGmxTokenAmount(args.collateralAmountHuman, collateralRow.decimals);
1590
+ if (amountWei === 0n) throw new Error("Collateral amount is zero after converting with token decimals.");
1591
+ const longToken = viem.getAddress(market.longTokenAddress);
1592
+ const shortToken = viem.getAddress(market.shortTokenAddress);
1593
+ if (collateralAddr !== longToken && collateralAddr !== shortToken) {
1594
+ throw new Error("Collateral token is not a pool token for the selected GM market.");
1595
+ }
1596
+ const exchangeRouter = gmxContractAddress(args.chainId, "ExchangeRouter");
1597
+ const depositVault = gmxContractAddress(args.chainId, "DepositVault");
1598
+ const executionFee = await gmxEstimateGmExecutionFee({
1599
+ chainId: args.chainId,
1600
+ rpcUrl: args.rpcUrl,
1601
+ operation: "deposit",
1602
+ executionFeeBufferBps: args.executionFeeBufferBps
1603
+ });
1604
+ const prepSteps = [];
1605
+ if (args.isNativeIn && args.nativeWrapped) {
1606
+ prepSteps.push(
1607
+ ...await buildNativeWrapPrepSteps({
1608
+ chainId: args.chainId,
1609
+ rpcUrl: args.rpcUrl,
1610
+ executorAddress: executor,
1611
+ nativeWrapped: args.nativeWrapped,
1612
+ exchangeRouter,
1613
+ amountWei
1614
+ })
1615
+ );
1616
+ } else {
1617
+ prepSteps.push(
1618
+ ...await buildApproveStepsIfNeeded({
1619
+ chainId: args.chainId,
1620
+ rpcUrl: args.rpcUrl,
1621
+ executorAddress: executor,
1622
+ token: collateralAddr,
1623
+ spender: exchangeRouter,
1624
+ amountWei
1625
+ })
1626
+ );
1627
+ }
1628
+ const multicallData = [
1629
+ viem.encodeFunctionData({
1630
+ abi: ExchangeRouterAbi,
1631
+ functionName: "sendWnt",
1632
+ args: [depositVault, executionFee]
1633
+ }),
1634
+ viem.encodeFunctionData({
1635
+ abi: ExchangeRouterAbi,
1636
+ functionName: "sendTokens",
1637
+ args: [collateralAddr, depositVault, amountWei]
1638
+ }),
1639
+ viem.encodeFunctionData({
1640
+ abi: ExchangeRouterAbi,
1641
+ functionName: "createDeposit",
1642
+ args: [
1643
+ {
1644
+ addresses: {
1645
+ receiver: executor,
1646
+ callbackContract: gmxZeroAddress(),
1647
+ uiFeeReceiver: gmxZeroAddress(),
1648
+ market: viem.getAddress(market.marketTokenAddress),
1649
+ initialLongToken: longToken,
1650
+ initialShortToken: shortToken,
1651
+ longTokenSwapPath: [],
1652
+ shortTokenSwapPath: []
1653
+ },
1654
+ minMarketTokens: 0n,
1655
+ shouldUnwrapNativeToken: Boolean(args.isNativeIn),
1656
+ executionFee,
1657
+ callbackGasLimit: 0n,
1658
+ dataList: []
1659
+ }
1660
+ ]
1661
+ })
1662
+ ];
1663
+ const orderStep = {
1664
+ to: exchangeRouter,
1665
+ data: viem.encodeFunctionData({
1666
+ abi: ExchangeRouterAbi,
1667
+ functionName: "multicall",
1668
+ args: [multicallData]
1669
+ }),
1670
+ value: executionFee,
1671
+ fallbackGas: GMX_GM_MULTICALL_FALLBACK
1672
+ };
1673
+ return buildGmLiquidityMultisignBatch(
1674
+ args,
1675
+ [...prepSteps, orderStep],
1676
+ {
1677
+ action: "gm-deposit",
1678
+ marketSymbol: args.marketSymbol,
1679
+ collateralToken: args.collateralToken,
1680
+ collateralAmountHuman: args.collateralAmountHuman,
1681
+ isNativeIn: Boolean(args.isNativeIn)
1682
+ },
1683
+ "GMX: GM pool deposit (keeper-executed asynchronously)."
1684
+ );
1685
+ }
1686
+ async function buildEvmMultisignBodyGmxGmWithdrawBatch(args) {
1687
+ const executor = viem.getAddress(args.executorAddress);
1688
+ const market = await gmxResolveMarketBySymbol(args.chainId, args.marketSymbol);
1689
+ const gmToken = viem.getAddress(market.marketTokenAddress);
1690
+ const gmDecimals = args.gmDecimals ?? 18;
1691
+ const gmAmountWei = parseGmxTokenAmount(args.gmAmountHuman, gmDecimals);
1692
+ if (gmAmountWei === 0n) throw new Error("GM amount is zero after converting with token decimals.");
1693
+ const exchangeRouter = gmxContractAddress(args.chainId, "ExchangeRouter");
1694
+ const withdrawalVault = gmxContractAddress(args.chainId, "WithdrawalVault");
1695
+ const executionFee = await gmxEstimateGmExecutionFee({
1696
+ chainId: args.chainId,
1697
+ rpcUrl: args.rpcUrl,
1698
+ operation: "withdrawal",
1699
+ executionFeeBufferBps: args.executionFeeBufferBps
1700
+ });
1701
+ const prepSteps = await buildApproveStepsIfNeeded({
1702
+ chainId: args.chainId,
1703
+ rpcUrl: args.rpcUrl,
1704
+ executorAddress: executor,
1705
+ token: gmToken,
1706
+ spender: exchangeRouter,
1707
+ amountWei: gmAmountWei
1708
+ });
1709
+ const multicallData = [
1710
+ viem.encodeFunctionData({
1711
+ abi: ExchangeRouterAbi,
1712
+ functionName: "sendWnt",
1713
+ args: [withdrawalVault, executionFee]
1714
+ }),
1715
+ viem.encodeFunctionData({
1716
+ abi: ExchangeRouterAbi,
1717
+ functionName: "sendTokens",
1718
+ args: [gmToken, withdrawalVault, gmAmountWei]
1719
+ }),
1720
+ viem.encodeFunctionData({
1721
+ abi: ExchangeRouterAbi,
1722
+ functionName: "createWithdrawal",
1723
+ args: [
1724
+ {
1725
+ addresses: {
1726
+ receiver: executor,
1727
+ callbackContract: gmxZeroAddress(),
1728
+ uiFeeReceiver: gmxZeroAddress(),
1729
+ market: gmToken,
1730
+ longTokenSwapPath: [],
1731
+ shortTokenSwapPath: []
1732
+ },
1733
+ minLongTokenAmount: 0n,
1734
+ minShortTokenAmount: 0n,
1735
+ shouldUnwrapNativeToken: Boolean(args.isNativeOut),
1736
+ executionFee,
1737
+ callbackGasLimit: 0n,
1738
+ dataList: []
1739
+ }
1740
+ ]
1741
+ })
1742
+ ];
1743
+ const orderStep = {
1744
+ to: exchangeRouter,
1745
+ data: viem.encodeFunctionData({
1746
+ abi: ExchangeRouterAbi,
1747
+ functionName: "multicall",
1748
+ args: [multicallData]
1749
+ }),
1750
+ value: executionFee,
1751
+ fallbackGas: GMX_GM_MULTICALL_FALLBACK
1752
+ };
1753
+ return buildGmLiquidityMultisignBatch(
1754
+ args,
1755
+ [...prepSteps, orderStep],
1756
+ {
1757
+ action: "gm-withdraw",
1758
+ marketSymbol: args.marketSymbol,
1759
+ gmAmountHuman: args.gmAmountHuman,
1760
+ isNativeOut: Boolean(args.isNativeOut)
1761
+ },
1762
+ "GMX: GM pool withdrawal (keeper-executed asynchronously)."
1763
+ );
1764
+ }
1765
+ var require10 = module$1.createRequire(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
1766
+ var RewardRouterAbi = require10("@gmx-io/sdk/abis/RewardRouter").default;
1767
+ var GMX_STAKE_FALLBACK = 600000n;
1768
+ var GMX_ERC20_APPROVE_FALLBACK3 = 100000n;
1769
+ var erc20AllowanceAbi3 = viem.parseAbi([
1770
+ "function allowance(address owner, address spender) view returns (uint256)"
1771
+ ]);
1772
+ var erc20ApproveAbi3 = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
1773
+ async function buildGmxStakeMultisignBatch(common, steps, signatureMeta, purposeSuffix) {
1774
+ const executor = viem.getAddress(common.executorAddress);
1775
+ const firstStep = steps[0];
1776
+ const dataNo0x = firstStep.data.startsWith("0x") ? firstStep.data.slice(2) : firstStep.data;
1777
+ return buildEvmMultisignBatch({
1778
+ context: {
1779
+ chainCategory: "evm",
1780
+ keyGen: common.keyGen,
1781
+ purposeText: common.purposeText,
1782
+ chainId: common.chainId,
1783
+ rpcUrl: common.rpcUrl,
1784
+ executorAddress: executor,
1785
+ chainDetail: common.chainDetail,
1786
+ useCustomGas: common.useCustomGas,
1787
+ customGasChainDetails: common.customGasChainDetails
1788
+ },
1789
+ steps,
1790
+ purposeSuffix,
1791
+ firstMsgRawNo0x: dataNo0x,
1792
+ destinationAddress: firstStep.to,
1793
+ resolveGasLimit: async ({ estimatedGas, index }) => {
1794
+ const step = steps[index];
1795
+ const fallback = step?.fallbackGas ?? GMX_STAKE_FALLBACK;
1796
+ return estimatedGas > fallback ? estimatedGas : fallback;
1797
+ },
1798
+ buildBatchMeta: ({ index }) => {
1799
+ const step = steps[index];
1800
+ const isApprove = step?.fallbackGas === GMX_ERC20_APPROVE_FALLBACK3;
1801
+ if (isApprove) {
1802
+ return {
1803
+ signatureText: JSON.stringify({ kind: "GMX", step: "approve", ...signatureMeta }),
1804
+ evm: { type: "gmx_erc20_approve", version: 1, chainId: String(common.chainId) }
1805
+ };
1806
+ }
1807
+ return {
1808
+ signatureText: JSON.stringify({ kind: "GMX", ...signatureMeta }),
1809
+ evm: { type: "gmx_token_stake", version: 1, chainId: String(common.chainId) }
1810
+ };
1811
+ }
1812
+ });
1813
+ }
1814
+ async function buildGmxApproveSteps(args) {
1815
+ const ch = viem.defineChain({
1816
+ id: args.chainId,
1817
+ name: "Destination",
1818
+ nativeCurrency: { decimals: 18, name: "Native", symbol: "N" },
1819
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1820
+ });
1821
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1822
+ const allowance = await publicClient.readContract({
1823
+ address: args.gmxToken,
1824
+ abi: erc20AllowanceAbi3,
1825
+ functionName: "allowance",
1826
+ args: [args.executorAddress, args.rewardRouter]
1827
+ });
1828
+ if (allowance >= args.amountWei) return [];
1829
+ const steps = [];
1830
+ if (allowance > 0n) {
1831
+ steps.push({
1832
+ to: args.gmxToken,
1833
+ data: viem.encodeFunctionData({ abi: erc20ApproveAbi3, functionName: "approve", args: [args.rewardRouter, 0n] }),
1834
+ value: 0n,
1835
+ fallbackGas: GMX_ERC20_APPROVE_FALLBACK3
1836
+ });
1837
+ }
1838
+ steps.push({
1839
+ to: args.gmxToken,
1840
+ data: viem.encodeFunctionData({ abi: erc20ApproveAbi3, functionName: "approve", args: [args.rewardRouter, args.amountWei] }),
1841
+ value: 0n,
1842
+ fallbackGas: GMX_ERC20_APPROVE_FALLBACK3
1843
+ });
1844
+ return steps;
1845
+ }
1846
+ async function buildEvmMultisignBodyGmxStakeGmxBatch(args) {
1847
+ const executor = viem.getAddress(args.executorAddress);
1848
+ const gmxToken = gmxContractAddress(args.chainId, "GMX");
1849
+ const rewardRouter = gmxContractAddress(args.chainId, "RewardRouter");
1850
+ const amountWei = parseGmxTokenAmount(args.amountHuman, args.gmxDecimals ?? 18);
1851
+ if (amountWei === 0n) throw new Error("GMX stake amount is zero after converting with token decimals.");
1852
+ const prepSteps = await buildGmxApproveSteps({
1853
+ chainId: args.chainId,
1854
+ rpcUrl: args.rpcUrl,
1855
+ executorAddress: executor,
1856
+ gmxToken,
1857
+ rewardRouter,
1858
+ amountWei
1859
+ });
1860
+ const stakeStep = {
1861
+ to: rewardRouter,
1862
+ data: viem.encodeFunctionData({
1863
+ abi: RewardRouterAbi,
1864
+ functionName: "stakeGmx",
1865
+ args: [amountWei]
1866
+ }),
1867
+ value: 0n,
1868
+ fallbackGas: GMX_STAKE_FALLBACK
1869
+ };
1870
+ return buildGmxStakeMultisignBatch(
1871
+ args,
1872
+ [...prepSteps, stakeStep],
1873
+ { action: "stake-gmx", amountHuman: args.amountHuman },
1874
+ "GMX: stake GMX tokens via RewardRouter."
1875
+ );
1876
+ }
1877
+ async function buildEvmMultisignBodyGmxUnstakeGmxBatch(args) {
1878
+ const rewardRouter = gmxContractAddress(args.chainId, "RewardRouter");
1879
+ const amountWei = parseGmxTokenAmount(args.amountHuman, args.gmxDecimals ?? 18);
1880
+ if (amountWei === 0n) throw new Error("GMX unstake amount is zero after converting with token decimals.");
1881
+ const unstakeStep = {
1882
+ to: rewardRouter,
1883
+ data: viem.encodeFunctionData({
1884
+ abi: RewardRouterAbi,
1885
+ functionName: "unstakeGmx",
1886
+ args: [amountWei]
1887
+ }),
1888
+ value: 0n,
1889
+ fallbackGas: GMX_STAKE_FALLBACK
1890
+ };
1891
+ return buildGmxStakeMultisignBatch(
1892
+ args,
1893
+ [unstakeStep],
1894
+ { action: "unstake-gmx", amountHuman: args.amountHuman },
1895
+ "GMX: unstake GMX tokens via RewardRouter."
1896
+ );
1897
+ }
1898
+ var erc20BalanceAbi = viem.parseAbi([
1899
+ "function balanceOf(address account) view returns (uint256)",
1900
+ "function decimals() view returns (uint8)"
1901
+ ]);
1902
+ var GM_DECIMALS_DEFAULT = 18;
1903
+ function gmTokenListSymbol(marketSymbol) {
1904
+ const bracket = marketSymbol.match(/^([^[\]]+)/)?.[1]?.trim();
1905
+ const base = (bracket ?? marketSymbol).replace(/\s+/g, "");
1906
+ const compact = base.length > 20 ? `${base.slice(0, 17)}\u2026` : base;
1907
+ return `GM:${compact}`;
1908
+ }
1909
+ function gmTokenListName(marketSymbol) {
1910
+ return `GMX GM ${marketSymbol.trim()}`;
1911
+ }
1912
+ async function gmxFetchGmLiquidityPositions(args) {
1913
+ const executor = viem.getAddress(args.executorAddress);
1914
+ const rpcUrl = args.rpcUrl.trim();
1915
+ if (!rpcUrl) throw new Error("rpcUrl is required to read GM liquidity balances.");
1916
+ const sdk = getGmxApiSdk(args.chainId);
1917
+ const [markets, apyRes] = await Promise.all([
1918
+ gmxFetchGmMarketsSummary(args.chainId),
1919
+ sdk.fetchApy().catch(() => ({ markets: {} }))
1920
+ ]);
1921
+ const apyByMarket = apyRes.markets ?? {};
1922
+ const ch = viem.defineChain({
1923
+ id: args.chainId,
1924
+ name: "Destination",
1925
+ nativeCurrency: { decimals: 18, name: "Native", symbol: "N" },
1926
+ rpcUrls: { default: { http: [rpcUrl] } }
1927
+ });
1928
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
1929
+ const rows = [];
1930
+ await Promise.all(
1931
+ markets.map(async (market) => {
1932
+ const token = viem.getAddress(market.marketTokenAddress);
1933
+ let balance = 0n;
1934
+ let decimals = GM_DECIMALS_DEFAULT;
1935
+ try {
1936
+ ;
1937
+ [balance, decimals] = await Promise.all([
1938
+ publicClient.readContract({
1939
+ address: token,
1940
+ abi: erc20BalanceAbi,
1941
+ functionName: "balanceOf",
1942
+ args: [executor]
1943
+ }),
1944
+ publicClient.readContract({
1945
+ address: token,
1946
+ abi: erc20BalanceAbi,
1947
+ functionName: "decimals"
1948
+ }).catch(() => GM_DECIMALS_DEFAULT)
1949
+ ]);
1950
+ } catch {
1951
+ return;
1952
+ }
1953
+ if (!args.includeZeroBalance && balance === 0n) return;
1954
+ const apyEntry = gmxLookupMarketApy(apyByMarket, token);
1955
+ rows.push({
1956
+ symbol: market.symbol,
1957
+ marketTokenAddress: market.marketTokenAddress,
1958
+ longTokenSymbol: market.longTokenSymbol,
1959
+ shortTokenSymbol: market.shortTokenSymbol,
1960
+ gmDecimals: Number(decimals),
1961
+ gmBalanceWei: balance.toString(),
1962
+ gmBalanceHuman: viem.formatUnits(balance, Number(decimals)),
1963
+ apyPercentLabel: gmxFormatApyPercent(apyEntry?.apy),
1964
+ tokenListSymbol: gmTokenListSymbol(market.symbol),
1965
+ tokenListName: gmTokenListName(market.symbol)
1966
+ });
1967
+ })
1968
+ );
1969
+ rows.sort((a, b) => a.symbol.localeCompare(b.symbol, void 0, { sensitivity: "base" }));
1970
+ return { rows };
1971
+ }
1972
+
1973
+ // src/protocols/evm/gmx/prices.ts
1974
+ var GMX_OHLCV_TIMEFRAMES = ["1m", "5m", "15m", "1h", "4h", "1d", "1w", "1M"];
1975
+ var DEFAULT_OHLCV_LIMIT = 16;
1976
+ function indexLabelFromMarketSymbol(symbol) {
1977
+ const m = symbol.trim().match(/^([^/]+)\/USD/i);
1978
+ return m?.[1]?.trim() || symbol.trim();
1979
+ }
1980
+ function asBigint3(v) {
1981
+ if (v == null) return void 0;
1982
+ try {
1983
+ return typeof v === "bigint" ? v : BigInt(String(v));
1984
+ } catch {
1985
+ return void 0;
1986
+ }
1987
+ }
1988
+ function formatIndexPerCollateral(indexMark, collateralUsd) {
1989
+ if (indexMark <= 0n || collateralUsd <= 0n) return null;
1990
+ const index = Number(indexMark) / 1e30;
1991
+ const coll = Number(collateralUsd) / 1e30;
1992
+ if (!Number.isFinite(index) || !Number.isFinite(coll) || coll <= 0) return null;
1993
+ const ratio = index / coll;
1994
+ return ratio.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 6 });
1995
+ }
1996
+ function formatCandleTime(timestampMs) {
1997
+ try {
1998
+ return new Date(timestampMs).toLocaleString(void 0, {
1999
+ month: "short",
2000
+ day: "numeric",
2001
+ hour: "2-digit",
2002
+ minute: "2-digit"
2003
+ });
2004
+ } catch {
2005
+ return String(timestampMs);
2006
+ }
2007
+ }
2008
+ function mapOhlcvRows(raw, sort) {
2009
+ const sorted = [...raw].sort((a, b) => sort === "asc" ? a.timestamp - b.timestamp : b.timestamp - a.timestamp);
2010
+ return sorted.map((c) => ({
2011
+ timestampMs: c.timestamp,
2012
+ timeLabel: formatCandleTime(c.timestamp),
2013
+ open: c.open,
2014
+ high: c.high,
2015
+ low: c.low,
2016
+ close: c.close
2017
+ }));
2018
+ }
2019
+ function assertGmxOhlcvTimeframe(timeframe) {
2020
+ const tf = timeframe.trim();
2021
+ if (!GMX_OHLCV_TIMEFRAMES.includes(tf)) {
2022
+ throw new Error(`Invalid timeframe: ${timeframe}. Valid: ${GMX_OHLCV_TIMEFRAMES.join(", ")}`);
2023
+ }
2024
+ return tf;
2025
+ }
2026
+ async function gmxFetchMarketPrices(args) {
2027
+ const symbol = args.symbol.trim();
2028
+ const collateralSymbol = args.collateralSymbol.trim();
2029
+ if (!symbol) throw new Error("GMX market symbol is required.");
2030
+ if (!collateralSymbol) throw new Error("Collateral symbol is required.");
2031
+ const sdk = getGmxApiSdk(args.chainId);
2032
+ const [tickers, collateralToken] = await Promise.all([
2033
+ sdk.fetchMarketsTickers({ symbols: [symbol] }),
2034
+ resolveGmxTokenBySymbol(args.chainId, collateralSymbol)
2035
+ ]);
2036
+ const ticker = tickers.find((t) => String(t.symbol ?? "") === symbol) ?? tickers[0];
2037
+ const mark = asBigint3(ticker?.markPrice ?? ticker?.minPrice);
2038
+ const indexMarkUsd = formatGmxUsd30(mark);
2039
+ const collateralPrice = asBigint3(
2040
+ collateralToken?.prices?.minPrice
2041
+ );
2042
+ const collateralUsd = formatGmxUsd30(collateralPrice);
2043
+ const indexPerCollateral = mark != null && collateralPrice != null ? formatIndexPerCollateral(mark, collateralPrice) : null;
2044
+ return {
2045
+ symbol,
2046
+ indexLabel: indexLabelFromMarketSymbol(symbol),
2047
+ collateralSymbol,
2048
+ indexMarkUsd,
2049
+ collateralUsd,
2050
+ indexPerCollateral,
2051
+ fetchedAtMs: Date.now()
2052
+ };
2053
+ }
2054
+ async function gmxFetchOhlcv(args) {
2055
+ const symbol = args.symbol.trim();
2056
+ if (!symbol) throw new Error("GMX market symbol is required.");
2057
+ const timeframe = assertGmxOhlcvTimeframe(args.timeframe ?? "15m");
2058
+ const limit = args.limit ?? DEFAULT_OHLCV_LIMIT;
2059
+ const sort = args.sort ?? "desc";
2060
+ const sdk = getGmxApiSdk(args.chainId);
2061
+ const ohlcvRaw = await sdk.fetchOhlcv({ symbol, timeframe, limit });
2062
+ return {
2063
+ symbol,
2064
+ timeframe,
2065
+ candles: mapOhlcvRows(ohlcvRaw, sort)
2066
+ };
2067
+ }
2068
+ async function gmxFetchMarketSnapshot(args) {
2069
+ const ohlcvTimeframe = assertGmxOhlcvTimeframe(args.ohlcvTimeframe ?? "15m");
2070
+ const [prices, ohlcv] = await Promise.all([
2071
+ gmxFetchMarketPrices(args),
2072
+ gmxFetchOhlcv({ chainId: args.chainId, symbol: args.symbol, timeframe: ohlcvTimeframe, sort: "desc" })
2073
+ ]);
2074
+ return {
2075
+ ...prices,
2076
+ ohlcvTimeframe: ohlcv.timeframe,
2077
+ ohlcv: ohlcv.candles
2078
+ };
2079
+ }
2080
+
2081
+ // src/protocols/evm/gmx/index.ts
2082
+ var GMX_PROTOCOL_ID = "gmx";
2083
+ var gmxProtocolModule = {
2084
+ id: GMX_PROTOCOL_ID,
2085
+ chainCategory: "evm",
2086
+ isChainSupported(ctx) {
2087
+ if (ctx.chainCategory !== "evm") return false;
2088
+ const chainId = typeof ctx.chainId === "number" ? ctx.chainId : Number(ctx.chainId);
2089
+ return Number.isFinite(chainId) && isGmxChainSupported(chainId);
2090
+ },
2091
+ isTokenSupported(token) {
2092
+ if (token.category !== "evm") return false;
2093
+ return token.kind === "erc20";
2094
+ },
2095
+ actions: [
2096
+ {
2097
+ id: "gmx.increase",
2098
+ protocolId: GMX_PROTOCOL_ID,
2099
+ chainCategory: "evm",
2100
+ description: "Open or add to a GMX perp position (classic on-chain order)",
2101
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2102
+ params: {
2103
+ symbol: { type: "string", required: true, description: "GMX market symbol e.g. ETH/USD [WETH-USDC]" },
2104
+ direction: { type: "string", required: true, description: "long or short" },
2105
+ orderType: { type: "string", required: true, description: "market or limit" },
2106
+ sizeUsdHuman: { type: "string", required: true, description: "Position size in USD" },
2107
+ collateralToken: { type: "string", required: true, description: "Collateral token symbol e.g. USDC" },
2108
+ collateralAmountHuman: { type: "string", required: true, description: "Collateral amount in token units" }
2109
+ }
2110
+ },
2111
+ {
2112
+ id: "gmx.decrease",
2113
+ protocolId: GMX_PROTOCOL_ID,
2114
+ chainCategory: "evm",
2115
+ description: "Close or reduce a GMX perp position (classic on-chain order)",
2116
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2117
+ params: {
2118
+ symbol: { type: "string", required: true, description: "GMX market symbol" },
2119
+ direction: { type: "string", required: true, description: "long or short" },
2120
+ orderType: { type: "string", required: true, description: "market or limit" },
2121
+ sizeUsdHuman: { type: "string", required: true, description: "Size to close in USD" },
2122
+ collateralToken: { type: "string", required: true, description: "Position collateral token symbol" }
2123
+ }
2124
+ },
2125
+ {
2126
+ id: "gmx.cancel",
2127
+ protocolId: GMX_PROTOCOL_ID,
2128
+ chainCategory: "evm",
2129
+ description: "Cancel a pending GMX order (classic on-chain)",
2130
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2131
+ params: {
2132
+ orderId: { type: "string", required: true, description: "Order key from fetchOrders" }
2133
+ }
2134
+ },
2135
+ {
2136
+ id: "gmx.gmDeposit",
2137
+ protocolId: GMX_PROTOCOL_ID,
2138
+ chainCategory: "evm",
2139
+ description: "Deposit collateral into a GMX GM liquidity pool (ExchangeRouter multicall)",
2140
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2141
+ params: {
2142
+ marketSymbol: { type: "string", required: true, description: "GM market symbol e.g. ETH/USD [WETH-USDC]" },
2143
+ collateralToken: { type: "string", required: true, description: "Pool collateral token symbol" },
2144
+ collateralAmountHuman: { type: "string", required: true, description: "Amount in token units" }
2145
+ }
2146
+ },
2147
+ {
2148
+ id: "gmx.gmWithdraw",
2149
+ protocolId: GMX_PROTOCOL_ID,
2150
+ chainCategory: "evm",
2151
+ description: "Withdraw GM market tokens from a GMX liquidity pool",
2152
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2153
+ params: {
2154
+ marketSymbol: { type: "string", required: true, description: "GM market symbol" },
2155
+ gmAmountHuman: { type: "string", required: true, description: "GM token amount to withdraw" }
2156
+ }
2157
+ },
2158
+ {
2159
+ id: "gmx.stakeGmx",
2160
+ protocolId: GMX_PROTOCOL_ID,
2161
+ chainCategory: "evm",
2162
+ description: "Stake GMX tokens via RewardRouter",
2163
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2164
+ params: {
2165
+ amountHuman: { type: "string", required: true, description: "GMX amount to stake" }
2166
+ }
2167
+ },
2168
+ {
2169
+ id: "gmx.unstakeGmx",
2170
+ protocolId: GMX_PROTOCOL_ID,
2171
+ chainCategory: "evm",
2172
+ description: "Unstake GMX tokens via RewardRouter",
2173
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
2174
+ params: {
2175
+ amountHuman: { type: "string", required: true, description: "GMX amount to unstake" }
2176
+ }
2177
+ }
2178
+ ]
2179
+ };
2180
+ registerProtocolModule(gmxProtocolModule);
2181
+
2182
+ exports.GMX_NATIVE_GAS_SYMBOL_BY_CHAIN = GMX_NATIVE_GAS_SYMBOL_BY_CHAIN;
2183
+ exports.GMX_OHLCV_TIMEFRAMES = GMX_OHLCV_TIMEFRAMES;
2184
+ exports.GMX_PROTOCOL_ID = GMX_PROTOCOL_ID;
2185
+ exports.GMX_SUPPORTED_CHAIN_IDS = GMX_SUPPORTED_CHAIN_IDS;
2186
+ exports.GMX_WRAPPED_NATIVE_SYMBOL_BY_CHAIN = GMX_WRAPPED_NATIVE_SYMBOL_BY_CHAIN;
2187
+ exports.assertGmxOhlcvTimeframe = assertGmxOhlcvTimeframe;
2188
+ exports.buildEvmMultisignBodyGmxCancelBatch = buildEvmMultisignBodyGmxCancelBatch;
2189
+ exports.buildEvmMultisignBodyGmxDecreaseBatch = buildEvmMultisignBodyGmxDecreaseBatch;
2190
+ exports.buildEvmMultisignBodyGmxGmDepositBatch = buildEvmMultisignBodyGmxGmDepositBatch;
2191
+ exports.buildEvmMultisignBodyGmxGmWithdrawBatch = buildEvmMultisignBodyGmxGmWithdrawBatch;
2192
+ exports.buildEvmMultisignBodyGmxIncreaseBatch = buildEvmMultisignBodyGmxIncreaseBatch;
2193
+ exports.buildEvmMultisignBodyGmxLimitDecreaseBatch = buildEvmMultisignBodyGmxLimitDecreaseBatch;
2194
+ exports.buildEvmMultisignBodyGmxLimitIncreaseBatch = buildEvmMultisignBodyGmxLimitIncreaseBatch;
2195
+ exports.buildEvmMultisignBodyGmxStakeGmxBatch = buildEvmMultisignBodyGmxStakeGmxBatch;
2196
+ exports.buildEvmMultisignBodyGmxUnstakeGmxBatch = buildEvmMultisignBodyGmxUnstakeGmxBatch;
2197
+ exports.formatGmxLeverageBps = formatGmxLeverageBps;
2198
+ exports.formatGmxTokenAmountHuman = formatGmxTokenAmountHuman;
2199
+ exports.formatGmxUsd30 = formatGmxUsd30;
2200
+ exports.formatGmxUsd30HumanInput = formatGmxUsd30HumanInput;
2201
+ exports.getGmxApiSdk = getGmxApiSdk;
2202
+ exports.gmxAssertIncreaseOrderWithinMaxLeverage = gmxAssertIncreaseOrderWithinMaxLeverage;
2203
+ exports.gmxContractAddress = gmxContractAddress;
2204
+ exports.gmxEnrichGmMarketRowWithApy = gmxEnrichGmMarketRowWithApy;
2205
+ exports.gmxEstimateDecreasePreview = gmxEstimateDecreasePreview;
2206
+ exports.gmxEstimateGmExecutionFee = gmxEstimateGmExecutionFee;
2207
+ exports.gmxEstimateIncreasePreview = gmxEstimateIncreasePreview;
2208
+ exports.gmxFetchClosePositionCap = gmxFetchClosePositionCap;
2209
+ exports.gmxFetchCollateralTokens = gmxFetchCollateralTokens;
2210
+ exports.gmxFetchGmApy = gmxFetchGmApy;
2211
+ exports.gmxFetchGmLiquidityPositions = gmxFetchGmLiquidityPositions;
2212
+ exports.gmxFetchGmMarketForToken = gmxFetchGmMarketForToken;
2213
+ exports.gmxFetchGmMarkets = gmxFetchGmMarkets;
2214
+ exports.gmxFetchGmMarketsSummary = gmxFetchGmMarketsSummary;
2215
+ exports.gmxFetchMarketPrices = gmxFetchMarketPrices;
2216
+ exports.gmxFetchMarketSnapshot = gmxFetchMarketSnapshot;
2217
+ exports.gmxFetchMarkets = gmxFetchMarkets;
2218
+ exports.gmxFetchMarketsSummary = gmxFetchMarketsSummary;
2219
+ exports.gmxFetchOhlcv = gmxFetchOhlcv;
2220
+ exports.gmxFetchOrdersForExecutor = gmxFetchOrdersForExecutor;
2221
+ exports.gmxFetchPositionDisplayRows = gmxFetchPositionDisplayRows;
2222
+ exports.gmxFetchPositionsForExecutor = gmxFetchPositionsForExecutor;
2223
+ exports.gmxFetchStakingPower = gmxFetchStakingPower;
2224
+ exports.gmxFetchTokens = gmxFetchTokens;
2225
+ exports.gmxFindWrappedNativeToken = gmxFindWrappedNativeToken;
2226
+ exports.gmxFormatApyPercent = gmxFormatApyPercent;
2227
+ exports.gmxFormatPositionsForDisplay = gmxFormatPositionsForDisplay;
2228
+ exports.gmxGmxTokenAddress = gmxGmxTokenAddress;
2229
+ exports.gmxIsCloseSizeWithinPosition = gmxIsCloseSizeWithinPosition;
2230
+ exports.gmxIsCollateralTokenSupported = gmxIsCollateralTokenSupported;
2231
+ exports.gmxIsGmMarketTokenAddress = gmxIsGmMarketTokenAddress;
2232
+ exports.gmxIsGmxStakeTokenAddress = gmxIsGmxStakeTokenAddress;
2233
+ exports.gmxKeyForNodeAssetRow = gmxKeyForNodeAssetRow;
2234
+ exports.gmxLookupMarketApy = gmxLookupMarketApy;
2235
+ exports.gmxMarketAcceptsCollateral = gmxMarketAcceptsCollateral;
2236
+ exports.gmxMarketCollateralTokens = gmxMarketCollateralTokens;
2237
+ exports.gmxMarketMaxLeverageBps = gmxMarketMaxLeverageBps;
2238
+ exports.gmxMarketMaxLeverageLabel = gmxMarketMaxLeverageLabel;
2239
+ exports.gmxMarketsForCollateralSymbol = gmxMarketsForCollateralSymbol;
2240
+ exports.gmxNativeGasSymbolForChain = gmxNativeGasSymbolForChain;
2241
+ exports.gmxProtocolModule = gmxProtocolModule;
2242
+ exports.gmxResolveMarketByGmTokenAddress = gmxResolveMarketByGmTokenAddress;
2243
+ exports.gmxResolveMarketBySymbol = gmxResolveMarketBySymbol;
2244
+ exports.gmxResolveTokenBySymbol = gmxResolveTokenBySymbol;
2245
+ exports.gmxWrappedNativeSymbolForChain = gmxWrappedNativeSymbolForChain;
2246
+ exports.gmxZeroAddress = gmxZeroAddress;
2247
+ exports.isGmxChainSupported = isGmxChainSupported;
2248
+ exports.parseGmxTokenAmount = parseGmxTokenAmount;
2249
+ exports.parseGmxUsd30 = parseGmxUsd30;
2250
+ //# sourceMappingURL=index.cjs.map
2251
+ //# sourceMappingURL=index.cjs.map