@ostium/builder-sdk 0.1.0 → 0.3.0
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.
- package/README.md +73 -3
- package/dist/cli.js +568 -150
- package/dist/index.cjs +559 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +193 -108
- package/dist/index.d.ts +193 -108
- package/dist/index.js +559 -142
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -243,11 +243,20 @@ function getNetworkConfig(isTestnet = false) {
|
|
|
243
243
|
// src/config.ts
|
|
244
244
|
var DEFAULT_PIMLICO_URL = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161";
|
|
245
245
|
var DEFAULT_PIMLICO_URL_TESTNET = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=421614";
|
|
246
|
-
function
|
|
247
|
-
return
|
|
246
|
+
function hasPrivateKey(config) {
|
|
247
|
+
return typeof config.privateKey === "string" && config.privateKey.length > 0;
|
|
248
248
|
}
|
|
249
|
-
function
|
|
250
|
-
return
|
|
249
|
+
function resolveMode(config) {
|
|
250
|
+
if (config.mode) return config.mode;
|
|
251
|
+
if (config.privateKey) {
|
|
252
|
+
if (config.pimlicoUrl) return config.traderAddress ? "delegated-gasless" : "self-gasless";
|
|
253
|
+
return config.traderAddress ? "delegated-self" : "self-self";
|
|
254
|
+
}
|
|
255
|
+
if (config.safeAddress && config.delegateAddress && config.traderAddress) return "delegated-gasless";
|
|
256
|
+
if (config.safeAddress && config.traderAddress) return "self-gasless";
|
|
257
|
+
if (config.delegateAddress && config.traderAddress) return "delegated-self";
|
|
258
|
+
if (config.traderAddress) return "self-self";
|
|
259
|
+
throw new Error("Unable to infer client mode from config");
|
|
251
260
|
}
|
|
252
261
|
|
|
253
262
|
// src/errors.ts
|
|
@@ -2648,8 +2657,8 @@ var DelegatedSignerStrategy = class {
|
|
|
2648
2657
|
};
|
|
2649
2658
|
|
|
2650
2659
|
// src/signer/index.ts
|
|
2651
|
-
function createSigner(
|
|
2652
|
-
if (
|
|
2660
|
+
function createSigner(mode, traderAddress) {
|
|
2661
|
+
if (mode !== "self-self") {
|
|
2653
2662
|
return new DelegatedSignerStrategy(traderAddress);
|
|
2654
2663
|
}
|
|
2655
2664
|
return new SelfSignerStrategy(traderAddress);
|
|
@@ -2673,6 +2682,12 @@ var SelfSubmissionStrategy = class {
|
|
|
2673
2682
|
effectiveSender;
|
|
2674
2683
|
send;
|
|
2675
2684
|
constructor(config, testnet) {
|
|
2685
|
+
if (!config.privateKey) {
|
|
2686
|
+
throw new OstiumError(
|
|
2687
|
+
"privateKey is required for self-submission mode",
|
|
2688
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2689
|
+
);
|
|
2690
|
+
}
|
|
2676
2691
|
if (!config.rpcUrl) {
|
|
2677
2692
|
throw new OstiumError(
|
|
2678
2693
|
"rpcUrl is required for self-submission mode",
|
|
@@ -2738,6 +2753,12 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2738
2753
|
this.effectiveSender = safeAccount.address;
|
|
2739
2754
|
}
|
|
2740
2755
|
static async create(config, testnet) {
|
|
2756
|
+
if (!config.privateKey) {
|
|
2757
|
+
throw new OstiumError(
|
|
2758
|
+
"privateKey is required for gasless submission mode",
|
|
2759
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2760
|
+
);
|
|
2761
|
+
}
|
|
2741
2762
|
if (!config.pimlicoUrl) {
|
|
2742
2763
|
throw new OstiumError(
|
|
2743
2764
|
"pimlicoUrl is required for gasless mode",
|
|
@@ -2806,8 +2827,8 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2806
2827
|
};
|
|
2807
2828
|
|
|
2808
2829
|
// src/submitter/index.ts
|
|
2809
|
-
async function createSubmitter(config, testnet) {
|
|
2810
|
-
if (
|
|
2830
|
+
async function createSubmitter(config, testnet, mode) {
|
|
2831
|
+
if (mode === "self-gasless" || mode === "delegated-gasless") {
|
|
2811
2832
|
return GaslessSubmissionStrategy.create(config, testnet);
|
|
2812
2833
|
}
|
|
2813
2834
|
if (!config.rpcUrl) {
|
|
@@ -2896,6 +2917,17 @@ query GetTraderOpenTrades($trader: String!, $skip: Int!, $first: Int!) {
|
|
|
2896
2917
|
${PAIR_FULL}
|
|
2897
2918
|
}
|
|
2898
2919
|
}`;
|
|
2920
|
+
var GetAllOpenTradesQuery = `
|
|
2921
|
+
query GetAllOpenTrades($skip: Int!, $first: Int!) {
|
|
2922
|
+
trades(
|
|
2923
|
+
where: { isOpen: true }
|
|
2924
|
+
skip: $skip first: $first
|
|
2925
|
+
orderBy: timestamp orderDirection: desc
|
|
2926
|
+
) {
|
|
2927
|
+
${TRADE_CORE}
|
|
2928
|
+
${PAIR_FULL}
|
|
2929
|
+
}
|
|
2930
|
+
}`;
|
|
2899
2931
|
var ORDER_FIELDS = `
|
|
2900
2932
|
id tradeID limitID trader
|
|
2901
2933
|
pair { id from to group { id name } }
|
|
@@ -2975,9 +3007,9 @@ query GetTraderActiveLimits($trader: String!, $skip: Int!, $first: Int!) {
|
|
|
2975
3007
|
}`;
|
|
2976
3008
|
|
|
2977
3009
|
// src/data/internal/pagination.ts
|
|
2978
|
-
async function paginateAll(fetcher, batchSize = 1e3, max = Infinity) {
|
|
3010
|
+
async function paginateAll(fetcher, batchSize = 1e3, max = Infinity, initialSkip = 0) {
|
|
2979
3011
|
const results = [];
|
|
2980
|
-
let skip =
|
|
3012
|
+
let skip = initialSkip;
|
|
2981
3013
|
while (results.length < max) {
|
|
2982
3014
|
const remaining = max - results.length;
|
|
2983
3015
|
const fetchSize = Math.min(batchSize, remaining);
|
|
@@ -3199,10 +3231,11 @@ async function fetchLivePrices(builderApiUrl) {
|
|
|
3199
3231
|
}
|
|
3200
3232
|
return result;
|
|
3201
3233
|
}
|
|
3202
|
-
function rawTickToPublic(item) {
|
|
3234
|
+
function rawTickToPublic(item, pairId) {
|
|
3203
3235
|
const from = normalizePairName(item.from);
|
|
3204
3236
|
const to = normalizePairName(item.to);
|
|
3205
3237
|
return {
|
|
3238
|
+
pairId,
|
|
3206
3239
|
feedId: item.feed_id,
|
|
3207
3240
|
pair: `${from}-${to}`,
|
|
3208
3241
|
from,
|
|
@@ -3249,9 +3282,12 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3249
3282
|
snapshotHandlers = /* @__PURE__ */ new Set();
|
|
3250
3283
|
/** pairId (string) → raw "FROM-TO" name for the WS API. */
|
|
3251
3284
|
pairRawNameCache;
|
|
3252
|
-
|
|
3285
|
+
/** raw "FROM-TO" name → pairId (string) for mapping incoming ticks back to SDK ids. */
|
|
3286
|
+
rawNamePairIdCache;
|
|
3287
|
+
constructor(ws, pairRawNameCache, rawNamePairIdCache, initialSubscribeRawPairs) {
|
|
3253
3288
|
this.ws = ws;
|
|
3254
3289
|
this.pairRawNameCache = pairRawNameCache;
|
|
3290
|
+
this.rawNamePairIdCache = rawNamePairIdCache;
|
|
3255
3291
|
if (initialSubscribeRawPairs?.length) {
|
|
3256
3292
|
ws.once("open", () => {
|
|
3257
3293
|
ws.send(JSON.stringify({ type: "subscribe", pairs: initialSubscribeRawPairs }));
|
|
@@ -3265,10 +3301,13 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3265
3301
|
return;
|
|
3266
3302
|
}
|
|
3267
3303
|
if (msg.type === "snapshot" && Array.isArray(msg.data)) {
|
|
3268
|
-
const ticks = msg.data.map(
|
|
3304
|
+
const ticks = msg.data.map(
|
|
3305
|
+
(item) => rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`))
|
|
3306
|
+
);
|
|
3269
3307
|
for (const h of this.snapshotHandlers) h(ticks);
|
|
3270
3308
|
} else if (msg.type === "tick" && msg.data) {
|
|
3271
|
-
const
|
|
3309
|
+
const item = msg.data;
|
|
3310
|
+
const tick = rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`));
|
|
3272
3311
|
for (const h of this.tickHandlers) h(tick);
|
|
3273
3312
|
}
|
|
3274
3313
|
});
|
|
@@ -3294,7 +3333,11 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3294
3333
|
const ws = new WS(url, {
|
|
3295
3334
|
headers: { "User-Agent": "ostium-sdk", "Accept": "*/*" }
|
|
3296
3335
|
});
|
|
3297
|
-
|
|
3336
|
+
const rawNamePairIdCache = /* @__PURE__ */ new Map();
|
|
3337
|
+
for (const [pairId, rawName] of pairRawNameCache.entries()) {
|
|
3338
|
+
rawNamePairIdCache.set(rawName, pairId);
|
|
3339
|
+
}
|
|
3340
|
+
return new _OstiumPriceStream(ws, pairRawNameCache, rawNamePairIdCache, initial);
|
|
3298
3341
|
}
|
|
3299
3342
|
/**
|
|
3300
3343
|
* Register a callback that fires on every incoming price tick.
|
|
@@ -3370,6 +3413,37 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3370
3413
|
}
|
|
3371
3414
|
};
|
|
3372
3415
|
|
|
3416
|
+
// src/data/internal/aggregations.ts
|
|
3417
|
+
function aggregateMarginSummary(pairPositions) {
|
|
3418
|
+
let accountValue = 0;
|
|
3419
|
+
let totalCollateralUsed = 0;
|
|
3420
|
+
let totalNtlPos = 0;
|
|
3421
|
+
let totalRawPnlUsd = 0;
|
|
3422
|
+
let totalCumRollover = 0;
|
|
3423
|
+
let totalWithdrawable = 0;
|
|
3424
|
+
for (const { position } of pairPositions) {
|
|
3425
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3426
|
+
const ntl = parseFloat(position.ntl) || 0;
|
|
3427
|
+
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
3428
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3429
|
+
const withdrawable = parseFloat(position.maxWithdrawable) || 0;
|
|
3430
|
+
accountValue += collateral + pnl;
|
|
3431
|
+
totalCollateralUsed += collateral;
|
|
3432
|
+
totalNtlPos += ntl;
|
|
3433
|
+
totalRawPnlUsd += pnl;
|
|
3434
|
+
totalCumRollover += rollover;
|
|
3435
|
+
totalWithdrawable += withdrawable;
|
|
3436
|
+
}
|
|
3437
|
+
return {
|
|
3438
|
+
accountValue: accountValue.toString(),
|
|
3439
|
+
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
3440
|
+
totalNtlPos: totalNtlPos.toString(),
|
|
3441
|
+
totalRawPnlUsd: totalRawPnlUsd.toString(),
|
|
3442
|
+
totalCumRollover: totalCumRollover.toString(),
|
|
3443
|
+
totalWithdrawable: totalWithdrawable.toString()
|
|
3444
|
+
};
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3373
3447
|
// src/data/internal/formatters.ts
|
|
3374
3448
|
var MIN_NOTIONAL = "5.0";
|
|
3375
3449
|
var MIN_NOTIONAL_NUM = 5;
|
|
@@ -3471,14 +3545,14 @@ function formatPosition(raw, price, pnl, maxLeverage) {
|
|
|
3471
3545
|
returnOnEquity: roe.toString(),
|
|
3472
3546
|
liquidationPx: pnl.liquidationPrice.toString(),
|
|
3473
3547
|
collateralUsed: collateral.toString(),
|
|
3474
|
-
cumRollover: pnl.rollover.toString(),
|
|
3548
|
+
cumRollover: (pnl.rollover * -1).toString(),
|
|
3475
3549
|
...raw.takeProfitPrice && raw.takeProfitPrice !== "0" ? { tpPx: formatTokens(raw.takeProfitPrice).toString() } : {},
|
|
3476
3550
|
...raw.stopLossPrice && raw.stopLossPrice !== "0" ? { slPx: formatTokens(raw.stopLossPrice).toString() } : {},
|
|
3477
3551
|
openTimestamp: parseInt(raw.timestamp || "0") * 1e3,
|
|
3478
3552
|
isDayTrade: raw.isDayTrade ?? false,
|
|
3479
|
-
maxLeverage: maxLeverage.toString()
|
|
3480
|
-
|
|
3481
|
-
|
|
3553
|
+
maxLeverage: maxLeverage.toString(),
|
|
3554
|
+
maxWithdrawable: maxWithdrawForPosition(collateral, leverage, maxLeverage).toString()
|
|
3555
|
+
}
|
|
3482
3556
|
};
|
|
3483
3557
|
}
|
|
3484
3558
|
function formatFill(raw) {
|
|
@@ -3555,6 +3629,131 @@ function formatOpenOrder(raw) {
|
|
|
3555
3629
|
timestamp: parseInt(raw.initiatedAt || "0") * 1e3
|
|
3556
3630
|
};
|
|
3557
3631
|
}
|
|
3632
|
+
|
|
3633
|
+
// src/data/internal/position-updates.ts
|
|
3634
|
+
function cloneResponse(response) {
|
|
3635
|
+
return {
|
|
3636
|
+
pairPositions: response.pairPositions.map((pairPos) => ({
|
|
3637
|
+
position: { ...pairPos.position }
|
|
3638
|
+
})),
|
|
3639
|
+
marginSummary: { ...response.marginSummary },
|
|
3640
|
+
time: response.time
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
function applyTickToPosition(position, tick) {
|
|
3644
|
+
const size = parseFloat(position.szi) || 0;
|
|
3645
|
+
const entry = parseFloat(position.entryPx) || 0;
|
|
3646
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3647
|
+
const maxLeverage = parseFloat(position.maxLeverage) || 0;
|
|
3648
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3649
|
+
const isLong = position.side === "B";
|
|
3650
|
+
const ntl = size * tick.mid;
|
|
3651
|
+
const rawPnl = isLong ? (tick.mid - entry) * size : (entry - tick.mid) * size;
|
|
3652
|
+
const netPnl = rawPnl + rollover;
|
|
3653
|
+
const roe = collateral > 0 ? netPnl / collateral : 0;
|
|
3654
|
+
const currentLeverage = collateral > 0 ? ntl / collateral : 0;
|
|
3655
|
+
const maxWithdrawable = maxWithdrawForPosition(collateral, currentLeverage, maxLeverage);
|
|
3656
|
+
return {
|
|
3657
|
+
...position,
|
|
3658
|
+
ntl: ntl.toString(),
|
|
3659
|
+
unrealizedPnl: netPnl.toString(),
|
|
3660
|
+
returnOnEquity: roe.toString(),
|
|
3661
|
+
maxWithdrawable: maxWithdrawable.toString()
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
function updateResponseWithTick(current, tick, timestampMs) {
|
|
3665
|
+
if (!tick.pairId) return current;
|
|
3666
|
+
let changed = false;
|
|
3667
|
+
const pairPositions = current.pairPositions.map((pairPos) => {
|
|
3668
|
+
if (String(pairPos.position.pairId) !== String(tick.pairId)) {
|
|
3669
|
+
return pairPos;
|
|
3670
|
+
}
|
|
3671
|
+
changed = true;
|
|
3672
|
+
return {
|
|
3673
|
+
...pairPos,
|
|
3674
|
+
position: applyTickToPosition(pairPos.position, tick)
|
|
3675
|
+
};
|
|
3676
|
+
});
|
|
3677
|
+
if (!changed) return current;
|
|
3678
|
+
return {
|
|
3679
|
+
pairPositions,
|
|
3680
|
+
marginSummary: aggregateMarginSummary(pairPositions),
|
|
3681
|
+
time: timestampMs
|
|
3682
|
+
};
|
|
3683
|
+
}
|
|
3684
|
+
var OstiumPositionUpdatesStream = class {
|
|
3685
|
+
priceStream;
|
|
3686
|
+
updateHandlers = /* @__PURE__ */ new Set();
|
|
3687
|
+
current;
|
|
3688
|
+
trackedPairIds;
|
|
3689
|
+
constructor(initial, trackedPairIds, priceStream) {
|
|
3690
|
+
this.current = cloneResponse(initial);
|
|
3691
|
+
this.priceStream = priceStream;
|
|
3692
|
+
this.trackedPairIds = new Set(trackedPairIds);
|
|
3693
|
+
if (!priceStream) return;
|
|
3694
|
+
priceStream.onSnapshot((ticks) => {
|
|
3695
|
+
this.ingestSnapshot(ticks);
|
|
3696
|
+
});
|
|
3697
|
+
priceStream.onTick((tick) => {
|
|
3698
|
+
this.ingestTick(tick);
|
|
3699
|
+
});
|
|
3700
|
+
}
|
|
3701
|
+
onUpdate(handler) {
|
|
3702
|
+
this.updateHandlers.add(handler);
|
|
3703
|
+
return () => {
|
|
3704
|
+
this.updateHandlers.delete(handler);
|
|
3705
|
+
};
|
|
3706
|
+
}
|
|
3707
|
+
onOpen(handler) {
|
|
3708
|
+
this.priceStream?.onOpen(handler);
|
|
3709
|
+
return this;
|
|
3710
|
+
}
|
|
3711
|
+
onError(handler) {
|
|
3712
|
+
this.priceStream?.onError(handler);
|
|
3713
|
+
return this;
|
|
3714
|
+
}
|
|
3715
|
+
onClose(handler) {
|
|
3716
|
+
this.priceStream?.onClose(handler);
|
|
3717
|
+
return this;
|
|
3718
|
+
}
|
|
3719
|
+
getCurrent() {
|
|
3720
|
+
return cloneResponse(this.current);
|
|
3721
|
+
}
|
|
3722
|
+
/**
|
|
3723
|
+
* Apply a single externally sourced price tick to the tracked positions.
|
|
3724
|
+
*
|
|
3725
|
+
* Use this when your app already has its own websocket connection and you want
|
|
3726
|
+
* the SDK to handle only the position recalculation logic.
|
|
3727
|
+
*/
|
|
3728
|
+
ingestTick(tick) {
|
|
3729
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) return;
|
|
3730
|
+
const next = updateResponseWithTick(this.current, tick, tick.timestampSeconds * 1e3);
|
|
3731
|
+
if (next !== this.current) {
|
|
3732
|
+
this.current = next;
|
|
3733
|
+
this.emit(next);
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
/**
|
|
3737
|
+
* Apply a batch of externally sourced ticks, such as a websocket snapshot.
|
|
3738
|
+
*/
|
|
3739
|
+
ingestSnapshot(ticks) {
|
|
3740
|
+
let next = this.current;
|
|
3741
|
+
for (const tick of ticks) {
|
|
3742
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) continue;
|
|
3743
|
+
next = updateResponseWithTick(next, tick, tick.timestampSeconds * 1e3);
|
|
3744
|
+
}
|
|
3745
|
+
if (next !== this.current) {
|
|
3746
|
+
this.current = next;
|
|
3747
|
+
this.emit(next);
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
close() {
|
|
3751
|
+
this.priceStream?.close();
|
|
3752
|
+
}
|
|
3753
|
+
emit(positions) {
|
|
3754
|
+
for (const handler of this.updateHandlers) handler(cloneResponse(positions));
|
|
3755
|
+
}
|
|
3756
|
+
};
|
|
3558
3757
|
var EMPTY_RESULT = { long: [], short: [] };
|
|
3559
3758
|
var ORDERBOOK_MAX_LEVELS = 20;
|
|
3560
3759
|
function generateLogSpacedNotionals(levels, capacity) {
|
|
@@ -3739,31 +3938,40 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
3739
3938
|
* PnL fields are zeroed.
|
|
3740
3939
|
*/
|
|
3741
3940
|
async getOpenPositions(params) {
|
|
3742
|
-
const { user, blockNumber } = params;
|
|
3941
|
+
const { user, blockNumber, limit = Infinity, skip: initialSkip = 0 } = params;
|
|
3942
|
+
const isGlobal = user === "ALL";
|
|
3743
3943
|
const [trades, prices] = await Promise.all([
|
|
3744
|
-
paginateAll(
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3944
|
+
paginateAll(
|
|
3945
|
+
async (skip, first) => {
|
|
3946
|
+
if (isGlobal) {
|
|
3947
|
+
const data2 = await this.query(
|
|
3948
|
+
GetAllOpenTradesQuery,
|
|
3949
|
+
{ skip, first }
|
|
3950
|
+
);
|
|
3951
|
+
return data2.trades;
|
|
3952
|
+
}
|
|
3953
|
+
const data = await this.query(
|
|
3954
|
+
GetTraderOpenTradesQuery,
|
|
3955
|
+
{ trader: user.toLowerCase(), skip, first }
|
|
3956
|
+
);
|
|
3957
|
+
return data.trades;
|
|
3958
|
+
},
|
|
3959
|
+
1e3,
|
|
3960
|
+
limit,
|
|
3961
|
+
initialSkip
|
|
3962
|
+
),
|
|
3751
3963
|
this.fetchLivePricesSafe()
|
|
3752
3964
|
]);
|
|
3753
|
-
let totalWithdrawable = 0;
|
|
3754
3965
|
const pairPositions = trades.map((trade) => {
|
|
3755
3966
|
const price = prices[`${trade.pair.from}/${trade.pair.to}`];
|
|
3756
3967
|
const pnl = price && blockNumber ? getTradePnL(trade, price, blockNumber) : EMPTY_PNL;
|
|
3757
3968
|
const maxLev = trade.isDayTrade ? formatLeverage(trade.pair.overnightMaxLeverage) : pairMaxLeverage(trade.pair);
|
|
3758
|
-
|
|
3759
|
-
totalWithdrawable += parseFloat(pairPos.maxWithdrawable);
|
|
3760
|
-
return pairPos;
|
|
3969
|
+
return formatPosition(trade, price, pnl, maxLev);
|
|
3761
3970
|
});
|
|
3762
3971
|
const marginSummary = aggregateMarginSummary(pairPositions);
|
|
3763
3972
|
return {
|
|
3764
3973
|
pairPositions,
|
|
3765
3974
|
marginSummary,
|
|
3766
|
-
withdrawable: totalWithdrawable.toString(),
|
|
3767
3975
|
time: Date.now()
|
|
3768
3976
|
};
|
|
3769
3977
|
}
|
|
@@ -4014,6 +4222,24 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4014
4222
|
});
|
|
4015
4223
|
return OstiumPriceStream.connect(this.builderApiUrl, rawNames, cache);
|
|
4016
4224
|
}
|
|
4225
|
+
/**
|
|
4226
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
4227
|
+
*
|
|
4228
|
+
* The SDK subscribes only to the unique pairs present in `initial.pairPositions`,
|
|
4229
|
+
* recalculates price-sensitive fields for affected positions on each tick, and
|
|
4230
|
+
* emits the full updated response.
|
|
4231
|
+
*
|
|
4232
|
+
* Pass an existing `priceStream` to reuse a websocket connection your app has
|
|
4233
|
+
* already opened. Omit it to let the SDK open and manage a dedicated
|
|
4234
|
+
* connection for the tracked pair ids.
|
|
4235
|
+
*/
|
|
4236
|
+
streamPositionUpdates(initial, priceStream) {
|
|
4237
|
+
const pairIds = [...new Set(initial.pairPositions.map(({ position }) => String(position.pairId)))];
|
|
4238
|
+
if (pairIds.length === 0) {
|
|
4239
|
+
return new OstiumPositionUpdatesStream(initial, []);
|
|
4240
|
+
}
|
|
4241
|
+
return new OstiumPositionUpdatesStream(initial, pairIds, priceStream ?? this.streamPrices(pairIds));
|
|
4242
|
+
}
|
|
4017
4243
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4018
4244
|
// Internal — fetchers, cache, query wrapper
|
|
4019
4245
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -4072,43 +4298,28 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4072
4298
|
}
|
|
4073
4299
|
}
|
|
4074
4300
|
};
|
|
4075
|
-
function aggregateMarginSummary(pairPositions) {
|
|
4076
|
-
let accountValue = 0;
|
|
4077
|
-
let totalCollateralUsed = 0;
|
|
4078
|
-
let totalNtlPos = 0;
|
|
4079
|
-
let totalRawPnlUsd = 0;
|
|
4080
|
-
for (const { position } of pairPositions) {
|
|
4081
|
-
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
4082
|
-
const ntl = parseFloat(position.ntl) || 0;
|
|
4083
|
-
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
4084
|
-
accountValue += collateral + pnl;
|
|
4085
|
-
totalCollateralUsed += collateral;
|
|
4086
|
-
totalNtlPos += ntl;
|
|
4087
|
-
totalRawPnlUsd += pnl;
|
|
4088
|
-
}
|
|
4089
|
-
return {
|
|
4090
|
-
accountValue: accountValue.toString(),
|
|
4091
|
-
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
4092
|
-
totalNtlPos: totalNtlPos.toString(),
|
|
4093
|
-
totalRawPnlUsd: totalRawPnlUsd.toString()
|
|
4094
|
-
};
|
|
4095
|
-
}
|
|
4096
4301
|
|
|
4097
4302
|
// src/client.ts
|
|
4098
4303
|
var HEX64_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
4099
4304
|
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
4100
4305
|
function validateConfig(config) {
|
|
4101
|
-
if (!HEX64_RE.test(config.privateKey)) {
|
|
4306
|
+
if (config.privateKey && !HEX64_RE.test(config.privateKey)) {
|
|
4102
4307
|
throw new OstiumError(
|
|
4103
4308
|
"privateKey must be a 64-character hex string prefixed with 0x",
|
|
4104
4309
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4105
4310
|
);
|
|
4106
4311
|
}
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4312
|
+
for (const [label, value] of [
|
|
4313
|
+
["traderAddress", config.traderAddress],
|
|
4314
|
+
["delegateAddress", config.delegateAddress],
|
|
4315
|
+
["safeAddress", config.safeAddress]
|
|
4316
|
+
]) {
|
|
4317
|
+
if (value && !ADDRESS_RE.test(value)) {
|
|
4318
|
+
throw new OstiumError(
|
|
4319
|
+
`${label} must be a valid 42-character Ethereum address (0x + 40 hex chars)`,
|
|
4320
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4321
|
+
);
|
|
4322
|
+
}
|
|
4112
4323
|
}
|
|
4113
4324
|
if (config.slippageBps !== void 0 && (config.slippageBps < 0 || config.slippageBps > 500)) {
|
|
4114
4325
|
throw new OstiumError(
|
|
@@ -4122,10 +4333,45 @@ function validateConfig(config) {
|
|
|
4122
4333
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4123
4334
|
);
|
|
4124
4335
|
}
|
|
4336
|
+
let mode;
|
|
4337
|
+
try {
|
|
4338
|
+
mode = resolveMode(config);
|
|
4339
|
+
} catch {
|
|
4340
|
+
throw new OstiumError(
|
|
4341
|
+
"Could not infer client mode from config. Provide the required addresses for the selected factory.",
|
|
4342
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4343
|
+
);
|
|
4344
|
+
}
|
|
4345
|
+
if (mode === "self-self" && !config.privateKey && !config.traderAddress) {
|
|
4346
|
+
throw new OstiumError(
|
|
4347
|
+
"Self + Self mode requires traderPrivateKey for submission or traderAddress for build-only usage.",
|
|
4348
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4349
|
+
);
|
|
4350
|
+
}
|
|
4351
|
+
if (mode === "self-gasless" && !config.privateKey && (!config.traderAddress || !config.safeAddress)) {
|
|
4352
|
+
throw new OstiumError(
|
|
4353
|
+
"Self + Gasless build-only mode requires traderAddress and safeAddress.",
|
|
4354
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4355
|
+
);
|
|
4356
|
+
}
|
|
4357
|
+
if (mode === "delegated-self" && (!config.traderAddress || !config.privateKey && !config.delegateAddress)) {
|
|
4358
|
+
throw new OstiumError(
|
|
4359
|
+
"Delegated + Self mode requires traderAddress and either delegatePrivateKey or delegateAddress.",
|
|
4360
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4361
|
+
);
|
|
4362
|
+
}
|
|
4363
|
+
if (mode === "delegated-gasless" && (!config.traderAddress || !config.privateKey && (!config.delegateAddress || !config.safeAddress))) {
|
|
4364
|
+
throw new OstiumError(
|
|
4365
|
+
"Delegated + Gasless mode requires traderAddress and either delegatePrivateKey or both delegateAddress and safeAddress.",
|
|
4366
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4367
|
+
);
|
|
4368
|
+
}
|
|
4125
4369
|
}
|
|
4126
4370
|
var OstiumClient = class _OstiumClient {
|
|
4127
4371
|
signer;
|
|
4128
4372
|
submitter;
|
|
4373
|
+
traderAddress;
|
|
4374
|
+
effectiveSender;
|
|
4129
4375
|
contracts;
|
|
4130
4376
|
publicClient;
|
|
4131
4377
|
defaultSlippageBps;
|
|
@@ -4140,6 +4386,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4140
4386
|
constructor(internals) {
|
|
4141
4387
|
this.signer = internals.signer;
|
|
4142
4388
|
this.submitter = internals.submitter;
|
|
4389
|
+
this.traderAddress = internals.traderAddress;
|
|
4390
|
+
this.effectiveSender = internals.effectiveSender;
|
|
4143
4391
|
this.contracts = internals.contracts;
|
|
4144
4392
|
this.publicClient = internals.publicClient;
|
|
4145
4393
|
this.defaultSlippageBps = internals.defaultSlippageBps;
|
|
@@ -4164,17 +4412,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4164
4412
|
* });
|
|
4165
4413
|
* await client.openTrade({ ... });
|
|
4166
4414
|
* ```
|
|
4167
|
-
|
|
4415
|
+
*/
|
|
4168
4416
|
static async createSelfAndSelf(params) {
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
rpcUrl: params.rpcUrl,
|
|
4417
|
+
const base = {
|
|
4418
|
+
mode: "self-self",
|
|
4172
4419
|
testnet: params.testnet,
|
|
4173
4420
|
slippageBps: params.slippageBps,
|
|
4174
4421
|
builder: params.builder,
|
|
4175
4422
|
subgraphUrl: params.subgraphUrl,
|
|
4176
|
-
builderApiUrl: params.builderApiUrl
|
|
4177
|
-
|
|
4423
|
+
builderApiUrl: params.builderApiUrl,
|
|
4424
|
+
rpcUrl: params.rpcUrl
|
|
4425
|
+
};
|
|
4426
|
+
return _OstiumClient._fromConfig(
|
|
4427
|
+
"traderPrivateKey" in params ? { ...base, privateKey: params.traderPrivateKey } : { ...base, traderAddress: params.traderAddress }
|
|
4428
|
+
);
|
|
4178
4429
|
}
|
|
4179
4430
|
/**
|
|
4180
4431
|
* **Self + Gasless** — your EOA owns everything; a Safe relays trades for free.
|
|
@@ -4198,17 +4449,27 @@ var OstiumClient = class _OstiumClient {
|
|
|
4198
4449
|
*/
|
|
4199
4450
|
static async createSelfAndGasless(params) {
|
|
4200
4451
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4452
|
+
const base = {
|
|
4453
|
+
mode: "self-gasless",
|
|
4204
4454
|
rpcUrl: params.rpcUrl,
|
|
4205
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4206
4455
|
testnet: params.testnet,
|
|
4207
4456
|
slippageBps: params.slippageBps,
|
|
4208
4457
|
builder: params.builder,
|
|
4209
4458
|
subgraphUrl: params.subgraphUrl,
|
|
4210
4459
|
builderApiUrl: params.builderApiUrl
|
|
4211
|
-
}
|
|
4460
|
+
};
|
|
4461
|
+
return _OstiumClient._fromConfig(
|
|
4462
|
+
"traderPrivateKey" in params ? {
|
|
4463
|
+
...base,
|
|
4464
|
+
privateKey: params.traderPrivateKey,
|
|
4465
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4466
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4467
|
+
} : {
|
|
4468
|
+
...base,
|
|
4469
|
+
traderAddress: params.traderAddress,
|
|
4470
|
+
safeAddress: params.safeAddress
|
|
4471
|
+
}
|
|
4472
|
+
);
|
|
4212
4473
|
}
|
|
4213
4474
|
/**
|
|
4214
4475
|
* **Delegated + Self** — a delegate EOA signs and pays gas on behalf of a trader address.
|
|
@@ -4228,8 +4489,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4228
4489
|
* ```
|
|
4229
4490
|
*/
|
|
4230
4491
|
static async createDelegatedAndSelf(params) {
|
|
4231
|
-
|
|
4232
|
-
|
|
4492
|
+
const base = {
|
|
4493
|
+
mode: "delegated-self",
|
|
4233
4494
|
traderAddress: params.traderAddress,
|
|
4234
4495
|
rpcUrl: params.rpcUrl,
|
|
4235
4496
|
testnet: params.testnet,
|
|
@@ -4237,7 +4498,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4237
4498
|
builder: params.builder,
|
|
4238
4499
|
subgraphUrl: params.subgraphUrl,
|
|
4239
4500
|
builderApiUrl: params.builderApiUrl
|
|
4240
|
-
}
|
|
4501
|
+
};
|
|
4502
|
+
return _OstiumClient._fromConfig(
|
|
4503
|
+
"delegatePrivateKey" in params ? { ...base, privateKey: params.delegatePrivateKey } : { ...base, delegateAddress: params.delegateAddress }
|
|
4504
|
+
);
|
|
4241
4505
|
}
|
|
4242
4506
|
/**
|
|
4243
4507
|
* **Delegated + Gasless** — a Safe derived from the delegate key submits sponsored
|
|
@@ -4258,18 +4522,28 @@ var OstiumClient = class _OstiumClient {
|
|
|
4258
4522
|
*/
|
|
4259
4523
|
static async createDelegatedAndGasless(params) {
|
|
4260
4524
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4261
|
-
|
|
4262
|
-
|
|
4525
|
+
const base = {
|
|
4526
|
+
mode: "delegated-gasless",
|
|
4263
4527
|
traderAddress: params.traderAddress,
|
|
4264
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4265
4528
|
rpcUrl: params.rpcUrl,
|
|
4266
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4267
4529
|
testnet: params.testnet,
|
|
4268
4530
|
slippageBps: params.slippageBps,
|
|
4269
4531
|
builder: params.builder,
|
|
4270
4532
|
subgraphUrl: params.subgraphUrl,
|
|
4271
4533
|
builderApiUrl: params.builderApiUrl
|
|
4272
|
-
}
|
|
4534
|
+
};
|
|
4535
|
+
return _OstiumClient._fromConfig(
|
|
4536
|
+
"delegatePrivateKey" in params ? {
|
|
4537
|
+
...base,
|
|
4538
|
+
privateKey: params.delegatePrivateKey,
|
|
4539
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4540
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4541
|
+
} : {
|
|
4542
|
+
...base,
|
|
4543
|
+
delegateAddress: params.delegateAddress,
|
|
4544
|
+
safeAddress: params.safeAddress
|
|
4545
|
+
}
|
|
4546
|
+
);
|
|
4273
4547
|
}
|
|
4274
4548
|
/**
|
|
4275
4549
|
* **Read-only client** — no signer, no submitter, no privateKey required.
|
|
@@ -4314,6 +4588,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4314
4588
|
/** Internal factory shared by all public constructors. */
|
|
4315
4589
|
static async _fromConfig(config) {
|
|
4316
4590
|
validateConfig(config);
|
|
4591
|
+
const mode = resolveMode(config);
|
|
4317
4592
|
const testnet = config.testnet ?? false;
|
|
4318
4593
|
const networkConfig = getNetworkConfig(testnet);
|
|
4319
4594
|
const chain = testnet ? arbitrumSepolia : arbitrum;
|
|
@@ -4323,18 +4598,29 @@ var OstiumClient = class _OstiumClient {
|
|
|
4323
4598
|
tradingStorage: networkConfig.contracts.tradingStorage,
|
|
4324
4599
|
usdc: networkConfig.contracts.usdc
|
|
4325
4600
|
};
|
|
4326
|
-
const selfGasless =
|
|
4327
|
-
const submitter = await createSubmitter(config, testnet);
|
|
4601
|
+
const selfGasless = mode === "self-gasless";
|
|
4328
4602
|
let traderAddress;
|
|
4329
|
-
if (
|
|
4603
|
+
if (mode === "self-self" || mode === "self-gasless") {
|
|
4604
|
+
traderAddress = config.traderAddress ?? privateKeyToAccount(config.privateKey).address;
|
|
4605
|
+
} else {
|
|
4330
4606
|
traderAddress = config.traderAddress;
|
|
4607
|
+
}
|
|
4608
|
+
const signer = createSigner(mode, traderAddress);
|
|
4609
|
+
let submitter;
|
|
4610
|
+
let effectiveSender;
|
|
4611
|
+
if (hasPrivateKey(config)) {
|
|
4612
|
+
submitter = await createSubmitter(config, testnet, mode);
|
|
4613
|
+
effectiveSender = submitter.effectiveSender;
|
|
4614
|
+
} else if (mode === "self-self") {
|
|
4615
|
+
effectiveSender = traderAddress;
|
|
4616
|
+
} else if (mode === "delegated-self") {
|
|
4617
|
+
effectiveSender = config.delegateAddress;
|
|
4331
4618
|
} else {
|
|
4332
|
-
|
|
4619
|
+
effectiveSender = config.safeAddress;
|
|
4333
4620
|
}
|
|
4334
|
-
const signer = createSigner(config, traderAddress, selfGasless);
|
|
4335
4621
|
const publicRpc = testnet ? "https://sepolia-rollup.arbitrum.io/rpc" : "https://arb1.arbitrum.io/rpc";
|
|
4336
|
-
const rpcUrl = config.rpcUrl ??
|
|
4337
|
-
if (
|
|
4622
|
+
const rpcUrl = config.rpcUrl ?? publicRpc;
|
|
4623
|
+
if (hasPrivateKey(config) && mode !== "self-gasless" && mode !== "delegated-gasless" && !config.rpcUrl) {
|
|
4338
4624
|
throw new OstiumError(
|
|
4339
4625
|
"rpcUrl is required for self-submission mode",
|
|
4340
4626
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
@@ -4342,7 +4628,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4342
4628
|
}
|
|
4343
4629
|
const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });
|
|
4344
4630
|
let eoaSubmit;
|
|
4345
|
-
if (selfGasless) {
|
|
4631
|
+
if (selfGasless && hasPrivateKey(config)) {
|
|
4346
4632
|
const eoaAccount = privateKeyToAccount(config.privateKey);
|
|
4347
4633
|
const eoaRpcUrl = config.rpcUrl ?? publicRpc;
|
|
4348
4634
|
const eoaWalletClient = createWalletClient({ account: eoaAccount, chain, transport: http(eoaRpcUrl) });
|
|
@@ -4364,11 +4650,13 @@ var OstiumClient = class _OstiumClient {
|
|
|
4364
4650
|
return new _OstiumClient({
|
|
4365
4651
|
signer,
|
|
4366
4652
|
submitter,
|
|
4653
|
+
traderAddress,
|
|
4654
|
+
effectiveSender,
|
|
4367
4655
|
contracts,
|
|
4368
4656
|
publicClient,
|
|
4369
4657
|
defaultSlippageBps,
|
|
4370
|
-
delegated:
|
|
4371
|
-
gasless:
|
|
4658
|
+
delegated: mode === "delegated-self" || mode === "delegated-gasless",
|
|
4659
|
+
gasless: mode === "self-gasless" || mode === "delegated-gasless",
|
|
4372
4660
|
selfGasless,
|
|
4373
4661
|
eoaSubmit,
|
|
4374
4662
|
builderAddress: config.builder?.address,
|
|
@@ -4386,17 +4674,25 @@ var OstiumClient = class _OstiumClient {
|
|
|
4386
4674
|
* - Self + Gasless: EOA derived from privateKey (NOT the Safe — USDC lives here).
|
|
4387
4675
|
*/
|
|
4388
4676
|
getTraderAddress() {
|
|
4389
|
-
if (!this.
|
|
4677
|
+
if (!this.traderAddress) {
|
|
4390
4678
|
throw new OstiumError(
|
|
4391
4679
|
"No connected trader \u2014 pass `user` explicitly or use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4392
4680
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4393
4681
|
);
|
|
4394
4682
|
}
|
|
4395
|
-
return this.
|
|
4683
|
+
return this.traderAddress;
|
|
4396
4684
|
}
|
|
4397
|
-
/** True when the client
|
|
4685
|
+
/** True when the client cannot submit transactions directly. */
|
|
4398
4686
|
isReadOnly() {
|
|
4399
|
-
return !this.
|
|
4687
|
+
return !this.submitter;
|
|
4688
|
+
}
|
|
4689
|
+
/** True when the client can build mode-correct unsigned transaction requests. */
|
|
4690
|
+
canBuildTransactions() {
|
|
4691
|
+
return !!this.signer && !!this.effectiveSender;
|
|
4692
|
+
}
|
|
4693
|
+
/** True when the client has the credentials needed for SDK-managed submission. */
|
|
4694
|
+
canSubmitTransactions() {
|
|
4695
|
+
return !!this.submitter;
|
|
4400
4696
|
}
|
|
4401
4697
|
/**
|
|
4402
4698
|
* Returns the Safe smart-account address used for gasless submission.
|
|
@@ -4408,7 +4704,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4408
4704
|
* - Self + Self / Delegated + Self: returns undefined.
|
|
4409
4705
|
*/
|
|
4410
4706
|
getSmartAccountAddress() {
|
|
4411
|
-
return this.gasless
|
|
4707
|
+
return this.gasless ? this.effectiveSender : void 0;
|
|
4412
4708
|
}
|
|
4413
4709
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4414
4710
|
// Self + Gasless setup
|
|
@@ -4432,15 +4728,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4432
4728
|
* ```
|
|
4433
4729
|
*/
|
|
4434
4730
|
async setupGaslessDelegation() {
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
);
|
|
4440
|
-
}
|
|
4441
|
-
const safeAddress = this.submitter.effectiveSender;
|
|
4442
|
-
const encoded = encodeSetDelegate(safeAddress, this.contracts.trading);
|
|
4443
|
-
return this.submitDirectFromEoa(encoded);
|
|
4731
|
+
return this.submitDirectEoa(this.getSetupGaslessDelegationEncoded());
|
|
4732
|
+
}
|
|
4733
|
+
getSetupGaslessDelegationTx() {
|
|
4734
|
+
return this.buildDirectEoaTx(this.getSetupGaslessDelegationEncoded());
|
|
4444
4735
|
}
|
|
4445
4736
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4446
4737
|
// USDC helpers
|
|
@@ -4502,18 +4793,14 @@ var OstiumClient = class _OstiumClient {
|
|
|
4502
4793
|
* @param amount USD amount as decimal string (e.g. "1000"), or "max" for MaxUint256.
|
|
4503
4794
|
*/
|
|
4504
4795
|
async approveUsdc(amount) {
|
|
4505
|
-
|
|
4506
|
-
throw new OstiumError(
|
|
4507
|
-
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
4508
|
-
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
4509
|
-
);
|
|
4510
|
-
}
|
|
4511
|
-
const rawAmount = amount === "max" ? maxUint256 : parseUsdc(amount);
|
|
4512
|
-
const encoded = encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
4796
|
+
const encoded = this.getApproveUsdcEncoded(amount);
|
|
4513
4797
|
if (this.selfGasless) {
|
|
4514
|
-
return this.
|
|
4798
|
+
return this.submitDirectEoa(encoded);
|
|
4515
4799
|
}
|
|
4516
|
-
return this.
|
|
4800
|
+
return this.submitPrepared(encoded);
|
|
4801
|
+
}
|
|
4802
|
+
getApproveUsdcTx(amount) {
|
|
4803
|
+
return this.buildDirectEoaTx(this.getApproveUsdcEncoded(amount));
|
|
4517
4804
|
}
|
|
4518
4805
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4519
4806
|
// Delegation helpers
|
|
@@ -4527,16 +4814,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4527
4814
|
* update the trader's delegation on their behalf.
|
|
4528
4815
|
*/
|
|
4529
4816
|
async setDelegate(delegateAddress) {
|
|
4530
|
-
|
|
4531
|
-
|
|
4817
|
+
return this.submitPrepared(this.getSetDelegateEncoded(delegateAddress));
|
|
4818
|
+
}
|
|
4819
|
+
getSetDelegateTx(delegateAddress) {
|
|
4820
|
+
return this.buildPreparedTx(this.getSetDelegateEncoded(delegateAddress));
|
|
4532
4821
|
}
|
|
4533
4822
|
/**
|
|
4534
4823
|
* Remove the current delegate on the trading contract.
|
|
4535
4824
|
* Follows the same delegation-wrapping rules as setDelegate().
|
|
4536
4825
|
*/
|
|
4537
4826
|
async removeDelegate() {
|
|
4538
|
-
|
|
4539
|
-
|
|
4827
|
+
return this.submitPrepared(this.getRemoveDelegateEncoded());
|
|
4828
|
+
}
|
|
4829
|
+
getRemoveDelegateTx() {
|
|
4830
|
+
return this.buildPreparedTx(this.getRemoveDelegateEncoded());
|
|
4540
4831
|
}
|
|
4541
4832
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4542
4833
|
// Core trading methods
|
|
@@ -4549,6 +4840,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4549
4840
|
* the current allowance.
|
|
4550
4841
|
*/
|
|
4551
4842
|
async openTrade(params) {
|
|
4843
|
+
return this.submitPrepared(this.getOpenTradeEncoded(params));
|
|
4844
|
+
}
|
|
4845
|
+
getOpenTradeTx(params) {
|
|
4846
|
+
return this.buildPreparedTx(this.getOpenTradeEncoded(params));
|
|
4847
|
+
}
|
|
4848
|
+
getOpenTradeEncoded(params) {
|
|
4552
4849
|
const collateralNum = parseFloat(params.collateral);
|
|
4553
4850
|
if (collateralNum > MAX_COLLATERAL_USD) {
|
|
4554
4851
|
throw new OstiumError(
|
|
@@ -4557,7 +4854,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4557
4854
|
);
|
|
4558
4855
|
}
|
|
4559
4856
|
const apiOpen = {
|
|
4560
|
-
a: params.
|
|
4857
|
+
a: Number(params.pairId),
|
|
4561
4858
|
b: params.buy,
|
|
4562
4859
|
p: params.price,
|
|
4563
4860
|
s: params.collateral,
|
|
@@ -4580,8 +4877,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4580
4877
|
}
|
|
4581
4878
|
const builderFee = buildBuilderFee(apiBuilder);
|
|
4582
4879
|
const slippageP = params.type === "market" /* Market */ ? BigInt(this.resolveSlippage(params.slippage)) : 0n;
|
|
4583
|
-
|
|
4584
|
-
return this.execute(encoded);
|
|
4880
|
+
return encodeOpenTrade(trade, builderFee, orderType, slippageP, this.contracts.trading);
|
|
4585
4881
|
}
|
|
4586
4882
|
/**
|
|
4587
4883
|
* Close an open position (full or partial).
|
|
@@ -4596,6 +4892,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4596
4892
|
* ```
|
|
4597
4893
|
*/
|
|
4598
4894
|
async closeTrade(params) {
|
|
4895
|
+
return this.submitPrepared(this.getCloseTradeEncoded(params));
|
|
4896
|
+
}
|
|
4897
|
+
getCloseTradeTx(params) {
|
|
4898
|
+
return this.buildPreparedTx(this.getCloseTradeEncoded(params));
|
|
4899
|
+
}
|
|
4900
|
+
getCloseTradeEncoded(params) {
|
|
4599
4901
|
try {
|
|
4600
4902
|
validateCloseParams({
|
|
4601
4903
|
a: Number(params.pairId),
|
|
@@ -4615,7 +4917,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4615
4917
|
const closePercentage = BigInt(Math.round(params.closePercent * 100));
|
|
4616
4918
|
const marketPrice = parsePrice(params.price);
|
|
4617
4919
|
const slippageP = BigInt(this.resolveSlippage(params.slippage));
|
|
4618
|
-
|
|
4920
|
+
return encodeCloseTradeMarket(
|
|
4619
4921
|
pairIndex,
|
|
4620
4922
|
index,
|
|
4621
4923
|
closePercentage,
|
|
@@ -4623,7 +4925,6 @@ var OstiumClient = class _OstiumClient {
|
|
|
4623
4925
|
slippageP,
|
|
4624
4926
|
this.contracts.trading
|
|
4625
4927
|
);
|
|
4626
|
-
return this.execute(encoded);
|
|
4627
4928
|
}
|
|
4628
4929
|
/**
|
|
4629
4930
|
* Cancel a pending order.
|
|
@@ -4646,6 +4947,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4646
4947
|
* ```
|
|
4647
4948
|
*/
|
|
4648
4949
|
async cancelOrder(params) {
|
|
4950
|
+
return this.submitPrepared(this.getCancelOrderEncoded(params));
|
|
4951
|
+
}
|
|
4952
|
+
getCancelOrderTx(params) {
|
|
4953
|
+
return this.buildPreparedTx(this.getCancelOrderEncoded(params));
|
|
4954
|
+
}
|
|
4955
|
+
getCancelOrderEncoded(params) {
|
|
4649
4956
|
let encoded;
|
|
4650
4957
|
if (params.type === "limit" /* Limit */) {
|
|
4651
4958
|
const pairIdNum = Number(params.pairId);
|
|
@@ -4682,7 +4989,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4682
4989
|
}
|
|
4683
4990
|
encoded = encodeOpenTradeMarketTimeout(BigInt(params.orderId), this.contracts.trading);
|
|
4684
4991
|
}
|
|
4685
|
-
return
|
|
4992
|
+
return encoded;
|
|
4686
4993
|
}
|
|
4687
4994
|
/**
|
|
4688
4995
|
* Modify an open trade or a pending limit order.
|
|
@@ -4707,6 +5014,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4707
5014
|
* - Both `takeProfit` and `stopLoss` without `price` → throws (send two calls).
|
|
4708
5015
|
*/
|
|
4709
5016
|
async modifyOrder(params) {
|
|
5017
|
+
return this.submitPrepared(this.getModifyOrderEncoded(params));
|
|
5018
|
+
}
|
|
5019
|
+
getModifyOrderTx(params) {
|
|
5020
|
+
return this.buildPreparedTx(this.getModifyOrderEncoded(params));
|
|
5021
|
+
}
|
|
5022
|
+
getModifyOrderEncoded(params) {
|
|
4710
5023
|
try {
|
|
4711
5024
|
validateModifyParams({
|
|
4712
5025
|
a: Number(params.pairId),
|
|
@@ -4746,7 +5059,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4746
5059
|
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4747
5060
|
);
|
|
4748
5061
|
}
|
|
4749
|
-
return
|
|
5062
|
+
return encoded;
|
|
4750
5063
|
}
|
|
4751
5064
|
/**
|
|
4752
5065
|
* Update collateral on an open position (isolated margin).
|
|
@@ -4766,6 +5079,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4766
5079
|
* Checks USDC allowance before top-up operations.
|
|
4767
5080
|
*/
|
|
4768
5081
|
async updateCollateral(params) {
|
|
5082
|
+
return this.submitPrepared(await this.getUpdateCollateralEncoded(params));
|
|
5083
|
+
}
|
|
5084
|
+
async getUpdateCollateralTx(params) {
|
|
5085
|
+
return this.buildPreparedTx(await this.getUpdateCollateralEncoded(params));
|
|
5086
|
+
}
|
|
5087
|
+
async getUpdateCollateralEncoded(params) {
|
|
4769
5088
|
const amount = parseFloat(params.amount);
|
|
4770
5089
|
if (Number.isNaN(amount) || amount === 0) {
|
|
4771
5090
|
throw new OstiumError(
|
|
@@ -4789,7 +5108,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4789
5108
|
} else {
|
|
4790
5109
|
encoded = encodeRemoveCollateral(pairIndex, index, absAmount, this.contracts.trading);
|
|
4791
5110
|
}
|
|
4792
|
-
return
|
|
5111
|
+
return encoded;
|
|
4793
5112
|
}
|
|
4794
5113
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4795
5114
|
// Subgraph reads (Hyperliquid-style API)
|
|
@@ -4808,11 +5127,18 @@ var OstiumClient = class _OstiumClient {
|
|
|
4808
5127
|
/**
|
|
4809
5128
|
* Open positions + margin summary for a user. Defaults to the connected
|
|
4810
5129
|
* trader. Live prices and the current block number are fetched automatically.
|
|
5130
|
+
*
|
|
5131
|
+
* - `user`: pass an address to scope to a single trader, or `'ALL'` to fetch
|
|
5132
|
+
* positions across every trader (no trader filter).
|
|
5133
|
+
* - `limit`: cap the number of positions returned (default: all).
|
|
5134
|
+
* - `skip`: offset into the result set for pagination (default: `0`).
|
|
4811
5135
|
*/
|
|
4812
5136
|
async getOpenPositions(params) {
|
|
4813
5137
|
return this.subgraph.getOpenPositions({
|
|
4814
5138
|
user: params?.user ?? this.getTraderAddress(),
|
|
4815
|
-
blockNumber: params?.blockNumber ?? await this.publicClient.getBlockNumber()
|
|
5139
|
+
blockNumber: params?.blockNumber ?? await this.publicClient.getBlockNumber(),
|
|
5140
|
+
limit: params?.limit,
|
|
5141
|
+
skip: params?.skip
|
|
4816
5142
|
});
|
|
4817
5143
|
}
|
|
4818
5144
|
/**
|
|
@@ -4917,31 +5243,104 @@ var OstiumClient = class _OstiumClient {
|
|
|
4917
5243
|
streamPrices(pairIds) {
|
|
4918
5244
|
return this.subgraph.streamPrices(pairIds);
|
|
4919
5245
|
}
|
|
5246
|
+
/**
|
|
5247
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
5248
|
+
*
|
|
5249
|
+
* Subscribes only to the unique pairs referenced by the response and emits the
|
|
5250
|
+
* full updated positions payload on each relevant price update. Pass an
|
|
5251
|
+
* existing `priceStream` to reuse a websocket your app already owns, or omit
|
|
5252
|
+
* it to let the SDK create a dedicated connection.
|
|
5253
|
+
*/
|
|
5254
|
+
streamPositionUpdates(initial, priceStream) {
|
|
5255
|
+
return this.subgraph.streamPositionUpdates(initial, priceStream);
|
|
5256
|
+
}
|
|
4920
5257
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4921
5258
|
// Internal helpers
|
|
4922
5259
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
* 1. signer.prepare() — optionally wraps in delegatedAction.
|
|
4926
|
-
* 2. submitter.submit() — sends via EOA walletClient or Pimlico UserOp.
|
|
4927
|
-
*/
|
|
4928
|
-
async execute(encoded) {
|
|
4929
|
-
const { signer, submitter } = this.requireWriter();
|
|
4930
|
-
const prepared = signer.prepare(encoded);
|
|
4931
|
-
return submitter.submit(prepared);
|
|
4932
|
-
}
|
|
4933
|
-
/** Throws if the client is read-only; otherwise returns the signer + submitter. */
|
|
4934
|
-
requireWriter() {
|
|
4935
|
-
if (!this.signer || !this.submitter) {
|
|
5260
|
+
getSetupGaslessDelegationEncoded() {
|
|
5261
|
+
if (!this.selfGasless || !this.effectiveSender) {
|
|
4936
5262
|
throw new OstiumError(
|
|
4937
|
-
"
|
|
5263
|
+
"setupGaslessDelegation() is only available in Self + Gasless mode.",
|
|
4938
5264
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4939
5265
|
);
|
|
4940
5266
|
}
|
|
4941
|
-
return
|
|
5267
|
+
return encodeSetDelegate(this.effectiveSender, this.contracts.trading);
|
|
5268
|
+
}
|
|
5269
|
+
getApproveUsdcEncoded(amount) {
|
|
5270
|
+
if (this.delegated) {
|
|
5271
|
+
throw new OstiumError(
|
|
5272
|
+
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
5273
|
+
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
5274
|
+
);
|
|
5275
|
+
}
|
|
5276
|
+
const rawAmount = amount === "max" ? maxUint256 : parseUsdc(amount);
|
|
5277
|
+
return encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
5278
|
+
}
|
|
5279
|
+
getSetDelegateEncoded(delegateAddress) {
|
|
5280
|
+
return encodeSetDelegate(delegateAddress, this.contracts.trading);
|
|
5281
|
+
}
|
|
5282
|
+
getRemoveDelegateEncoded() {
|
|
5283
|
+
return encodeRemoveDelegate(this.contracts.trading);
|
|
5284
|
+
}
|
|
5285
|
+
prepareEncoded(encoded) {
|
|
5286
|
+
const signer = this.requireBuildCapability();
|
|
5287
|
+
return signer.prepare(encoded);
|
|
5288
|
+
}
|
|
5289
|
+
buildPreparedTx(encoded) {
|
|
5290
|
+
return this.toBuiltTxRequest(this.prepareEncoded(encoded));
|
|
5291
|
+
}
|
|
5292
|
+
buildDirectEoaTx(encoded) {
|
|
5293
|
+
if (!this.canBuildTransactions() || !this.traderAddress) {
|
|
5294
|
+
throw new OstiumError(
|
|
5295
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5296
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5297
|
+
);
|
|
5298
|
+
}
|
|
5299
|
+
return {
|
|
5300
|
+
kind: "eoa",
|
|
5301
|
+
to: encoded.to,
|
|
5302
|
+
data: encoded.data,
|
|
5303
|
+
value: encoded.value,
|
|
5304
|
+
from: this.traderAddress,
|
|
5305
|
+
traderAddress: this.traderAddress
|
|
5306
|
+
};
|
|
5307
|
+
}
|
|
5308
|
+
toBuiltTxRequest(encoded) {
|
|
5309
|
+
if (!this.canBuildTransactions() || !this.traderAddress || !this.effectiveSender) {
|
|
5310
|
+
throw new OstiumError(
|
|
5311
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5312
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5313
|
+
);
|
|
5314
|
+
}
|
|
5315
|
+
if (this.gasless) {
|
|
5316
|
+
return {
|
|
5317
|
+
kind: "safe",
|
|
5318
|
+
safeAddress: this.effectiveSender,
|
|
5319
|
+
traderAddress: this.traderAddress,
|
|
5320
|
+
calls: [encoded]
|
|
5321
|
+
};
|
|
5322
|
+
}
|
|
5323
|
+
return {
|
|
5324
|
+
kind: "eoa",
|
|
5325
|
+
to: encoded.to,
|
|
5326
|
+
data: encoded.data,
|
|
5327
|
+
value: encoded.value,
|
|
5328
|
+
from: this.effectiveSender,
|
|
5329
|
+
traderAddress: this.traderAddress
|
|
5330
|
+
};
|
|
5331
|
+
}
|
|
5332
|
+
async submitPrepared(encoded) {
|
|
5333
|
+
const submitter = this.requireSubmitCapability();
|
|
5334
|
+
return submitter.submit(this.prepareEncoded(encoded));
|
|
4942
5335
|
}
|
|
4943
5336
|
/** Direct EOA submission — used only in Self + Gasless for approveUsdc and setupGaslessDelegation. */
|
|
4944
|
-
async
|
|
5337
|
+
async submitDirectEoa(encoded) {
|
|
5338
|
+
if (!this.eoaSubmit) {
|
|
5339
|
+
throw new OstiumError(
|
|
5340
|
+
"This client cannot submit direct EOA transactions.",
|
|
5341
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5342
|
+
);
|
|
5343
|
+
}
|
|
4945
5344
|
try {
|
|
4946
5345
|
return await this.eoaSubmit(encoded);
|
|
4947
5346
|
} catch (err) {
|
|
@@ -4957,6 +5356,24 @@ var OstiumClient = class _OstiumClient {
|
|
|
4957
5356
|
throw new OstiumError(`Transaction failed: ${msg}`, "SUBMISSION_FAILED" /* SUBMISSION_FAILED */, err);
|
|
4958
5357
|
}
|
|
4959
5358
|
}
|
|
5359
|
+
requireBuildCapability() {
|
|
5360
|
+
if (!this.signer) {
|
|
5361
|
+
throw new OstiumError(
|
|
5362
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5363
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5364
|
+
);
|
|
5365
|
+
}
|
|
5366
|
+
return this.signer;
|
|
5367
|
+
}
|
|
5368
|
+
requireSubmitCapability() {
|
|
5369
|
+
if (!this.submitter) {
|
|
5370
|
+
throw new OstiumError(
|
|
5371
|
+
"This client does not have submission credentials. Use the corresponding get*Tx() method or initialize with a private key.",
|
|
5372
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5373
|
+
);
|
|
5374
|
+
}
|
|
5375
|
+
return this.submitter;
|
|
5376
|
+
}
|
|
4960
5377
|
resolveSlippage(override) {
|
|
4961
5378
|
return override ?? this.defaultSlippageBps;
|
|
4962
5379
|
}
|
|
@@ -5839,7 +6256,7 @@ async function actionOpenTrade(client2) {
|
|
|
5839
6256
|
const result = await spin(
|
|
5840
6257
|
"Submitting openTrade\u2026",
|
|
5841
6258
|
() => client2.openTrade({
|
|
5842
|
-
|
|
6259
|
+
pairId,
|
|
5843
6260
|
buy: buy === "true",
|
|
5844
6261
|
price,
|
|
5845
6262
|
collateral,
|
|
@@ -5929,7 +6346,7 @@ async function actionCloseTrade(client2) {
|
|
|
5929
6346
|
}
|
|
5930
6347
|
async function actionCancelOrder(client2) {
|
|
5931
6348
|
let cancelType = "limit" /* Limit */;
|
|
5932
|
-
let
|
|
6349
|
+
let pairIdInput = "0";
|
|
5933
6350
|
let orderIndex = "0";
|
|
5934
6351
|
let orderId = "";
|
|
5935
6352
|
let retry = false;
|
|
@@ -5948,12 +6365,12 @@ async function actionCancelOrder(client2) {
|
|
|
5948
6365
|
}
|
|
5949
6366
|
case 1: {
|
|
5950
6367
|
if (cancelType === "limit" /* Limit */) {
|
|
5951
|
-
const v = await wtext({ message: "Pair
|
|
6368
|
+
const v = await wtext({ message: "Pair ID", initialValue: pairIdInput });
|
|
5952
6369
|
if (v === BACK) {
|
|
5953
6370
|
step--;
|
|
5954
6371
|
break;
|
|
5955
6372
|
}
|
|
5956
|
-
|
|
6373
|
+
pairIdInput = v;
|
|
5957
6374
|
step++;
|
|
5958
6375
|
break;
|
|
5959
6376
|
} else {
|
|
@@ -6000,7 +6417,7 @@ async function actionCancelOrder(client2) {
|
|
|
6000
6417
|
}
|
|
6001
6418
|
}
|
|
6002
6419
|
}
|
|
6003
|
-
const cancelParams = cancelType === "limit" /* Limit */ ? { type: "limit" /* Limit */, pairId: parseInt(
|
|
6420
|
+
const cancelParams = cancelType === "limit" /* Limit */ ? { type: "limit" /* Limit */, pairId: parseInt(pairIdInput), idx: parseInt(orderIndex) } : cancelType === "pendingClose" /* PendingClose */ ? { type: "pendingClose" /* PendingClose */, orderId: parseInt(orderId), retry } : { type: "pendingOpen" /* PendingOpen */, orderId: parseInt(orderId) };
|
|
6004
6421
|
const result = await spin(
|
|
6005
6422
|
"Submitting cancelOrder\u2026",
|
|
6006
6423
|
() => client2.cancelOrder(cancelParams)
|
|
@@ -6010,7 +6427,7 @@ async function actionCancelOrder(client2) {
|
|
|
6010
6427
|
async function actionModifyOrder(client2) {
|
|
6011
6428
|
const fields = await p.group(
|
|
6012
6429
|
{
|
|
6013
|
-
pairIndex: () => p.text({ message: "Pair
|
|
6430
|
+
pairIndex: () => p.text({ message: "Pair ID", initialValue: "0" }),
|
|
6014
6431
|
index: () => p.text({ message: "Trade / order index", initialValue: "0" }),
|
|
6015
6432
|
price: () => p.text({ message: "New limit price (optional)", placeholder: "leave blank to skip" }),
|
|
6016
6433
|
tp: () => p.text({ message: "New take profit (optional)", placeholder: "leave blank to skip" }),
|
|
@@ -6033,7 +6450,7 @@ async function actionModifyOrder(client2) {
|
|
|
6033
6450
|
async function actionUpdateCollateral(client2) {
|
|
6034
6451
|
const fields = await p.group(
|
|
6035
6452
|
{
|
|
6036
|
-
pairIndex: () => p.text({ message: "Pair
|
|
6453
|
+
pairIndex: () => p.text({ message: "Pair ID", initialValue: "0" }),
|
|
6037
6454
|
index: () => p.text({ message: "Trade index", initialValue: "0" }),
|
|
6038
6455
|
amount: () => p.text({
|
|
6039
6456
|
message: "Amount (positive = add, negative = remove)",
|
|
@@ -6172,7 +6589,7 @@ async function actionViewPrices(client2) {
|
|
|
6172
6589
|
}
|
|
6173
6590
|
}
|
|
6174
6591
|
async function actionViewPositions(client2) {
|
|
6175
|
-
const { pairPositions, marginSummary
|
|
6592
|
+
const { pairPositions, marginSummary } = await spin(
|
|
6176
6593
|
"Fetching open positions\u2026",
|
|
6177
6594
|
() => client2.getOpenPositions()
|
|
6178
6595
|
);
|
|
@@ -6209,7 +6626,8 @@ async function actionViewPositions(client2) {
|
|
|
6209
6626
|
p.log.info(`Total collateral: $${parseFloat(marginSummary.totalCollateralUsed).toFixed(4)}`);
|
|
6210
6627
|
p.log.info(`Total notional: $${parseFloat(marginSummary.totalNtlPos).toFixed(4)}`);
|
|
6211
6628
|
p.log.info(`Unrealized PnL: $${parseFloat(marginSummary.totalRawPnlUsd).toFixed(4)}`);
|
|
6212
|
-
p.log.info(`
|
|
6629
|
+
p.log.info(`Cum rollover: $${parseFloat(marginSummary.totalCumRollover).toFixed(4)}`);
|
|
6630
|
+
p.log.info(`Max withdrawable: $${parseFloat(marginSummary.totalWithdrawable).toFixed(4)}`);
|
|
6213
6631
|
}
|
|
6214
6632
|
async function actionViewFills(client2) {
|
|
6215
6633
|
const limitRaw = await p.text({
|