@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/index.cjs
CHANGED
|
@@ -260,11 +260,20 @@ var CancelOrderType = /* @__PURE__ */ ((CancelOrderType2) => {
|
|
|
260
260
|
// src/config.ts
|
|
261
261
|
var DEFAULT_PIMLICO_URL = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161";
|
|
262
262
|
var DEFAULT_PIMLICO_URL_TESTNET = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=421614";
|
|
263
|
-
function
|
|
264
|
-
return
|
|
263
|
+
function hasPrivateKey(config) {
|
|
264
|
+
return typeof config.privateKey === "string" && config.privateKey.length > 0;
|
|
265
265
|
}
|
|
266
|
-
function
|
|
267
|
-
return
|
|
266
|
+
function resolveMode(config) {
|
|
267
|
+
if (config.mode) return config.mode;
|
|
268
|
+
if (config.privateKey) {
|
|
269
|
+
if (config.pimlicoUrl) return config.traderAddress ? "delegated-gasless" : "self-gasless";
|
|
270
|
+
return config.traderAddress ? "delegated-self" : "self-self";
|
|
271
|
+
}
|
|
272
|
+
if (config.safeAddress && config.delegateAddress && config.traderAddress) return "delegated-gasless";
|
|
273
|
+
if (config.safeAddress && config.traderAddress) return "self-gasless";
|
|
274
|
+
if (config.delegateAddress && config.traderAddress) return "delegated-self";
|
|
275
|
+
if (config.traderAddress) return "self-self";
|
|
276
|
+
throw new Error("Unable to infer client mode from config");
|
|
268
277
|
}
|
|
269
278
|
|
|
270
279
|
// src/errors.ts
|
|
@@ -2667,8 +2676,8 @@ var DelegatedSignerStrategy = class {
|
|
|
2667
2676
|
};
|
|
2668
2677
|
|
|
2669
2678
|
// src/signer/index.ts
|
|
2670
|
-
function createSigner(
|
|
2671
|
-
if (
|
|
2679
|
+
function createSigner(mode, traderAddress) {
|
|
2680
|
+
if (mode !== "self-self") {
|
|
2672
2681
|
return new DelegatedSignerStrategy(traderAddress);
|
|
2673
2682
|
}
|
|
2674
2683
|
return new SelfSignerStrategy(traderAddress);
|
|
@@ -2692,6 +2701,12 @@ var SelfSubmissionStrategy = class {
|
|
|
2692
2701
|
effectiveSender;
|
|
2693
2702
|
send;
|
|
2694
2703
|
constructor(config, testnet) {
|
|
2704
|
+
if (!config.privateKey) {
|
|
2705
|
+
throw new OstiumError(
|
|
2706
|
+
"privateKey is required for self-submission mode",
|
|
2707
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2695
2710
|
if (!config.rpcUrl) {
|
|
2696
2711
|
throw new OstiumError(
|
|
2697
2712
|
"rpcUrl is required for self-submission mode",
|
|
@@ -2757,6 +2772,12 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2757
2772
|
this.effectiveSender = safeAccount.address;
|
|
2758
2773
|
}
|
|
2759
2774
|
static async create(config, testnet) {
|
|
2775
|
+
if (!config.privateKey) {
|
|
2776
|
+
throw new OstiumError(
|
|
2777
|
+
"privateKey is required for gasless submission mode",
|
|
2778
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2760
2781
|
if (!config.pimlicoUrl) {
|
|
2761
2782
|
throw new OstiumError(
|
|
2762
2783
|
"pimlicoUrl is required for gasless mode",
|
|
@@ -2825,8 +2846,8 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2825
2846
|
};
|
|
2826
2847
|
|
|
2827
2848
|
// src/submitter/index.ts
|
|
2828
|
-
async function createSubmitter(config, testnet) {
|
|
2829
|
-
if (
|
|
2849
|
+
async function createSubmitter(config, testnet, mode) {
|
|
2850
|
+
if (mode === "self-gasless" || mode === "delegated-gasless") {
|
|
2830
2851
|
return GaslessSubmissionStrategy.create(config, testnet);
|
|
2831
2852
|
}
|
|
2832
2853
|
if (!config.rpcUrl) {
|
|
@@ -2923,6 +2944,17 @@ query GetTraderOpenTrades($trader: String!, $skip: Int!, $first: Int!) {
|
|
|
2923
2944
|
${PAIR_FULL}
|
|
2924
2945
|
}
|
|
2925
2946
|
}`;
|
|
2947
|
+
var GetAllOpenTradesQuery = `
|
|
2948
|
+
query GetAllOpenTrades($skip: Int!, $first: Int!) {
|
|
2949
|
+
trades(
|
|
2950
|
+
where: { isOpen: true }
|
|
2951
|
+
skip: $skip first: $first
|
|
2952
|
+
orderBy: timestamp orderDirection: desc
|
|
2953
|
+
) {
|
|
2954
|
+
${TRADE_CORE}
|
|
2955
|
+
${PAIR_FULL}
|
|
2956
|
+
}
|
|
2957
|
+
}`;
|
|
2926
2958
|
var ORDER_FIELDS = `
|
|
2927
2959
|
id tradeID limitID trader
|
|
2928
2960
|
pair { id from to group { id name } }
|
|
@@ -3002,9 +3034,9 @@ query GetTraderActiveLimits($trader: String!, $skip: Int!, $first: Int!) {
|
|
|
3002
3034
|
}`;
|
|
3003
3035
|
|
|
3004
3036
|
// src/data/internal/pagination.ts
|
|
3005
|
-
async function paginateAll(fetcher, batchSize = 1e3, max = Infinity) {
|
|
3037
|
+
async function paginateAll(fetcher, batchSize = 1e3, max = Infinity, initialSkip = 0) {
|
|
3006
3038
|
const results = [];
|
|
3007
|
-
let skip =
|
|
3039
|
+
let skip = initialSkip;
|
|
3008
3040
|
while (results.length < max) {
|
|
3009
3041
|
const remaining = max - results.length;
|
|
3010
3042
|
const fetchSize = Math.min(batchSize, remaining);
|
|
@@ -3226,10 +3258,11 @@ async function fetchLivePrices(builderApiUrl) {
|
|
|
3226
3258
|
}
|
|
3227
3259
|
return result;
|
|
3228
3260
|
}
|
|
3229
|
-
function rawTickToPublic(item) {
|
|
3261
|
+
function rawTickToPublic(item, pairId) {
|
|
3230
3262
|
const from = normalizePairName(item.from);
|
|
3231
3263
|
const to = normalizePairName(item.to);
|
|
3232
3264
|
return {
|
|
3265
|
+
pairId,
|
|
3233
3266
|
feedId: item.feed_id,
|
|
3234
3267
|
pair: `${from}-${to}`,
|
|
3235
3268
|
from,
|
|
@@ -3276,9 +3309,12 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3276
3309
|
snapshotHandlers = /* @__PURE__ */ new Set();
|
|
3277
3310
|
/** pairId (string) → raw "FROM-TO" name for the WS API. */
|
|
3278
3311
|
pairRawNameCache;
|
|
3279
|
-
|
|
3312
|
+
/** raw "FROM-TO" name → pairId (string) for mapping incoming ticks back to SDK ids. */
|
|
3313
|
+
rawNamePairIdCache;
|
|
3314
|
+
constructor(ws, pairRawNameCache, rawNamePairIdCache, initialSubscribeRawPairs) {
|
|
3280
3315
|
this.ws = ws;
|
|
3281
3316
|
this.pairRawNameCache = pairRawNameCache;
|
|
3317
|
+
this.rawNamePairIdCache = rawNamePairIdCache;
|
|
3282
3318
|
if (initialSubscribeRawPairs?.length) {
|
|
3283
3319
|
ws.once("open", () => {
|
|
3284
3320
|
ws.send(JSON.stringify({ type: "subscribe", pairs: initialSubscribeRawPairs }));
|
|
@@ -3292,10 +3328,13 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3292
3328
|
return;
|
|
3293
3329
|
}
|
|
3294
3330
|
if (msg.type === "snapshot" && Array.isArray(msg.data)) {
|
|
3295
|
-
const ticks = msg.data.map(
|
|
3331
|
+
const ticks = msg.data.map(
|
|
3332
|
+
(item) => rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`))
|
|
3333
|
+
);
|
|
3296
3334
|
for (const h of this.snapshotHandlers) h(ticks);
|
|
3297
3335
|
} else if (msg.type === "tick" && msg.data) {
|
|
3298
|
-
const
|
|
3336
|
+
const item = msg.data;
|
|
3337
|
+
const tick = rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`));
|
|
3299
3338
|
for (const h of this.tickHandlers) h(tick);
|
|
3300
3339
|
}
|
|
3301
3340
|
});
|
|
@@ -3321,7 +3360,11 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3321
3360
|
const ws = new WS__default.default(url, {
|
|
3322
3361
|
headers: { "User-Agent": "ostium-sdk", "Accept": "*/*" }
|
|
3323
3362
|
});
|
|
3324
|
-
|
|
3363
|
+
const rawNamePairIdCache = /* @__PURE__ */ new Map();
|
|
3364
|
+
for (const [pairId, rawName] of pairRawNameCache.entries()) {
|
|
3365
|
+
rawNamePairIdCache.set(rawName, pairId);
|
|
3366
|
+
}
|
|
3367
|
+
return new _OstiumPriceStream(ws, pairRawNameCache, rawNamePairIdCache, initial);
|
|
3325
3368
|
}
|
|
3326
3369
|
/**
|
|
3327
3370
|
* Register a callback that fires on every incoming price tick.
|
|
@@ -3397,6 +3440,37 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3397
3440
|
}
|
|
3398
3441
|
};
|
|
3399
3442
|
|
|
3443
|
+
// src/data/internal/aggregations.ts
|
|
3444
|
+
function aggregateMarginSummary(pairPositions) {
|
|
3445
|
+
let accountValue = 0;
|
|
3446
|
+
let totalCollateralUsed = 0;
|
|
3447
|
+
let totalNtlPos = 0;
|
|
3448
|
+
let totalRawPnlUsd = 0;
|
|
3449
|
+
let totalCumRollover = 0;
|
|
3450
|
+
let totalWithdrawable = 0;
|
|
3451
|
+
for (const { position } of pairPositions) {
|
|
3452
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3453
|
+
const ntl = parseFloat(position.ntl) || 0;
|
|
3454
|
+
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
3455
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3456
|
+
const withdrawable = parseFloat(position.maxWithdrawable) || 0;
|
|
3457
|
+
accountValue += collateral + pnl;
|
|
3458
|
+
totalCollateralUsed += collateral;
|
|
3459
|
+
totalNtlPos += ntl;
|
|
3460
|
+
totalRawPnlUsd += pnl;
|
|
3461
|
+
totalCumRollover += rollover;
|
|
3462
|
+
totalWithdrawable += withdrawable;
|
|
3463
|
+
}
|
|
3464
|
+
return {
|
|
3465
|
+
accountValue: accountValue.toString(),
|
|
3466
|
+
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
3467
|
+
totalNtlPos: totalNtlPos.toString(),
|
|
3468
|
+
totalRawPnlUsd: totalRawPnlUsd.toString(),
|
|
3469
|
+
totalCumRollover: totalCumRollover.toString(),
|
|
3470
|
+
totalWithdrawable: totalWithdrawable.toString()
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3400
3474
|
// src/data/internal/formatters.ts
|
|
3401
3475
|
var MIN_NOTIONAL = "5.0";
|
|
3402
3476
|
var MIN_NOTIONAL_NUM = 5;
|
|
@@ -3498,14 +3572,14 @@ function formatPosition(raw, price, pnl, maxLeverage) {
|
|
|
3498
3572
|
returnOnEquity: roe.toString(),
|
|
3499
3573
|
liquidationPx: pnl.liquidationPrice.toString(),
|
|
3500
3574
|
collateralUsed: collateral.toString(),
|
|
3501
|
-
cumRollover: pnl.rollover.toString(),
|
|
3575
|
+
cumRollover: (pnl.rollover * -1).toString(),
|
|
3502
3576
|
...raw.takeProfitPrice && raw.takeProfitPrice !== "0" ? { tpPx: formatTokens(raw.takeProfitPrice).toString() } : {},
|
|
3503
3577
|
...raw.stopLossPrice && raw.stopLossPrice !== "0" ? { slPx: formatTokens(raw.stopLossPrice).toString() } : {},
|
|
3504
3578
|
openTimestamp: parseInt(raw.timestamp || "0") * 1e3,
|
|
3505
3579
|
isDayTrade: raw.isDayTrade ?? false,
|
|
3506
|
-
maxLeverage: maxLeverage.toString()
|
|
3507
|
-
|
|
3508
|
-
|
|
3580
|
+
maxLeverage: maxLeverage.toString(),
|
|
3581
|
+
maxWithdrawable: maxWithdrawForPosition(collateral, leverage, maxLeverage).toString()
|
|
3582
|
+
}
|
|
3509
3583
|
};
|
|
3510
3584
|
}
|
|
3511
3585
|
function formatFill(raw) {
|
|
@@ -3582,6 +3656,131 @@ function formatOpenOrder(raw) {
|
|
|
3582
3656
|
timestamp: parseInt(raw.initiatedAt || "0") * 1e3
|
|
3583
3657
|
};
|
|
3584
3658
|
}
|
|
3659
|
+
|
|
3660
|
+
// src/data/internal/position-updates.ts
|
|
3661
|
+
function cloneResponse(response) {
|
|
3662
|
+
return {
|
|
3663
|
+
pairPositions: response.pairPositions.map((pairPos) => ({
|
|
3664
|
+
position: { ...pairPos.position }
|
|
3665
|
+
})),
|
|
3666
|
+
marginSummary: { ...response.marginSummary },
|
|
3667
|
+
time: response.time
|
|
3668
|
+
};
|
|
3669
|
+
}
|
|
3670
|
+
function applyTickToPosition(position, tick) {
|
|
3671
|
+
const size = parseFloat(position.szi) || 0;
|
|
3672
|
+
const entry = parseFloat(position.entryPx) || 0;
|
|
3673
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3674
|
+
const maxLeverage = parseFloat(position.maxLeverage) || 0;
|
|
3675
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3676
|
+
const isLong = position.side === "B";
|
|
3677
|
+
const ntl = size * tick.mid;
|
|
3678
|
+
const rawPnl = isLong ? (tick.mid - entry) * size : (entry - tick.mid) * size;
|
|
3679
|
+
const netPnl = rawPnl + rollover;
|
|
3680
|
+
const roe = collateral > 0 ? netPnl / collateral : 0;
|
|
3681
|
+
const currentLeverage = collateral > 0 ? ntl / collateral : 0;
|
|
3682
|
+
const maxWithdrawable = maxWithdrawForPosition(collateral, currentLeverage, maxLeverage);
|
|
3683
|
+
return {
|
|
3684
|
+
...position,
|
|
3685
|
+
ntl: ntl.toString(),
|
|
3686
|
+
unrealizedPnl: netPnl.toString(),
|
|
3687
|
+
returnOnEquity: roe.toString(),
|
|
3688
|
+
maxWithdrawable: maxWithdrawable.toString()
|
|
3689
|
+
};
|
|
3690
|
+
}
|
|
3691
|
+
function updateResponseWithTick(current, tick, timestampMs) {
|
|
3692
|
+
if (!tick.pairId) return current;
|
|
3693
|
+
let changed = false;
|
|
3694
|
+
const pairPositions = current.pairPositions.map((pairPos) => {
|
|
3695
|
+
if (String(pairPos.position.pairId) !== String(tick.pairId)) {
|
|
3696
|
+
return pairPos;
|
|
3697
|
+
}
|
|
3698
|
+
changed = true;
|
|
3699
|
+
return {
|
|
3700
|
+
...pairPos,
|
|
3701
|
+
position: applyTickToPosition(pairPos.position, tick)
|
|
3702
|
+
};
|
|
3703
|
+
});
|
|
3704
|
+
if (!changed) return current;
|
|
3705
|
+
return {
|
|
3706
|
+
pairPositions,
|
|
3707
|
+
marginSummary: aggregateMarginSummary(pairPositions),
|
|
3708
|
+
time: timestampMs
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3711
|
+
var OstiumPositionUpdatesStream = class {
|
|
3712
|
+
priceStream;
|
|
3713
|
+
updateHandlers = /* @__PURE__ */ new Set();
|
|
3714
|
+
current;
|
|
3715
|
+
trackedPairIds;
|
|
3716
|
+
constructor(initial, trackedPairIds, priceStream) {
|
|
3717
|
+
this.current = cloneResponse(initial);
|
|
3718
|
+
this.priceStream = priceStream;
|
|
3719
|
+
this.trackedPairIds = new Set(trackedPairIds);
|
|
3720
|
+
if (!priceStream) return;
|
|
3721
|
+
priceStream.onSnapshot((ticks) => {
|
|
3722
|
+
this.ingestSnapshot(ticks);
|
|
3723
|
+
});
|
|
3724
|
+
priceStream.onTick((tick) => {
|
|
3725
|
+
this.ingestTick(tick);
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
onUpdate(handler) {
|
|
3729
|
+
this.updateHandlers.add(handler);
|
|
3730
|
+
return () => {
|
|
3731
|
+
this.updateHandlers.delete(handler);
|
|
3732
|
+
};
|
|
3733
|
+
}
|
|
3734
|
+
onOpen(handler) {
|
|
3735
|
+
this.priceStream?.onOpen(handler);
|
|
3736
|
+
return this;
|
|
3737
|
+
}
|
|
3738
|
+
onError(handler) {
|
|
3739
|
+
this.priceStream?.onError(handler);
|
|
3740
|
+
return this;
|
|
3741
|
+
}
|
|
3742
|
+
onClose(handler) {
|
|
3743
|
+
this.priceStream?.onClose(handler);
|
|
3744
|
+
return this;
|
|
3745
|
+
}
|
|
3746
|
+
getCurrent() {
|
|
3747
|
+
return cloneResponse(this.current);
|
|
3748
|
+
}
|
|
3749
|
+
/**
|
|
3750
|
+
* Apply a single externally sourced price tick to the tracked positions.
|
|
3751
|
+
*
|
|
3752
|
+
* Use this when your app already has its own websocket connection and you want
|
|
3753
|
+
* the SDK to handle only the position recalculation logic.
|
|
3754
|
+
*/
|
|
3755
|
+
ingestTick(tick) {
|
|
3756
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) return;
|
|
3757
|
+
const next = updateResponseWithTick(this.current, tick, tick.timestampSeconds * 1e3);
|
|
3758
|
+
if (next !== this.current) {
|
|
3759
|
+
this.current = next;
|
|
3760
|
+
this.emit(next);
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
/**
|
|
3764
|
+
* Apply a batch of externally sourced ticks, such as a websocket snapshot.
|
|
3765
|
+
*/
|
|
3766
|
+
ingestSnapshot(ticks) {
|
|
3767
|
+
let next = this.current;
|
|
3768
|
+
for (const tick of ticks) {
|
|
3769
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) continue;
|
|
3770
|
+
next = updateResponseWithTick(next, tick, tick.timestampSeconds * 1e3);
|
|
3771
|
+
}
|
|
3772
|
+
if (next !== this.current) {
|
|
3773
|
+
this.current = next;
|
|
3774
|
+
this.emit(next);
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
close() {
|
|
3778
|
+
this.priceStream?.close();
|
|
3779
|
+
}
|
|
3780
|
+
emit(positions) {
|
|
3781
|
+
for (const handler of this.updateHandlers) handler(cloneResponse(positions));
|
|
3782
|
+
}
|
|
3783
|
+
};
|
|
3585
3784
|
var EMPTY_RESULT = { long: [], short: [] };
|
|
3586
3785
|
var ORDERBOOK_MAX_LEVELS = 20;
|
|
3587
3786
|
function generateLogSpacedNotionals(levels, capacity) {
|
|
@@ -3766,31 +3965,40 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
3766
3965
|
* PnL fields are zeroed.
|
|
3767
3966
|
*/
|
|
3768
3967
|
async getOpenPositions(params) {
|
|
3769
|
-
const { user, blockNumber } = params;
|
|
3968
|
+
const { user, blockNumber, limit = Infinity, skip: initialSkip = 0 } = params;
|
|
3969
|
+
const isGlobal = user === "ALL";
|
|
3770
3970
|
const [trades, prices] = await Promise.all([
|
|
3771
|
-
paginateAll(
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3971
|
+
paginateAll(
|
|
3972
|
+
async (skip, first) => {
|
|
3973
|
+
if (isGlobal) {
|
|
3974
|
+
const data2 = await this.query(
|
|
3975
|
+
GetAllOpenTradesQuery,
|
|
3976
|
+
{ skip, first }
|
|
3977
|
+
);
|
|
3978
|
+
return data2.trades;
|
|
3979
|
+
}
|
|
3980
|
+
const data = await this.query(
|
|
3981
|
+
GetTraderOpenTradesQuery,
|
|
3982
|
+
{ trader: user.toLowerCase(), skip, first }
|
|
3983
|
+
);
|
|
3984
|
+
return data.trades;
|
|
3985
|
+
},
|
|
3986
|
+
1e3,
|
|
3987
|
+
limit,
|
|
3988
|
+
initialSkip
|
|
3989
|
+
),
|
|
3778
3990
|
this.fetchLivePricesSafe()
|
|
3779
3991
|
]);
|
|
3780
|
-
let totalWithdrawable = 0;
|
|
3781
3992
|
const pairPositions = trades.map((trade) => {
|
|
3782
3993
|
const price = prices[`${trade.pair.from}/${trade.pair.to}`];
|
|
3783
3994
|
const pnl = price && blockNumber ? getTradePnL(trade, price, blockNumber) : EMPTY_PNL;
|
|
3784
3995
|
const maxLev = trade.isDayTrade ? formatLeverage(trade.pair.overnightMaxLeverage) : pairMaxLeverage(trade.pair);
|
|
3785
|
-
|
|
3786
|
-
totalWithdrawable += parseFloat(pairPos.maxWithdrawable);
|
|
3787
|
-
return pairPos;
|
|
3996
|
+
return formatPosition(trade, price, pnl, maxLev);
|
|
3788
3997
|
});
|
|
3789
3998
|
const marginSummary = aggregateMarginSummary(pairPositions);
|
|
3790
3999
|
return {
|
|
3791
4000
|
pairPositions,
|
|
3792
4001
|
marginSummary,
|
|
3793
|
-
withdrawable: totalWithdrawable.toString(),
|
|
3794
4002
|
time: Date.now()
|
|
3795
4003
|
};
|
|
3796
4004
|
}
|
|
@@ -4041,6 +4249,24 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4041
4249
|
});
|
|
4042
4250
|
return OstiumPriceStream.connect(this.builderApiUrl, rawNames, cache);
|
|
4043
4251
|
}
|
|
4252
|
+
/**
|
|
4253
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
4254
|
+
*
|
|
4255
|
+
* The SDK subscribes only to the unique pairs present in `initial.pairPositions`,
|
|
4256
|
+
* recalculates price-sensitive fields for affected positions on each tick, and
|
|
4257
|
+
* emits the full updated response.
|
|
4258
|
+
*
|
|
4259
|
+
* Pass an existing `priceStream` to reuse a websocket connection your app has
|
|
4260
|
+
* already opened. Omit it to let the SDK open and manage a dedicated
|
|
4261
|
+
* connection for the tracked pair ids.
|
|
4262
|
+
*/
|
|
4263
|
+
streamPositionUpdates(initial, priceStream) {
|
|
4264
|
+
const pairIds = [...new Set(initial.pairPositions.map(({ position }) => String(position.pairId)))];
|
|
4265
|
+
if (pairIds.length === 0) {
|
|
4266
|
+
return new OstiumPositionUpdatesStream(initial, []);
|
|
4267
|
+
}
|
|
4268
|
+
return new OstiumPositionUpdatesStream(initial, pairIds, priceStream ?? this.streamPrices(pairIds));
|
|
4269
|
+
}
|
|
4044
4270
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4045
4271
|
// Internal — fetchers, cache, query wrapper
|
|
4046
4272
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -4099,43 +4325,28 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4099
4325
|
}
|
|
4100
4326
|
}
|
|
4101
4327
|
};
|
|
4102
|
-
function aggregateMarginSummary(pairPositions) {
|
|
4103
|
-
let accountValue = 0;
|
|
4104
|
-
let totalCollateralUsed = 0;
|
|
4105
|
-
let totalNtlPos = 0;
|
|
4106
|
-
let totalRawPnlUsd = 0;
|
|
4107
|
-
for (const { position } of pairPositions) {
|
|
4108
|
-
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
4109
|
-
const ntl = parseFloat(position.ntl) || 0;
|
|
4110
|
-
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
4111
|
-
accountValue += collateral + pnl;
|
|
4112
|
-
totalCollateralUsed += collateral;
|
|
4113
|
-
totalNtlPos += ntl;
|
|
4114
|
-
totalRawPnlUsd += pnl;
|
|
4115
|
-
}
|
|
4116
|
-
return {
|
|
4117
|
-
accountValue: accountValue.toString(),
|
|
4118
|
-
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
4119
|
-
totalNtlPos: totalNtlPos.toString(),
|
|
4120
|
-
totalRawPnlUsd: totalRawPnlUsd.toString()
|
|
4121
|
-
};
|
|
4122
|
-
}
|
|
4123
4328
|
|
|
4124
4329
|
// src/client.ts
|
|
4125
4330
|
var HEX64_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
4126
4331
|
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
4127
4332
|
function validateConfig(config) {
|
|
4128
|
-
if (!HEX64_RE.test(config.privateKey)) {
|
|
4333
|
+
if (config.privateKey && !HEX64_RE.test(config.privateKey)) {
|
|
4129
4334
|
throw new OstiumError(
|
|
4130
4335
|
"privateKey must be a 64-character hex string prefixed with 0x",
|
|
4131
4336
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4132
4337
|
);
|
|
4133
4338
|
}
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4339
|
+
for (const [label, value] of [
|
|
4340
|
+
["traderAddress", config.traderAddress],
|
|
4341
|
+
["delegateAddress", config.delegateAddress],
|
|
4342
|
+
["safeAddress", config.safeAddress]
|
|
4343
|
+
]) {
|
|
4344
|
+
if (value && !ADDRESS_RE.test(value)) {
|
|
4345
|
+
throw new OstiumError(
|
|
4346
|
+
`${label} must be a valid 42-character Ethereum address (0x + 40 hex chars)`,
|
|
4347
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4348
|
+
);
|
|
4349
|
+
}
|
|
4139
4350
|
}
|
|
4140
4351
|
if (config.slippageBps !== void 0 && (config.slippageBps < 0 || config.slippageBps > 500)) {
|
|
4141
4352
|
throw new OstiumError(
|
|
@@ -4149,10 +4360,45 @@ function validateConfig(config) {
|
|
|
4149
4360
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4150
4361
|
);
|
|
4151
4362
|
}
|
|
4363
|
+
let mode;
|
|
4364
|
+
try {
|
|
4365
|
+
mode = resolveMode(config);
|
|
4366
|
+
} catch {
|
|
4367
|
+
throw new OstiumError(
|
|
4368
|
+
"Could not infer client mode from config. Provide the required addresses for the selected factory.",
|
|
4369
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4370
|
+
);
|
|
4371
|
+
}
|
|
4372
|
+
if (mode === "self-self" && !config.privateKey && !config.traderAddress) {
|
|
4373
|
+
throw new OstiumError(
|
|
4374
|
+
"Self + Self mode requires traderPrivateKey for submission or traderAddress for build-only usage.",
|
|
4375
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4376
|
+
);
|
|
4377
|
+
}
|
|
4378
|
+
if (mode === "self-gasless" && !config.privateKey && (!config.traderAddress || !config.safeAddress)) {
|
|
4379
|
+
throw new OstiumError(
|
|
4380
|
+
"Self + Gasless build-only mode requires traderAddress and safeAddress.",
|
|
4381
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
if (mode === "delegated-self" && (!config.traderAddress || !config.privateKey && !config.delegateAddress)) {
|
|
4385
|
+
throw new OstiumError(
|
|
4386
|
+
"Delegated + Self mode requires traderAddress and either delegatePrivateKey or delegateAddress.",
|
|
4387
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4388
|
+
);
|
|
4389
|
+
}
|
|
4390
|
+
if (mode === "delegated-gasless" && (!config.traderAddress || !config.privateKey && (!config.delegateAddress || !config.safeAddress))) {
|
|
4391
|
+
throw new OstiumError(
|
|
4392
|
+
"Delegated + Gasless mode requires traderAddress and either delegatePrivateKey or both delegateAddress and safeAddress.",
|
|
4393
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4394
|
+
);
|
|
4395
|
+
}
|
|
4152
4396
|
}
|
|
4153
4397
|
var OstiumClient = class _OstiumClient {
|
|
4154
4398
|
signer;
|
|
4155
4399
|
submitter;
|
|
4400
|
+
traderAddress;
|
|
4401
|
+
effectiveSender;
|
|
4156
4402
|
contracts;
|
|
4157
4403
|
publicClient;
|
|
4158
4404
|
defaultSlippageBps;
|
|
@@ -4167,6 +4413,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4167
4413
|
constructor(internals) {
|
|
4168
4414
|
this.signer = internals.signer;
|
|
4169
4415
|
this.submitter = internals.submitter;
|
|
4416
|
+
this.traderAddress = internals.traderAddress;
|
|
4417
|
+
this.effectiveSender = internals.effectiveSender;
|
|
4170
4418
|
this.contracts = internals.contracts;
|
|
4171
4419
|
this.publicClient = internals.publicClient;
|
|
4172
4420
|
this.defaultSlippageBps = internals.defaultSlippageBps;
|
|
@@ -4191,17 +4439,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4191
4439
|
* });
|
|
4192
4440
|
* await client.openTrade({ ... });
|
|
4193
4441
|
* ```
|
|
4194
|
-
|
|
4442
|
+
*/
|
|
4195
4443
|
static async createSelfAndSelf(params) {
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
rpcUrl: params.rpcUrl,
|
|
4444
|
+
const base = {
|
|
4445
|
+
mode: "self-self",
|
|
4199
4446
|
testnet: params.testnet,
|
|
4200
4447
|
slippageBps: params.slippageBps,
|
|
4201
4448
|
builder: params.builder,
|
|
4202
4449
|
subgraphUrl: params.subgraphUrl,
|
|
4203
|
-
builderApiUrl: params.builderApiUrl
|
|
4204
|
-
|
|
4450
|
+
builderApiUrl: params.builderApiUrl,
|
|
4451
|
+
rpcUrl: params.rpcUrl
|
|
4452
|
+
};
|
|
4453
|
+
return _OstiumClient._fromConfig(
|
|
4454
|
+
"traderPrivateKey" in params ? { ...base, privateKey: params.traderPrivateKey } : { ...base, traderAddress: params.traderAddress }
|
|
4455
|
+
);
|
|
4205
4456
|
}
|
|
4206
4457
|
/**
|
|
4207
4458
|
* **Self + Gasless** — your EOA owns everything; a Safe relays trades for free.
|
|
@@ -4225,17 +4476,27 @@ var OstiumClient = class _OstiumClient {
|
|
|
4225
4476
|
*/
|
|
4226
4477
|
static async createSelfAndGasless(params) {
|
|
4227
4478
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4479
|
+
const base = {
|
|
4480
|
+
mode: "self-gasless",
|
|
4231
4481
|
rpcUrl: params.rpcUrl,
|
|
4232
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4233
4482
|
testnet: params.testnet,
|
|
4234
4483
|
slippageBps: params.slippageBps,
|
|
4235
4484
|
builder: params.builder,
|
|
4236
4485
|
subgraphUrl: params.subgraphUrl,
|
|
4237
4486
|
builderApiUrl: params.builderApiUrl
|
|
4238
|
-
}
|
|
4487
|
+
};
|
|
4488
|
+
return _OstiumClient._fromConfig(
|
|
4489
|
+
"traderPrivateKey" in params ? {
|
|
4490
|
+
...base,
|
|
4491
|
+
privateKey: params.traderPrivateKey,
|
|
4492
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4493
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4494
|
+
} : {
|
|
4495
|
+
...base,
|
|
4496
|
+
traderAddress: params.traderAddress,
|
|
4497
|
+
safeAddress: params.safeAddress
|
|
4498
|
+
}
|
|
4499
|
+
);
|
|
4239
4500
|
}
|
|
4240
4501
|
/**
|
|
4241
4502
|
* **Delegated + Self** — a delegate EOA signs and pays gas on behalf of a trader address.
|
|
@@ -4255,8 +4516,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4255
4516
|
* ```
|
|
4256
4517
|
*/
|
|
4257
4518
|
static async createDelegatedAndSelf(params) {
|
|
4258
|
-
|
|
4259
|
-
|
|
4519
|
+
const base = {
|
|
4520
|
+
mode: "delegated-self",
|
|
4260
4521
|
traderAddress: params.traderAddress,
|
|
4261
4522
|
rpcUrl: params.rpcUrl,
|
|
4262
4523
|
testnet: params.testnet,
|
|
@@ -4264,7 +4525,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4264
4525
|
builder: params.builder,
|
|
4265
4526
|
subgraphUrl: params.subgraphUrl,
|
|
4266
4527
|
builderApiUrl: params.builderApiUrl
|
|
4267
|
-
}
|
|
4528
|
+
};
|
|
4529
|
+
return _OstiumClient._fromConfig(
|
|
4530
|
+
"delegatePrivateKey" in params ? { ...base, privateKey: params.delegatePrivateKey } : { ...base, delegateAddress: params.delegateAddress }
|
|
4531
|
+
);
|
|
4268
4532
|
}
|
|
4269
4533
|
/**
|
|
4270
4534
|
* **Delegated + Gasless** — a Safe derived from the delegate key submits sponsored
|
|
@@ -4285,18 +4549,28 @@ var OstiumClient = class _OstiumClient {
|
|
|
4285
4549
|
*/
|
|
4286
4550
|
static async createDelegatedAndGasless(params) {
|
|
4287
4551
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4288
|
-
|
|
4289
|
-
|
|
4552
|
+
const base = {
|
|
4553
|
+
mode: "delegated-gasless",
|
|
4290
4554
|
traderAddress: params.traderAddress,
|
|
4291
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4292
4555
|
rpcUrl: params.rpcUrl,
|
|
4293
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4294
4556
|
testnet: params.testnet,
|
|
4295
4557
|
slippageBps: params.slippageBps,
|
|
4296
4558
|
builder: params.builder,
|
|
4297
4559
|
subgraphUrl: params.subgraphUrl,
|
|
4298
4560
|
builderApiUrl: params.builderApiUrl
|
|
4299
|
-
}
|
|
4561
|
+
};
|
|
4562
|
+
return _OstiumClient._fromConfig(
|
|
4563
|
+
"delegatePrivateKey" in params ? {
|
|
4564
|
+
...base,
|
|
4565
|
+
privateKey: params.delegatePrivateKey,
|
|
4566
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4567
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4568
|
+
} : {
|
|
4569
|
+
...base,
|
|
4570
|
+
delegateAddress: params.delegateAddress,
|
|
4571
|
+
safeAddress: params.safeAddress
|
|
4572
|
+
}
|
|
4573
|
+
);
|
|
4300
4574
|
}
|
|
4301
4575
|
/**
|
|
4302
4576
|
* **Read-only client** — no signer, no submitter, no privateKey required.
|
|
@@ -4341,6 +4615,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4341
4615
|
/** Internal factory shared by all public constructors. */
|
|
4342
4616
|
static async _fromConfig(config) {
|
|
4343
4617
|
validateConfig(config);
|
|
4618
|
+
const mode = resolveMode(config);
|
|
4344
4619
|
const testnet = config.testnet ?? false;
|
|
4345
4620
|
const networkConfig = getNetworkConfig(testnet);
|
|
4346
4621
|
const chain = testnet ? chains.arbitrumSepolia : chains.arbitrum;
|
|
@@ -4350,18 +4625,29 @@ var OstiumClient = class _OstiumClient {
|
|
|
4350
4625
|
tradingStorage: networkConfig.contracts.tradingStorage,
|
|
4351
4626
|
usdc: networkConfig.contracts.usdc
|
|
4352
4627
|
};
|
|
4353
|
-
const selfGasless =
|
|
4354
|
-
const submitter = await createSubmitter(config, testnet);
|
|
4628
|
+
const selfGasless = mode === "self-gasless";
|
|
4355
4629
|
let traderAddress;
|
|
4356
|
-
if (
|
|
4630
|
+
if (mode === "self-self" || mode === "self-gasless") {
|
|
4631
|
+
traderAddress = config.traderAddress ?? accounts.privateKeyToAccount(config.privateKey).address;
|
|
4632
|
+
} else {
|
|
4357
4633
|
traderAddress = config.traderAddress;
|
|
4634
|
+
}
|
|
4635
|
+
const signer = createSigner(mode, traderAddress);
|
|
4636
|
+
let submitter;
|
|
4637
|
+
let effectiveSender;
|
|
4638
|
+
if (hasPrivateKey(config)) {
|
|
4639
|
+
submitter = await createSubmitter(config, testnet, mode);
|
|
4640
|
+
effectiveSender = submitter.effectiveSender;
|
|
4641
|
+
} else if (mode === "self-self") {
|
|
4642
|
+
effectiveSender = traderAddress;
|
|
4643
|
+
} else if (mode === "delegated-self") {
|
|
4644
|
+
effectiveSender = config.delegateAddress;
|
|
4358
4645
|
} else {
|
|
4359
|
-
|
|
4646
|
+
effectiveSender = config.safeAddress;
|
|
4360
4647
|
}
|
|
4361
|
-
const signer = createSigner(config, traderAddress, selfGasless);
|
|
4362
4648
|
const publicRpc = testnet ? "https://sepolia-rollup.arbitrum.io/rpc" : "https://arb1.arbitrum.io/rpc";
|
|
4363
|
-
const rpcUrl = config.rpcUrl ??
|
|
4364
|
-
if (
|
|
4649
|
+
const rpcUrl = config.rpcUrl ?? publicRpc;
|
|
4650
|
+
if (hasPrivateKey(config) && mode !== "self-gasless" && mode !== "delegated-gasless" && !config.rpcUrl) {
|
|
4365
4651
|
throw new OstiumError(
|
|
4366
4652
|
"rpcUrl is required for self-submission mode",
|
|
4367
4653
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
@@ -4369,7 +4655,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4369
4655
|
}
|
|
4370
4656
|
const publicClient = viem.createPublicClient({ chain, transport: viem.http(rpcUrl) });
|
|
4371
4657
|
let eoaSubmit;
|
|
4372
|
-
if (selfGasless) {
|
|
4658
|
+
if (selfGasless && hasPrivateKey(config)) {
|
|
4373
4659
|
const eoaAccount = accounts.privateKeyToAccount(config.privateKey);
|
|
4374
4660
|
const eoaRpcUrl = config.rpcUrl ?? publicRpc;
|
|
4375
4661
|
const eoaWalletClient = viem.createWalletClient({ account: eoaAccount, chain, transport: viem.http(eoaRpcUrl) });
|
|
@@ -4391,11 +4677,13 @@ var OstiumClient = class _OstiumClient {
|
|
|
4391
4677
|
return new _OstiumClient({
|
|
4392
4678
|
signer,
|
|
4393
4679
|
submitter,
|
|
4680
|
+
traderAddress,
|
|
4681
|
+
effectiveSender,
|
|
4394
4682
|
contracts,
|
|
4395
4683
|
publicClient,
|
|
4396
4684
|
defaultSlippageBps,
|
|
4397
|
-
delegated:
|
|
4398
|
-
gasless:
|
|
4685
|
+
delegated: mode === "delegated-self" || mode === "delegated-gasless",
|
|
4686
|
+
gasless: mode === "self-gasless" || mode === "delegated-gasless",
|
|
4399
4687
|
selfGasless,
|
|
4400
4688
|
eoaSubmit,
|
|
4401
4689
|
builderAddress: config.builder?.address,
|
|
@@ -4413,17 +4701,25 @@ var OstiumClient = class _OstiumClient {
|
|
|
4413
4701
|
* - Self + Gasless: EOA derived from privateKey (NOT the Safe — USDC lives here).
|
|
4414
4702
|
*/
|
|
4415
4703
|
getTraderAddress() {
|
|
4416
|
-
if (!this.
|
|
4704
|
+
if (!this.traderAddress) {
|
|
4417
4705
|
throw new OstiumError(
|
|
4418
4706
|
"No connected trader \u2014 pass `user` explicitly or use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4419
4707
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4420
4708
|
);
|
|
4421
4709
|
}
|
|
4422
|
-
return this.
|
|
4710
|
+
return this.traderAddress;
|
|
4423
4711
|
}
|
|
4424
|
-
/** True when the client
|
|
4712
|
+
/** True when the client cannot submit transactions directly. */
|
|
4425
4713
|
isReadOnly() {
|
|
4426
|
-
return !this.
|
|
4714
|
+
return !this.submitter;
|
|
4715
|
+
}
|
|
4716
|
+
/** True when the client can build mode-correct unsigned transaction requests. */
|
|
4717
|
+
canBuildTransactions() {
|
|
4718
|
+
return !!this.signer && !!this.effectiveSender;
|
|
4719
|
+
}
|
|
4720
|
+
/** True when the client has the credentials needed for SDK-managed submission. */
|
|
4721
|
+
canSubmitTransactions() {
|
|
4722
|
+
return !!this.submitter;
|
|
4427
4723
|
}
|
|
4428
4724
|
/**
|
|
4429
4725
|
* Returns the Safe smart-account address used for gasless submission.
|
|
@@ -4435,7 +4731,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4435
4731
|
* - Self + Self / Delegated + Self: returns undefined.
|
|
4436
4732
|
*/
|
|
4437
4733
|
getSmartAccountAddress() {
|
|
4438
|
-
return this.gasless
|
|
4734
|
+
return this.gasless ? this.effectiveSender : void 0;
|
|
4439
4735
|
}
|
|
4440
4736
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4441
4737
|
// Self + Gasless setup
|
|
@@ -4459,15 +4755,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4459
4755
|
* ```
|
|
4460
4756
|
*/
|
|
4461
4757
|
async setupGaslessDelegation() {
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
);
|
|
4467
|
-
}
|
|
4468
|
-
const safeAddress = this.submitter.effectiveSender;
|
|
4469
|
-
const encoded = encodeSetDelegate(safeAddress, this.contracts.trading);
|
|
4470
|
-
return this.submitDirectFromEoa(encoded);
|
|
4758
|
+
return this.submitDirectEoa(this.getSetupGaslessDelegationEncoded());
|
|
4759
|
+
}
|
|
4760
|
+
getSetupGaslessDelegationTx() {
|
|
4761
|
+
return this.buildDirectEoaTx(this.getSetupGaslessDelegationEncoded());
|
|
4471
4762
|
}
|
|
4472
4763
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4473
4764
|
// USDC helpers
|
|
@@ -4529,18 +4820,14 @@ var OstiumClient = class _OstiumClient {
|
|
|
4529
4820
|
* @param amount USD amount as decimal string (e.g. "1000"), or "max" for MaxUint256.
|
|
4530
4821
|
*/
|
|
4531
4822
|
async approveUsdc(amount) {
|
|
4532
|
-
|
|
4533
|
-
throw new OstiumError(
|
|
4534
|
-
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
4535
|
-
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
4536
|
-
);
|
|
4537
|
-
}
|
|
4538
|
-
const rawAmount = amount === "max" ? viem.maxUint256 : parseUsdc(amount);
|
|
4539
|
-
const encoded = encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
4823
|
+
const encoded = this.getApproveUsdcEncoded(amount);
|
|
4540
4824
|
if (this.selfGasless) {
|
|
4541
|
-
return this.
|
|
4825
|
+
return this.submitDirectEoa(encoded);
|
|
4542
4826
|
}
|
|
4543
|
-
return this.
|
|
4827
|
+
return this.submitPrepared(encoded);
|
|
4828
|
+
}
|
|
4829
|
+
getApproveUsdcTx(amount) {
|
|
4830
|
+
return this.buildDirectEoaTx(this.getApproveUsdcEncoded(amount));
|
|
4544
4831
|
}
|
|
4545
4832
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4546
4833
|
// Delegation helpers
|
|
@@ -4554,16 +4841,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4554
4841
|
* update the trader's delegation on their behalf.
|
|
4555
4842
|
*/
|
|
4556
4843
|
async setDelegate(delegateAddress) {
|
|
4557
|
-
|
|
4558
|
-
|
|
4844
|
+
return this.submitPrepared(this.getSetDelegateEncoded(delegateAddress));
|
|
4845
|
+
}
|
|
4846
|
+
getSetDelegateTx(delegateAddress) {
|
|
4847
|
+
return this.buildPreparedTx(this.getSetDelegateEncoded(delegateAddress));
|
|
4559
4848
|
}
|
|
4560
4849
|
/**
|
|
4561
4850
|
* Remove the current delegate on the trading contract.
|
|
4562
4851
|
* Follows the same delegation-wrapping rules as setDelegate().
|
|
4563
4852
|
*/
|
|
4564
4853
|
async removeDelegate() {
|
|
4565
|
-
|
|
4566
|
-
|
|
4854
|
+
return this.submitPrepared(this.getRemoveDelegateEncoded());
|
|
4855
|
+
}
|
|
4856
|
+
getRemoveDelegateTx() {
|
|
4857
|
+
return this.buildPreparedTx(this.getRemoveDelegateEncoded());
|
|
4567
4858
|
}
|
|
4568
4859
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4569
4860
|
// Core trading methods
|
|
@@ -4576,6 +4867,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4576
4867
|
* the current allowance.
|
|
4577
4868
|
*/
|
|
4578
4869
|
async openTrade(params) {
|
|
4870
|
+
return this.submitPrepared(this.getOpenTradeEncoded(params));
|
|
4871
|
+
}
|
|
4872
|
+
getOpenTradeTx(params) {
|
|
4873
|
+
return this.buildPreparedTx(this.getOpenTradeEncoded(params));
|
|
4874
|
+
}
|
|
4875
|
+
getOpenTradeEncoded(params) {
|
|
4579
4876
|
const collateralNum = parseFloat(params.collateral);
|
|
4580
4877
|
if (collateralNum > MAX_COLLATERAL_USD) {
|
|
4581
4878
|
throw new OstiumError(
|
|
@@ -4584,7 +4881,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4584
4881
|
);
|
|
4585
4882
|
}
|
|
4586
4883
|
const apiOpen = {
|
|
4587
|
-
a: params.
|
|
4884
|
+
a: Number(params.pairId),
|
|
4588
4885
|
b: params.buy,
|
|
4589
4886
|
p: params.price,
|
|
4590
4887
|
s: params.collateral,
|
|
@@ -4607,8 +4904,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4607
4904
|
}
|
|
4608
4905
|
const builderFee = buildBuilderFee(apiBuilder);
|
|
4609
4906
|
const slippageP = params.type === "market" /* Market */ ? BigInt(this.resolveSlippage(params.slippage)) : 0n;
|
|
4610
|
-
|
|
4611
|
-
return this.execute(encoded);
|
|
4907
|
+
return encodeOpenTrade(trade, builderFee, orderType, slippageP, this.contracts.trading);
|
|
4612
4908
|
}
|
|
4613
4909
|
/**
|
|
4614
4910
|
* Close an open position (full or partial).
|
|
@@ -4623,6 +4919,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4623
4919
|
* ```
|
|
4624
4920
|
*/
|
|
4625
4921
|
async closeTrade(params) {
|
|
4922
|
+
return this.submitPrepared(this.getCloseTradeEncoded(params));
|
|
4923
|
+
}
|
|
4924
|
+
getCloseTradeTx(params) {
|
|
4925
|
+
return this.buildPreparedTx(this.getCloseTradeEncoded(params));
|
|
4926
|
+
}
|
|
4927
|
+
getCloseTradeEncoded(params) {
|
|
4626
4928
|
try {
|
|
4627
4929
|
validateCloseParams({
|
|
4628
4930
|
a: Number(params.pairId),
|
|
@@ -4642,7 +4944,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4642
4944
|
const closePercentage = BigInt(Math.round(params.closePercent * 100));
|
|
4643
4945
|
const marketPrice = parsePrice(params.price);
|
|
4644
4946
|
const slippageP = BigInt(this.resolveSlippage(params.slippage));
|
|
4645
|
-
|
|
4947
|
+
return encodeCloseTradeMarket(
|
|
4646
4948
|
pairIndex,
|
|
4647
4949
|
index,
|
|
4648
4950
|
closePercentage,
|
|
@@ -4650,7 +4952,6 @@ var OstiumClient = class _OstiumClient {
|
|
|
4650
4952
|
slippageP,
|
|
4651
4953
|
this.contracts.trading
|
|
4652
4954
|
);
|
|
4653
|
-
return this.execute(encoded);
|
|
4654
4955
|
}
|
|
4655
4956
|
/**
|
|
4656
4957
|
* Cancel a pending order.
|
|
@@ -4673,6 +4974,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4673
4974
|
* ```
|
|
4674
4975
|
*/
|
|
4675
4976
|
async cancelOrder(params) {
|
|
4977
|
+
return this.submitPrepared(this.getCancelOrderEncoded(params));
|
|
4978
|
+
}
|
|
4979
|
+
getCancelOrderTx(params) {
|
|
4980
|
+
return this.buildPreparedTx(this.getCancelOrderEncoded(params));
|
|
4981
|
+
}
|
|
4982
|
+
getCancelOrderEncoded(params) {
|
|
4676
4983
|
let encoded;
|
|
4677
4984
|
if (params.type === "limit" /* Limit */) {
|
|
4678
4985
|
const pairIdNum = Number(params.pairId);
|
|
@@ -4709,7 +5016,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4709
5016
|
}
|
|
4710
5017
|
encoded = encodeOpenTradeMarketTimeout(BigInt(params.orderId), this.contracts.trading);
|
|
4711
5018
|
}
|
|
4712
|
-
return
|
|
5019
|
+
return encoded;
|
|
4713
5020
|
}
|
|
4714
5021
|
/**
|
|
4715
5022
|
* Modify an open trade or a pending limit order.
|
|
@@ -4734,6 +5041,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4734
5041
|
* - Both `takeProfit` and `stopLoss` without `price` → throws (send two calls).
|
|
4735
5042
|
*/
|
|
4736
5043
|
async modifyOrder(params) {
|
|
5044
|
+
return this.submitPrepared(this.getModifyOrderEncoded(params));
|
|
5045
|
+
}
|
|
5046
|
+
getModifyOrderTx(params) {
|
|
5047
|
+
return this.buildPreparedTx(this.getModifyOrderEncoded(params));
|
|
5048
|
+
}
|
|
5049
|
+
getModifyOrderEncoded(params) {
|
|
4737
5050
|
try {
|
|
4738
5051
|
validateModifyParams({
|
|
4739
5052
|
a: Number(params.pairId),
|
|
@@ -4773,7 +5086,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4773
5086
|
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4774
5087
|
);
|
|
4775
5088
|
}
|
|
4776
|
-
return
|
|
5089
|
+
return encoded;
|
|
4777
5090
|
}
|
|
4778
5091
|
/**
|
|
4779
5092
|
* Update collateral on an open position (isolated margin).
|
|
@@ -4793,6 +5106,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4793
5106
|
* Checks USDC allowance before top-up operations.
|
|
4794
5107
|
*/
|
|
4795
5108
|
async updateCollateral(params) {
|
|
5109
|
+
return this.submitPrepared(await this.getUpdateCollateralEncoded(params));
|
|
5110
|
+
}
|
|
5111
|
+
async getUpdateCollateralTx(params) {
|
|
5112
|
+
return this.buildPreparedTx(await this.getUpdateCollateralEncoded(params));
|
|
5113
|
+
}
|
|
5114
|
+
async getUpdateCollateralEncoded(params) {
|
|
4796
5115
|
const amount = parseFloat(params.amount);
|
|
4797
5116
|
if (Number.isNaN(amount) || amount === 0) {
|
|
4798
5117
|
throw new OstiumError(
|
|
@@ -4816,7 +5135,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4816
5135
|
} else {
|
|
4817
5136
|
encoded = encodeRemoveCollateral(pairIndex, index, absAmount, this.contracts.trading);
|
|
4818
5137
|
}
|
|
4819
|
-
return
|
|
5138
|
+
return encoded;
|
|
4820
5139
|
}
|
|
4821
5140
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4822
5141
|
// Subgraph reads (Hyperliquid-style API)
|
|
@@ -4835,11 +5154,18 @@ var OstiumClient = class _OstiumClient {
|
|
|
4835
5154
|
/**
|
|
4836
5155
|
* Open positions + margin summary for a user. Defaults to the connected
|
|
4837
5156
|
* trader. Live prices and the current block number are fetched automatically.
|
|
5157
|
+
*
|
|
5158
|
+
* - `user`: pass an address to scope to a single trader, or `'ALL'` to fetch
|
|
5159
|
+
* positions across every trader (no trader filter).
|
|
5160
|
+
* - `limit`: cap the number of positions returned (default: all).
|
|
5161
|
+
* - `skip`: offset into the result set for pagination (default: `0`).
|
|
4838
5162
|
*/
|
|
4839
5163
|
async getOpenPositions(params) {
|
|
4840
5164
|
return this.subgraph.getOpenPositions({
|
|
4841
5165
|
user: params?.user ?? this.getTraderAddress(),
|
|
4842
|
-
blockNumber: params?.blockNumber ?? await this.publicClient.getBlockNumber()
|
|
5166
|
+
blockNumber: params?.blockNumber ?? await this.publicClient.getBlockNumber(),
|
|
5167
|
+
limit: params?.limit,
|
|
5168
|
+
skip: params?.skip
|
|
4843
5169
|
});
|
|
4844
5170
|
}
|
|
4845
5171
|
/**
|
|
@@ -4944,31 +5270,104 @@ var OstiumClient = class _OstiumClient {
|
|
|
4944
5270
|
streamPrices(pairIds) {
|
|
4945
5271
|
return this.subgraph.streamPrices(pairIds);
|
|
4946
5272
|
}
|
|
5273
|
+
/**
|
|
5274
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
5275
|
+
*
|
|
5276
|
+
* Subscribes only to the unique pairs referenced by the response and emits the
|
|
5277
|
+
* full updated positions payload on each relevant price update. Pass an
|
|
5278
|
+
* existing `priceStream` to reuse a websocket your app already owns, or omit
|
|
5279
|
+
* it to let the SDK create a dedicated connection.
|
|
5280
|
+
*/
|
|
5281
|
+
streamPositionUpdates(initial, priceStream) {
|
|
5282
|
+
return this.subgraph.streamPositionUpdates(initial, priceStream);
|
|
5283
|
+
}
|
|
4947
5284
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4948
5285
|
// Internal helpers
|
|
4949
5286
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
5287
|
+
getSetupGaslessDelegationEncoded() {
|
|
5288
|
+
if (!this.selfGasless || !this.effectiveSender) {
|
|
5289
|
+
throw new OstiumError(
|
|
5290
|
+
"setupGaslessDelegation() is only available in Self + Gasless mode.",
|
|
5291
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5292
|
+
);
|
|
5293
|
+
}
|
|
5294
|
+
return encodeSetDelegate(this.effectiveSender, this.contracts.trading);
|
|
5295
|
+
}
|
|
5296
|
+
getApproveUsdcEncoded(amount) {
|
|
5297
|
+
if (this.delegated) {
|
|
5298
|
+
throw new OstiumError(
|
|
5299
|
+
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
5300
|
+
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
5301
|
+
);
|
|
5302
|
+
}
|
|
5303
|
+
const rawAmount = amount === "max" ? viem.maxUint256 : parseUsdc(amount);
|
|
5304
|
+
return encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
5305
|
+
}
|
|
5306
|
+
getSetDelegateEncoded(delegateAddress) {
|
|
5307
|
+
return encodeSetDelegate(delegateAddress, this.contracts.trading);
|
|
5308
|
+
}
|
|
5309
|
+
getRemoveDelegateEncoded() {
|
|
5310
|
+
return encodeRemoveDelegate(this.contracts.trading);
|
|
5311
|
+
}
|
|
5312
|
+
prepareEncoded(encoded) {
|
|
5313
|
+
const signer = this.requireBuildCapability();
|
|
5314
|
+
return signer.prepare(encoded);
|
|
5315
|
+
}
|
|
5316
|
+
buildPreparedTx(encoded) {
|
|
5317
|
+
return this.toBuiltTxRequest(this.prepareEncoded(encoded));
|
|
5318
|
+
}
|
|
5319
|
+
buildDirectEoaTx(encoded) {
|
|
5320
|
+
if (!this.canBuildTransactions() || !this.traderAddress) {
|
|
5321
|
+
throw new OstiumError(
|
|
5322
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5323
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5324
|
+
);
|
|
5325
|
+
}
|
|
5326
|
+
return {
|
|
5327
|
+
kind: "eoa",
|
|
5328
|
+
to: encoded.to,
|
|
5329
|
+
data: encoded.data,
|
|
5330
|
+
value: encoded.value,
|
|
5331
|
+
from: this.traderAddress,
|
|
5332
|
+
traderAddress: this.traderAddress
|
|
5333
|
+
};
|
|
5334
|
+
}
|
|
5335
|
+
toBuiltTxRequest(encoded) {
|
|
5336
|
+
if (!this.canBuildTransactions() || !this.traderAddress || !this.effectiveSender) {
|
|
4963
5337
|
throw new OstiumError(
|
|
4964
|
-
"This client
|
|
5338
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4965
5339
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4966
5340
|
);
|
|
4967
5341
|
}
|
|
4968
|
-
|
|
5342
|
+
if (this.gasless) {
|
|
5343
|
+
return {
|
|
5344
|
+
kind: "safe",
|
|
5345
|
+
safeAddress: this.effectiveSender,
|
|
5346
|
+
traderAddress: this.traderAddress,
|
|
5347
|
+
calls: [encoded]
|
|
5348
|
+
};
|
|
5349
|
+
}
|
|
5350
|
+
return {
|
|
5351
|
+
kind: "eoa",
|
|
5352
|
+
to: encoded.to,
|
|
5353
|
+
data: encoded.data,
|
|
5354
|
+
value: encoded.value,
|
|
5355
|
+
from: this.effectiveSender,
|
|
5356
|
+
traderAddress: this.traderAddress
|
|
5357
|
+
};
|
|
5358
|
+
}
|
|
5359
|
+
async submitPrepared(encoded) {
|
|
5360
|
+
const submitter = this.requireSubmitCapability();
|
|
5361
|
+
return submitter.submit(this.prepareEncoded(encoded));
|
|
4969
5362
|
}
|
|
4970
5363
|
/** Direct EOA submission — used only in Self + Gasless for approveUsdc and setupGaslessDelegation. */
|
|
4971
|
-
async
|
|
5364
|
+
async submitDirectEoa(encoded) {
|
|
5365
|
+
if (!this.eoaSubmit) {
|
|
5366
|
+
throw new OstiumError(
|
|
5367
|
+
"This client cannot submit direct EOA transactions.",
|
|
5368
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5369
|
+
);
|
|
5370
|
+
}
|
|
4972
5371
|
try {
|
|
4973
5372
|
return await this.eoaSubmit(encoded);
|
|
4974
5373
|
} catch (err) {
|
|
@@ -4984,6 +5383,24 @@ var OstiumClient = class _OstiumClient {
|
|
|
4984
5383
|
throw new OstiumError(`Transaction failed: ${msg}`, "SUBMISSION_FAILED" /* SUBMISSION_FAILED */, err);
|
|
4985
5384
|
}
|
|
4986
5385
|
}
|
|
5386
|
+
requireBuildCapability() {
|
|
5387
|
+
if (!this.signer) {
|
|
5388
|
+
throw new OstiumError(
|
|
5389
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5390
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5391
|
+
);
|
|
5392
|
+
}
|
|
5393
|
+
return this.signer;
|
|
5394
|
+
}
|
|
5395
|
+
requireSubmitCapability() {
|
|
5396
|
+
if (!this.submitter) {
|
|
5397
|
+
throw new OstiumError(
|
|
5398
|
+
"This client does not have submission credentials. Use the corresponding get*Tx() method or initialize with a private key.",
|
|
5399
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5400
|
+
);
|
|
5401
|
+
}
|
|
5402
|
+
return this.submitter;
|
|
5403
|
+
}
|
|
4987
5404
|
resolveSlippage(override) {
|
|
4988
5405
|
return override ?? this.defaultSlippageBps;
|
|
4989
5406
|
}
|
|
@@ -5000,6 +5417,7 @@ exports.OrderType = OrderType;
|
|
|
5000
5417
|
exports.OstiumClient = OstiumClient;
|
|
5001
5418
|
exports.OstiumError = OstiumError;
|
|
5002
5419
|
exports.OstiumErrorCode = OstiumErrorCode;
|
|
5420
|
+
exports.OstiumPositionUpdatesStream = OstiumPositionUpdatesStream;
|
|
5003
5421
|
exports.OstiumPriceStream = OstiumPriceStream;
|
|
5004
5422
|
exports.OstiumSubgraphClient = OstiumSubgraphClient;
|
|
5005
5423
|
exports.OstiumSubgraphError = OstiumSubgraphError;
|