@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.js
CHANGED
|
@@ -254,11 +254,20 @@ var CancelOrderType = /* @__PURE__ */ ((CancelOrderType2) => {
|
|
|
254
254
|
// src/config.ts
|
|
255
255
|
var DEFAULT_PIMLICO_URL = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161";
|
|
256
256
|
var DEFAULT_PIMLICO_URL_TESTNET = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=421614";
|
|
257
|
-
function
|
|
258
|
-
return
|
|
257
|
+
function hasPrivateKey(config) {
|
|
258
|
+
return typeof config.privateKey === "string" && config.privateKey.length > 0;
|
|
259
259
|
}
|
|
260
|
-
function
|
|
261
|
-
return
|
|
260
|
+
function resolveMode(config) {
|
|
261
|
+
if (config.mode) return config.mode;
|
|
262
|
+
if (config.privateKey) {
|
|
263
|
+
if (config.pimlicoUrl) return config.traderAddress ? "delegated-gasless" : "self-gasless";
|
|
264
|
+
return config.traderAddress ? "delegated-self" : "self-self";
|
|
265
|
+
}
|
|
266
|
+
if (config.safeAddress && config.delegateAddress && config.traderAddress) return "delegated-gasless";
|
|
267
|
+
if (config.safeAddress && config.traderAddress) return "self-gasless";
|
|
268
|
+
if (config.delegateAddress && config.traderAddress) return "delegated-self";
|
|
269
|
+
if (config.traderAddress) return "self-self";
|
|
270
|
+
throw new Error("Unable to infer client mode from config");
|
|
262
271
|
}
|
|
263
272
|
|
|
264
273
|
// src/errors.ts
|
|
@@ -2661,8 +2670,8 @@ var DelegatedSignerStrategy = class {
|
|
|
2661
2670
|
};
|
|
2662
2671
|
|
|
2663
2672
|
// src/signer/index.ts
|
|
2664
|
-
function createSigner(
|
|
2665
|
-
if (
|
|
2673
|
+
function createSigner(mode, traderAddress) {
|
|
2674
|
+
if (mode !== "self-self") {
|
|
2666
2675
|
return new DelegatedSignerStrategy(traderAddress);
|
|
2667
2676
|
}
|
|
2668
2677
|
return new SelfSignerStrategy(traderAddress);
|
|
@@ -2686,6 +2695,12 @@ var SelfSubmissionStrategy = class {
|
|
|
2686
2695
|
effectiveSender;
|
|
2687
2696
|
send;
|
|
2688
2697
|
constructor(config, testnet) {
|
|
2698
|
+
if (!config.privateKey) {
|
|
2699
|
+
throw new OstiumError(
|
|
2700
|
+
"privateKey is required for self-submission mode",
|
|
2701
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2702
|
+
);
|
|
2703
|
+
}
|
|
2689
2704
|
if (!config.rpcUrl) {
|
|
2690
2705
|
throw new OstiumError(
|
|
2691
2706
|
"rpcUrl is required for self-submission mode",
|
|
@@ -2751,6 +2766,12 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2751
2766
|
this.effectiveSender = safeAccount.address;
|
|
2752
2767
|
}
|
|
2753
2768
|
static async create(config, testnet) {
|
|
2769
|
+
if (!config.privateKey) {
|
|
2770
|
+
throw new OstiumError(
|
|
2771
|
+
"privateKey is required for gasless submission mode",
|
|
2772
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2754
2775
|
if (!config.pimlicoUrl) {
|
|
2755
2776
|
throw new OstiumError(
|
|
2756
2777
|
"pimlicoUrl is required for gasless mode",
|
|
@@ -2819,8 +2840,8 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2819
2840
|
};
|
|
2820
2841
|
|
|
2821
2842
|
// src/submitter/index.ts
|
|
2822
|
-
async function createSubmitter(config, testnet) {
|
|
2823
|
-
if (
|
|
2843
|
+
async function createSubmitter(config, testnet, mode) {
|
|
2844
|
+
if (mode === "self-gasless" || mode === "delegated-gasless") {
|
|
2824
2845
|
return GaslessSubmissionStrategy.create(config, testnet);
|
|
2825
2846
|
}
|
|
2826
2847
|
if (!config.rpcUrl) {
|
|
@@ -2917,6 +2938,17 @@ query GetTraderOpenTrades($trader: String!, $skip: Int!, $first: Int!) {
|
|
|
2917
2938
|
${PAIR_FULL}
|
|
2918
2939
|
}
|
|
2919
2940
|
}`;
|
|
2941
|
+
var GetAllOpenTradesQuery = `
|
|
2942
|
+
query GetAllOpenTrades($skip: Int!, $first: Int!) {
|
|
2943
|
+
trades(
|
|
2944
|
+
where: { isOpen: true }
|
|
2945
|
+
skip: $skip first: $first
|
|
2946
|
+
orderBy: timestamp orderDirection: desc
|
|
2947
|
+
) {
|
|
2948
|
+
${TRADE_CORE}
|
|
2949
|
+
${PAIR_FULL}
|
|
2950
|
+
}
|
|
2951
|
+
}`;
|
|
2920
2952
|
var ORDER_FIELDS = `
|
|
2921
2953
|
id tradeID limitID trader
|
|
2922
2954
|
pair { id from to group { id name } }
|
|
@@ -2996,9 +3028,9 @@ query GetTraderActiveLimits($trader: String!, $skip: Int!, $first: Int!) {
|
|
|
2996
3028
|
}`;
|
|
2997
3029
|
|
|
2998
3030
|
// src/data/internal/pagination.ts
|
|
2999
|
-
async function paginateAll(fetcher, batchSize = 1e3, max = Infinity) {
|
|
3031
|
+
async function paginateAll(fetcher, batchSize = 1e3, max = Infinity, initialSkip = 0) {
|
|
3000
3032
|
const results = [];
|
|
3001
|
-
let skip =
|
|
3033
|
+
let skip = initialSkip;
|
|
3002
3034
|
while (results.length < max) {
|
|
3003
3035
|
const remaining = max - results.length;
|
|
3004
3036
|
const fetchSize = Math.min(batchSize, remaining);
|
|
@@ -3220,10 +3252,11 @@ async function fetchLivePrices(builderApiUrl) {
|
|
|
3220
3252
|
}
|
|
3221
3253
|
return result;
|
|
3222
3254
|
}
|
|
3223
|
-
function rawTickToPublic(item) {
|
|
3255
|
+
function rawTickToPublic(item, pairId) {
|
|
3224
3256
|
const from = normalizePairName(item.from);
|
|
3225
3257
|
const to = normalizePairName(item.to);
|
|
3226
3258
|
return {
|
|
3259
|
+
pairId,
|
|
3227
3260
|
feedId: item.feed_id,
|
|
3228
3261
|
pair: `${from}-${to}`,
|
|
3229
3262
|
from,
|
|
@@ -3270,9 +3303,12 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3270
3303
|
snapshotHandlers = /* @__PURE__ */ new Set();
|
|
3271
3304
|
/** pairId (string) → raw "FROM-TO" name for the WS API. */
|
|
3272
3305
|
pairRawNameCache;
|
|
3273
|
-
|
|
3306
|
+
/** raw "FROM-TO" name → pairId (string) for mapping incoming ticks back to SDK ids. */
|
|
3307
|
+
rawNamePairIdCache;
|
|
3308
|
+
constructor(ws, pairRawNameCache, rawNamePairIdCache, initialSubscribeRawPairs) {
|
|
3274
3309
|
this.ws = ws;
|
|
3275
3310
|
this.pairRawNameCache = pairRawNameCache;
|
|
3311
|
+
this.rawNamePairIdCache = rawNamePairIdCache;
|
|
3276
3312
|
if (initialSubscribeRawPairs?.length) {
|
|
3277
3313
|
ws.once("open", () => {
|
|
3278
3314
|
ws.send(JSON.stringify({ type: "subscribe", pairs: initialSubscribeRawPairs }));
|
|
@@ -3286,10 +3322,13 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3286
3322
|
return;
|
|
3287
3323
|
}
|
|
3288
3324
|
if (msg.type === "snapshot" && Array.isArray(msg.data)) {
|
|
3289
|
-
const ticks = msg.data.map(
|
|
3325
|
+
const ticks = msg.data.map(
|
|
3326
|
+
(item) => rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`))
|
|
3327
|
+
);
|
|
3290
3328
|
for (const h of this.snapshotHandlers) h(ticks);
|
|
3291
3329
|
} else if (msg.type === "tick" && msg.data) {
|
|
3292
|
-
const
|
|
3330
|
+
const item = msg.data;
|
|
3331
|
+
const tick = rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`));
|
|
3293
3332
|
for (const h of this.tickHandlers) h(tick);
|
|
3294
3333
|
}
|
|
3295
3334
|
});
|
|
@@ -3315,7 +3354,11 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3315
3354
|
const ws = new WS(url, {
|
|
3316
3355
|
headers: { "User-Agent": "ostium-sdk", "Accept": "*/*" }
|
|
3317
3356
|
});
|
|
3318
|
-
|
|
3357
|
+
const rawNamePairIdCache = /* @__PURE__ */ new Map();
|
|
3358
|
+
for (const [pairId, rawName] of pairRawNameCache.entries()) {
|
|
3359
|
+
rawNamePairIdCache.set(rawName, pairId);
|
|
3360
|
+
}
|
|
3361
|
+
return new _OstiumPriceStream(ws, pairRawNameCache, rawNamePairIdCache, initial);
|
|
3319
3362
|
}
|
|
3320
3363
|
/**
|
|
3321
3364
|
* Register a callback that fires on every incoming price tick.
|
|
@@ -3391,6 +3434,37 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3391
3434
|
}
|
|
3392
3435
|
};
|
|
3393
3436
|
|
|
3437
|
+
// src/data/internal/aggregations.ts
|
|
3438
|
+
function aggregateMarginSummary(pairPositions) {
|
|
3439
|
+
let accountValue = 0;
|
|
3440
|
+
let totalCollateralUsed = 0;
|
|
3441
|
+
let totalNtlPos = 0;
|
|
3442
|
+
let totalRawPnlUsd = 0;
|
|
3443
|
+
let totalCumRollover = 0;
|
|
3444
|
+
let totalWithdrawable = 0;
|
|
3445
|
+
for (const { position } of pairPositions) {
|
|
3446
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3447
|
+
const ntl = parseFloat(position.ntl) || 0;
|
|
3448
|
+
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
3449
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3450
|
+
const withdrawable = parseFloat(position.maxWithdrawable) || 0;
|
|
3451
|
+
accountValue += collateral + pnl;
|
|
3452
|
+
totalCollateralUsed += collateral;
|
|
3453
|
+
totalNtlPos += ntl;
|
|
3454
|
+
totalRawPnlUsd += pnl;
|
|
3455
|
+
totalCumRollover += rollover;
|
|
3456
|
+
totalWithdrawable += withdrawable;
|
|
3457
|
+
}
|
|
3458
|
+
return {
|
|
3459
|
+
accountValue: accountValue.toString(),
|
|
3460
|
+
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
3461
|
+
totalNtlPos: totalNtlPos.toString(),
|
|
3462
|
+
totalRawPnlUsd: totalRawPnlUsd.toString(),
|
|
3463
|
+
totalCumRollover: totalCumRollover.toString(),
|
|
3464
|
+
totalWithdrawable: totalWithdrawable.toString()
|
|
3465
|
+
};
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3394
3468
|
// src/data/internal/formatters.ts
|
|
3395
3469
|
var MIN_NOTIONAL = "5.0";
|
|
3396
3470
|
var MIN_NOTIONAL_NUM = 5;
|
|
@@ -3492,14 +3566,14 @@ function formatPosition(raw, price, pnl, maxLeverage) {
|
|
|
3492
3566
|
returnOnEquity: roe.toString(),
|
|
3493
3567
|
liquidationPx: pnl.liquidationPrice.toString(),
|
|
3494
3568
|
collateralUsed: collateral.toString(),
|
|
3495
|
-
cumRollover: pnl.rollover.toString(),
|
|
3569
|
+
cumRollover: (pnl.rollover * -1).toString(),
|
|
3496
3570
|
...raw.takeProfitPrice && raw.takeProfitPrice !== "0" ? { tpPx: formatTokens(raw.takeProfitPrice).toString() } : {},
|
|
3497
3571
|
...raw.stopLossPrice && raw.stopLossPrice !== "0" ? { slPx: formatTokens(raw.stopLossPrice).toString() } : {},
|
|
3498
3572
|
openTimestamp: parseInt(raw.timestamp || "0") * 1e3,
|
|
3499
3573
|
isDayTrade: raw.isDayTrade ?? false,
|
|
3500
|
-
maxLeverage: maxLeverage.toString()
|
|
3501
|
-
|
|
3502
|
-
|
|
3574
|
+
maxLeverage: maxLeverage.toString(),
|
|
3575
|
+
maxWithdrawable: maxWithdrawForPosition(collateral, leverage, maxLeverage).toString()
|
|
3576
|
+
}
|
|
3503
3577
|
};
|
|
3504
3578
|
}
|
|
3505
3579
|
function formatFill(raw) {
|
|
@@ -3576,6 +3650,131 @@ function formatOpenOrder(raw) {
|
|
|
3576
3650
|
timestamp: parseInt(raw.initiatedAt || "0") * 1e3
|
|
3577
3651
|
};
|
|
3578
3652
|
}
|
|
3653
|
+
|
|
3654
|
+
// src/data/internal/position-updates.ts
|
|
3655
|
+
function cloneResponse(response) {
|
|
3656
|
+
return {
|
|
3657
|
+
pairPositions: response.pairPositions.map((pairPos) => ({
|
|
3658
|
+
position: { ...pairPos.position }
|
|
3659
|
+
})),
|
|
3660
|
+
marginSummary: { ...response.marginSummary },
|
|
3661
|
+
time: response.time
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
function applyTickToPosition(position, tick) {
|
|
3665
|
+
const size = parseFloat(position.szi) || 0;
|
|
3666
|
+
const entry = parseFloat(position.entryPx) || 0;
|
|
3667
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3668
|
+
const maxLeverage = parseFloat(position.maxLeverage) || 0;
|
|
3669
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3670
|
+
const isLong = position.side === "B";
|
|
3671
|
+
const ntl = size * tick.mid;
|
|
3672
|
+
const rawPnl = isLong ? (tick.mid - entry) * size : (entry - tick.mid) * size;
|
|
3673
|
+
const netPnl = rawPnl + rollover;
|
|
3674
|
+
const roe = collateral > 0 ? netPnl / collateral : 0;
|
|
3675
|
+
const currentLeverage = collateral > 0 ? ntl / collateral : 0;
|
|
3676
|
+
const maxWithdrawable = maxWithdrawForPosition(collateral, currentLeverage, maxLeverage);
|
|
3677
|
+
return {
|
|
3678
|
+
...position,
|
|
3679
|
+
ntl: ntl.toString(),
|
|
3680
|
+
unrealizedPnl: netPnl.toString(),
|
|
3681
|
+
returnOnEquity: roe.toString(),
|
|
3682
|
+
maxWithdrawable: maxWithdrawable.toString()
|
|
3683
|
+
};
|
|
3684
|
+
}
|
|
3685
|
+
function updateResponseWithTick(current, tick, timestampMs) {
|
|
3686
|
+
if (!tick.pairId) return current;
|
|
3687
|
+
let changed = false;
|
|
3688
|
+
const pairPositions = current.pairPositions.map((pairPos) => {
|
|
3689
|
+
if (String(pairPos.position.pairId) !== String(tick.pairId)) {
|
|
3690
|
+
return pairPos;
|
|
3691
|
+
}
|
|
3692
|
+
changed = true;
|
|
3693
|
+
return {
|
|
3694
|
+
...pairPos,
|
|
3695
|
+
position: applyTickToPosition(pairPos.position, tick)
|
|
3696
|
+
};
|
|
3697
|
+
});
|
|
3698
|
+
if (!changed) return current;
|
|
3699
|
+
return {
|
|
3700
|
+
pairPositions,
|
|
3701
|
+
marginSummary: aggregateMarginSummary(pairPositions),
|
|
3702
|
+
time: timestampMs
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
var OstiumPositionUpdatesStream = class {
|
|
3706
|
+
priceStream;
|
|
3707
|
+
updateHandlers = /* @__PURE__ */ new Set();
|
|
3708
|
+
current;
|
|
3709
|
+
trackedPairIds;
|
|
3710
|
+
constructor(initial, trackedPairIds, priceStream) {
|
|
3711
|
+
this.current = cloneResponse(initial);
|
|
3712
|
+
this.priceStream = priceStream;
|
|
3713
|
+
this.trackedPairIds = new Set(trackedPairIds);
|
|
3714
|
+
if (!priceStream) return;
|
|
3715
|
+
priceStream.onSnapshot((ticks) => {
|
|
3716
|
+
this.ingestSnapshot(ticks);
|
|
3717
|
+
});
|
|
3718
|
+
priceStream.onTick((tick) => {
|
|
3719
|
+
this.ingestTick(tick);
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
onUpdate(handler) {
|
|
3723
|
+
this.updateHandlers.add(handler);
|
|
3724
|
+
return () => {
|
|
3725
|
+
this.updateHandlers.delete(handler);
|
|
3726
|
+
};
|
|
3727
|
+
}
|
|
3728
|
+
onOpen(handler) {
|
|
3729
|
+
this.priceStream?.onOpen(handler);
|
|
3730
|
+
return this;
|
|
3731
|
+
}
|
|
3732
|
+
onError(handler) {
|
|
3733
|
+
this.priceStream?.onError(handler);
|
|
3734
|
+
return this;
|
|
3735
|
+
}
|
|
3736
|
+
onClose(handler) {
|
|
3737
|
+
this.priceStream?.onClose(handler);
|
|
3738
|
+
return this;
|
|
3739
|
+
}
|
|
3740
|
+
getCurrent() {
|
|
3741
|
+
return cloneResponse(this.current);
|
|
3742
|
+
}
|
|
3743
|
+
/**
|
|
3744
|
+
* Apply a single externally sourced price tick to the tracked positions.
|
|
3745
|
+
*
|
|
3746
|
+
* Use this when your app already has its own websocket connection and you want
|
|
3747
|
+
* the SDK to handle only the position recalculation logic.
|
|
3748
|
+
*/
|
|
3749
|
+
ingestTick(tick) {
|
|
3750
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) return;
|
|
3751
|
+
const next = updateResponseWithTick(this.current, tick, tick.timestampSeconds * 1e3);
|
|
3752
|
+
if (next !== this.current) {
|
|
3753
|
+
this.current = next;
|
|
3754
|
+
this.emit(next);
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
/**
|
|
3758
|
+
* Apply a batch of externally sourced ticks, such as a websocket snapshot.
|
|
3759
|
+
*/
|
|
3760
|
+
ingestSnapshot(ticks) {
|
|
3761
|
+
let next = this.current;
|
|
3762
|
+
for (const tick of ticks) {
|
|
3763
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) continue;
|
|
3764
|
+
next = updateResponseWithTick(next, tick, tick.timestampSeconds * 1e3);
|
|
3765
|
+
}
|
|
3766
|
+
if (next !== this.current) {
|
|
3767
|
+
this.current = next;
|
|
3768
|
+
this.emit(next);
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
close() {
|
|
3772
|
+
this.priceStream?.close();
|
|
3773
|
+
}
|
|
3774
|
+
emit(positions) {
|
|
3775
|
+
for (const handler of this.updateHandlers) handler(cloneResponse(positions));
|
|
3776
|
+
}
|
|
3777
|
+
};
|
|
3579
3778
|
var EMPTY_RESULT = { long: [], short: [] };
|
|
3580
3779
|
var ORDERBOOK_MAX_LEVELS = 20;
|
|
3581
3780
|
function generateLogSpacedNotionals(levels, capacity) {
|
|
@@ -3760,31 +3959,40 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
3760
3959
|
* PnL fields are zeroed.
|
|
3761
3960
|
*/
|
|
3762
3961
|
async getOpenPositions(params) {
|
|
3763
|
-
const { user, blockNumber } = params;
|
|
3962
|
+
const { user, blockNumber, limit = Infinity, skip: initialSkip = 0 } = params;
|
|
3963
|
+
const isGlobal = user === "ALL";
|
|
3764
3964
|
const [trades, prices] = await Promise.all([
|
|
3765
|
-
paginateAll(
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3965
|
+
paginateAll(
|
|
3966
|
+
async (skip, first) => {
|
|
3967
|
+
if (isGlobal) {
|
|
3968
|
+
const data2 = await this.query(
|
|
3969
|
+
GetAllOpenTradesQuery,
|
|
3970
|
+
{ skip, first }
|
|
3971
|
+
);
|
|
3972
|
+
return data2.trades;
|
|
3973
|
+
}
|
|
3974
|
+
const data = await this.query(
|
|
3975
|
+
GetTraderOpenTradesQuery,
|
|
3976
|
+
{ trader: user.toLowerCase(), skip, first }
|
|
3977
|
+
);
|
|
3978
|
+
return data.trades;
|
|
3979
|
+
},
|
|
3980
|
+
1e3,
|
|
3981
|
+
limit,
|
|
3982
|
+
initialSkip
|
|
3983
|
+
),
|
|
3772
3984
|
this.fetchLivePricesSafe()
|
|
3773
3985
|
]);
|
|
3774
|
-
let totalWithdrawable = 0;
|
|
3775
3986
|
const pairPositions = trades.map((trade) => {
|
|
3776
3987
|
const price = prices[`${trade.pair.from}/${trade.pair.to}`];
|
|
3777
3988
|
const pnl = price && blockNumber ? getTradePnL(trade, price, blockNumber) : EMPTY_PNL;
|
|
3778
3989
|
const maxLev = trade.isDayTrade ? formatLeverage(trade.pair.overnightMaxLeverage) : pairMaxLeverage(trade.pair);
|
|
3779
|
-
|
|
3780
|
-
totalWithdrawable += parseFloat(pairPos.maxWithdrawable);
|
|
3781
|
-
return pairPos;
|
|
3990
|
+
return formatPosition(trade, price, pnl, maxLev);
|
|
3782
3991
|
});
|
|
3783
3992
|
const marginSummary = aggregateMarginSummary(pairPositions);
|
|
3784
3993
|
return {
|
|
3785
3994
|
pairPositions,
|
|
3786
3995
|
marginSummary,
|
|
3787
|
-
withdrawable: totalWithdrawable.toString(),
|
|
3788
3996
|
time: Date.now()
|
|
3789
3997
|
};
|
|
3790
3998
|
}
|
|
@@ -4035,6 +4243,24 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4035
4243
|
});
|
|
4036
4244
|
return OstiumPriceStream.connect(this.builderApiUrl, rawNames, cache);
|
|
4037
4245
|
}
|
|
4246
|
+
/**
|
|
4247
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
4248
|
+
*
|
|
4249
|
+
* The SDK subscribes only to the unique pairs present in `initial.pairPositions`,
|
|
4250
|
+
* recalculates price-sensitive fields for affected positions on each tick, and
|
|
4251
|
+
* emits the full updated response.
|
|
4252
|
+
*
|
|
4253
|
+
* Pass an existing `priceStream` to reuse a websocket connection your app has
|
|
4254
|
+
* already opened. Omit it to let the SDK open and manage a dedicated
|
|
4255
|
+
* connection for the tracked pair ids.
|
|
4256
|
+
*/
|
|
4257
|
+
streamPositionUpdates(initial, priceStream) {
|
|
4258
|
+
const pairIds = [...new Set(initial.pairPositions.map(({ position }) => String(position.pairId)))];
|
|
4259
|
+
if (pairIds.length === 0) {
|
|
4260
|
+
return new OstiumPositionUpdatesStream(initial, []);
|
|
4261
|
+
}
|
|
4262
|
+
return new OstiumPositionUpdatesStream(initial, pairIds, priceStream ?? this.streamPrices(pairIds));
|
|
4263
|
+
}
|
|
4038
4264
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4039
4265
|
// Internal — fetchers, cache, query wrapper
|
|
4040
4266
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -4093,43 +4319,28 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4093
4319
|
}
|
|
4094
4320
|
}
|
|
4095
4321
|
};
|
|
4096
|
-
function aggregateMarginSummary(pairPositions) {
|
|
4097
|
-
let accountValue = 0;
|
|
4098
|
-
let totalCollateralUsed = 0;
|
|
4099
|
-
let totalNtlPos = 0;
|
|
4100
|
-
let totalRawPnlUsd = 0;
|
|
4101
|
-
for (const { position } of pairPositions) {
|
|
4102
|
-
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
4103
|
-
const ntl = parseFloat(position.ntl) || 0;
|
|
4104
|
-
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
4105
|
-
accountValue += collateral + pnl;
|
|
4106
|
-
totalCollateralUsed += collateral;
|
|
4107
|
-
totalNtlPos += ntl;
|
|
4108
|
-
totalRawPnlUsd += pnl;
|
|
4109
|
-
}
|
|
4110
|
-
return {
|
|
4111
|
-
accountValue: accountValue.toString(),
|
|
4112
|
-
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
4113
|
-
totalNtlPos: totalNtlPos.toString(),
|
|
4114
|
-
totalRawPnlUsd: totalRawPnlUsd.toString()
|
|
4115
|
-
};
|
|
4116
|
-
}
|
|
4117
4322
|
|
|
4118
4323
|
// src/client.ts
|
|
4119
4324
|
var HEX64_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
4120
4325
|
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
4121
4326
|
function validateConfig(config) {
|
|
4122
|
-
if (!HEX64_RE.test(config.privateKey)) {
|
|
4327
|
+
if (config.privateKey && !HEX64_RE.test(config.privateKey)) {
|
|
4123
4328
|
throw new OstiumError(
|
|
4124
4329
|
"privateKey must be a 64-character hex string prefixed with 0x",
|
|
4125
4330
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4126
4331
|
);
|
|
4127
4332
|
}
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4333
|
+
for (const [label, value] of [
|
|
4334
|
+
["traderAddress", config.traderAddress],
|
|
4335
|
+
["delegateAddress", config.delegateAddress],
|
|
4336
|
+
["safeAddress", config.safeAddress]
|
|
4337
|
+
]) {
|
|
4338
|
+
if (value && !ADDRESS_RE.test(value)) {
|
|
4339
|
+
throw new OstiumError(
|
|
4340
|
+
`${label} must be a valid 42-character Ethereum address (0x + 40 hex chars)`,
|
|
4341
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4342
|
+
);
|
|
4343
|
+
}
|
|
4133
4344
|
}
|
|
4134
4345
|
if (config.slippageBps !== void 0 && (config.slippageBps < 0 || config.slippageBps > 500)) {
|
|
4135
4346
|
throw new OstiumError(
|
|
@@ -4143,10 +4354,45 @@ function validateConfig(config) {
|
|
|
4143
4354
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4144
4355
|
);
|
|
4145
4356
|
}
|
|
4357
|
+
let mode;
|
|
4358
|
+
try {
|
|
4359
|
+
mode = resolveMode(config);
|
|
4360
|
+
} catch {
|
|
4361
|
+
throw new OstiumError(
|
|
4362
|
+
"Could not infer client mode from config. Provide the required addresses for the selected factory.",
|
|
4363
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4366
|
+
if (mode === "self-self" && !config.privateKey && !config.traderAddress) {
|
|
4367
|
+
throw new OstiumError(
|
|
4368
|
+
"Self + Self mode requires traderPrivateKey for submission or traderAddress for build-only usage.",
|
|
4369
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4370
|
+
);
|
|
4371
|
+
}
|
|
4372
|
+
if (mode === "self-gasless" && !config.privateKey && (!config.traderAddress || !config.safeAddress)) {
|
|
4373
|
+
throw new OstiumError(
|
|
4374
|
+
"Self + Gasless build-only mode requires traderAddress and safeAddress.",
|
|
4375
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4376
|
+
);
|
|
4377
|
+
}
|
|
4378
|
+
if (mode === "delegated-self" && (!config.traderAddress || !config.privateKey && !config.delegateAddress)) {
|
|
4379
|
+
throw new OstiumError(
|
|
4380
|
+
"Delegated + Self mode requires traderAddress and either delegatePrivateKey or delegateAddress.",
|
|
4381
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
if (mode === "delegated-gasless" && (!config.traderAddress || !config.privateKey && (!config.delegateAddress || !config.safeAddress))) {
|
|
4385
|
+
throw new OstiumError(
|
|
4386
|
+
"Delegated + Gasless mode requires traderAddress and either delegatePrivateKey or both delegateAddress and safeAddress.",
|
|
4387
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4388
|
+
);
|
|
4389
|
+
}
|
|
4146
4390
|
}
|
|
4147
4391
|
var OstiumClient = class _OstiumClient {
|
|
4148
4392
|
signer;
|
|
4149
4393
|
submitter;
|
|
4394
|
+
traderAddress;
|
|
4395
|
+
effectiveSender;
|
|
4150
4396
|
contracts;
|
|
4151
4397
|
publicClient;
|
|
4152
4398
|
defaultSlippageBps;
|
|
@@ -4161,6 +4407,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4161
4407
|
constructor(internals) {
|
|
4162
4408
|
this.signer = internals.signer;
|
|
4163
4409
|
this.submitter = internals.submitter;
|
|
4410
|
+
this.traderAddress = internals.traderAddress;
|
|
4411
|
+
this.effectiveSender = internals.effectiveSender;
|
|
4164
4412
|
this.contracts = internals.contracts;
|
|
4165
4413
|
this.publicClient = internals.publicClient;
|
|
4166
4414
|
this.defaultSlippageBps = internals.defaultSlippageBps;
|
|
@@ -4185,17 +4433,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4185
4433
|
* });
|
|
4186
4434
|
* await client.openTrade({ ... });
|
|
4187
4435
|
* ```
|
|
4188
|
-
|
|
4436
|
+
*/
|
|
4189
4437
|
static async createSelfAndSelf(params) {
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
rpcUrl: params.rpcUrl,
|
|
4438
|
+
const base = {
|
|
4439
|
+
mode: "self-self",
|
|
4193
4440
|
testnet: params.testnet,
|
|
4194
4441
|
slippageBps: params.slippageBps,
|
|
4195
4442
|
builder: params.builder,
|
|
4196
4443
|
subgraphUrl: params.subgraphUrl,
|
|
4197
|
-
builderApiUrl: params.builderApiUrl
|
|
4198
|
-
|
|
4444
|
+
builderApiUrl: params.builderApiUrl,
|
|
4445
|
+
rpcUrl: params.rpcUrl
|
|
4446
|
+
};
|
|
4447
|
+
return _OstiumClient._fromConfig(
|
|
4448
|
+
"traderPrivateKey" in params ? { ...base, privateKey: params.traderPrivateKey } : { ...base, traderAddress: params.traderAddress }
|
|
4449
|
+
);
|
|
4199
4450
|
}
|
|
4200
4451
|
/**
|
|
4201
4452
|
* **Self + Gasless** — your EOA owns everything; a Safe relays trades for free.
|
|
@@ -4219,17 +4470,27 @@ var OstiumClient = class _OstiumClient {
|
|
|
4219
4470
|
*/
|
|
4220
4471
|
static async createSelfAndGasless(params) {
|
|
4221
4472
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4473
|
+
const base = {
|
|
4474
|
+
mode: "self-gasless",
|
|
4225
4475
|
rpcUrl: params.rpcUrl,
|
|
4226
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4227
4476
|
testnet: params.testnet,
|
|
4228
4477
|
slippageBps: params.slippageBps,
|
|
4229
4478
|
builder: params.builder,
|
|
4230
4479
|
subgraphUrl: params.subgraphUrl,
|
|
4231
4480
|
builderApiUrl: params.builderApiUrl
|
|
4232
|
-
}
|
|
4481
|
+
};
|
|
4482
|
+
return _OstiumClient._fromConfig(
|
|
4483
|
+
"traderPrivateKey" in params ? {
|
|
4484
|
+
...base,
|
|
4485
|
+
privateKey: params.traderPrivateKey,
|
|
4486
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4487
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4488
|
+
} : {
|
|
4489
|
+
...base,
|
|
4490
|
+
traderAddress: params.traderAddress,
|
|
4491
|
+
safeAddress: params.safeAddress
|
|
4492
|
+
}
|
|
4493
|
+
);
|
|
4233
4494
|
}
|
|
4234
4495
|
/**
|
|
4235
4496
|
* **Delegated + Self** — a delegate EOA signs and pays gas on behalf of a trader address.
|
|
@@ -4249,8 +4510,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4249
4510
|
* ```
|
|
4250
4511
|
*/
|
|
4251
4512
|
static async createDelegatedAndSelf(params) {
|
|
4252
|
-
|
|
4253
|
-
|
|
4513
|
+
const base = {
|
|
4514
|
+
mode: "delegated-self",
|
|
4254
4515
|
traderAddress: params.traderAddress,
|
|
4255
4516
|
rpcUrl: params.rpcUrl,
|
|
4256
4517
|
testnet: params.testnet,
|
|
@@ -4258,7 +4519,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4258
4519
|
builder: params.builder,
|
|
4259
4520
|
subgraphUrl: params.subgraphUrl,
|
|
4260
4521
|
builderApiUrl: params.builderApiUrl
|
|
4261
|
-
}
|
|
4522
|
+
};
|
|
4523
|
+
return _OstiumClient._fromConfig(
|
|
4524
|
+
"delegatePrivateKey" in params ? { ...base, privateKey: params.delegatePrivateKey } : { ...base, delegateAddress: params.delegateAddress }
|
|
4525
|
+
);
|
|
4262
4526
|
}
|
|
4263
4527
|
/**
|
|
4264
4528
|
* **Delegated + Gasless** — a Safe derived from the delegate key submits sponsored
|
|
@@ -4279,18 +4543,28 @@ var OstiumClient = class _OstiumClient {
|
|
|
4279
4543
|
*/
|
|
4280
4544
|
static async createDelegatedAndGasless(params) {
|
|
4281
4545
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4282
|
-
|
|
4283
|
-
|
|
4546
|
+
const base = {
|
|
4547
|
+
mode: "delegated-gasless",
|
|
4284
4548
|
traderAddress: params.traderAddress,
|
|
4285
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4286
4549
|
rpcUrl: params.rpcUrl,
|
|
4287
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4288
4550
|
testnet: params.testnet,
|
|
4289
4551
|
slippageBps: params.slippageBps,
|
|
4290
4552
|
builder: params.builder,
|
|
4291
4553
|
subgraphUrl: params.subgraphUrl,
|
|
4292
4554
|
builderApiUrl: params.builderApiUrl
|
|
4293
|
-
}
|
|
4555
|
+
};
|
|
4556
|
+
return _OstiumClient._fromConfig(
|
|
4557
|
+
"delegatePrivateKey" in params ? {
|
|
4558
|
+
...base,
|
|
4559
|
+
privateKey: params.delegatePrivateKey,
|
|
4560
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4561
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4562
|
+
} : {
|
|
4563
|
+
...base,
|
|
4564
|
+
delegateAddress: params.delegateAddress,
|
|
4565
|
+
safeAddress: params.safeAddress
|
|
4566
|
+
}
|
|
4567
|
+
);
|
|
4294
4568
|
}
|
|
4295
4569
|
/**
|
|
4296
4570
|
* **Read-only client** — no signer, no submitter, no privateKey required.
|
|
@@ -4335,6 +4609,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4335
4609
|
/** Internal factory shared by all public constructors. */
|
|
4336
4610
|
static async _fromConfig(config) {
|
|
4337
4611
|
validateConfig(config);
|
|
4612
|
+
const mode = resolveMode(config);
|
|
4338
4613
|
const testnet = config.testnet ?? false;
|
|
4339
4614
|
const networkConfig = getNetworkConfig(testnet);
|
|
4340
4615
|
const chain = testnet ? arbitrumSepolia : arbitrum;
|
|
@@ -4344,18 +4619,29 @@ var OstiumClient = class _OstiumClient {
|
|
|
4344
4619
|
tradingStorage: networkConfig.contracts.tradingStorage,
|
|
4345
4620
|
usdc: networkConfig.contracts.usdc
|
|
4346
4621
|
};
|
|
4347
|
-
const selfGasless =
|
|
4348
|
-
const submitter = await createSubmitter(config, testnet);
|
|
4622
|
+
const selfGasless = mode === "self-gasless";
|
|
4349
4623
|
let traderAddress;
|
|
4350
|
-
if (
|
|
4624
|
+
if (mode === "self-self" || mode === "self-gasless") {
|
|
4625
|
+
traderAddress = config.traderAddress ?? privateKeyToAccount(config.privateKey).address;
|
|
4626
|
+
} else {
|
|
4351
4627
|
traderAddress = config.traderAddress;
|
|
4628
|
+
}
|
|
4629
|
+
const signer = createSigner(mode, traderAddress);
|
|
4630
|
+
let submitter;
|
|
4631
|
+
let effectiveSender;
|
|
4632
|
+
if (hasPrivateKey(config)) {
|
|
4633
|
+
submitter = await createSubmitter(config, testnet, mode);
|
|
4634
|
+
effectiveSender = submitter.effectiveSender;
|
|
4635
|
+
} else if (mode === "self-self") {
|
|
4636
|
+
effectiveSender = traderAddress;
|
|
4637
|
+
} else if (mode === "delegated-self") {
|
|
4638
|
+
effectiveSender = config.delegateAddress;
|
|
4352
4639
|
} else {
|
|
4353
|
-
|
|
4640
|
+
effectiveSender = config.safeAddress;
|
|
4354
4641
|
}
|
|
4355
|
-
const signer = createSigner(config, traderAddress, selfGasless);
|
|
4356
4642
|
const publicRpc = testnet ? "https://sepolia-rollup.arbitrum.io/rpc" : "https://arb1.arbitrum.io/rpc";
|
|
4357
|
-
const rpcUrl = config.rpcUrl ??
|
|
4358
|
-
if (
|
|
4643
|
+
const rpcUrl = config.rpcUrl ?? publicRpc;
|
|
4644
|
+
if (hasPrivateKey(config) && mode !== "self-gasless" && mode !== "delegated-gasless" && !config.rpcUrl) {
|
|
4359
4645
|
throw new OstiumError(
|
|
4360
4646
|
"rpcUrl is required for self-submission mode",
|
|
4361
4647
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
@@ -4363,7 +4649,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4363
4649
|
}
|
|
4364
4650
|
const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });
|
|
4365
4651
|
let eoaSubmit;
|
|
4366
|
-
if (selfGasless) {
|
|
4652
|
+
if (selfGasless && hasPrivateKey(config)) {
|
|
4367
4653
|
const eoaAccount = privateKeyToAccount(config.privateKey);
|
|
4368
4654
|
const eoaRpcUrl = config.rpcUrl ?? publicRpc;
|
|
4369
4655
|
const eoaWalletClient = createWalletClient({ account: eoaAccount, chain, transport: http(eoaRpcUrl) });
|
|
@@ -4385,11 +4671,13 @@ var OstiumClient = class _OstiumClient {
|
|
|
4385
4671
|
return new _OstiumClient({
|
|
4386
4672
|
signer,
|
|
4387
4673
|
submitter,
|
|
4674
|
+
traderAddress,
|
|
4675
|
+
effectiveSender,
|
|
4388
4676
|
contracts,
|
|
4389
4677
|
publicClient,
|
|
4390
4678
|
defaultSlippageBps,
|
|
4391
|
-
delegated:
|
|
4392
|
-
gasless:
|
|
4679
|
+
delegated: mode === "delegated-self" || mode === "delegated-gasless",
|
|
4680
|
+
gasless: mode === "self-gasless" || mode === "delegated-gasless",
|
|
4393
4681
|
selfGasless,
|
|
4394
4682
|
eoaSubmit,
|
|
4395
4683
|
builderAddress: config.builder?.address,
|
|
@@ -4407,17 +4695,25 @@ var OstiumClient = class _OstiumClient {
|
|
|
4407
4695
|
* - Self + Gasless: EOA derived from privateKey (NOT the Safe — USDC lives here).
|
|
4408
4696
|
*/
|
|
4409
4697
|
getTraderAddress() {
|
|
4410
|
-
if (!this.
|
|
4698
|
+
if (!this.traderAddress) {
|
|
4411
4699
|
throw new OstiumError(
|
|
4412
4700
|
"No connected trader \u2014 pass `user` explicitly or use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4413
4701
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4414
4702
|
);
|
|
4415
4703
|
}
|
|
4416
|
-
return this.
|
|
4704
|
+
return this.traderAddress;
|
|
4417
4705
|
}
|
|
4418
|
-
/** True when the client
|
|
4706
|
+
/** True when the client cannot submit transactions directly. */
|
|
4419
4707
|
isReadOnly() {
|
|
4420
|
-
return !this.
|
|
4708
|
+
return !this.submitter;
|
|
4709
|
+
}
|
|
4710
|
+
/** True when the client can build mode-correct unsigned transaction requests. */
|
|
4711
|
+
canBuildTransactions() {
|
|
4712
|
+
return !!this.signer && !!this.effectiveSender;
|
|
4713
|
+
}
|
|
4714
|
+
/** True when the client has the credentials needed for SDK-managed submission. */
|
|
4715
|
+
canSubmitTransactions() {
|
|
4716
|
+
return !!this.submitter;
|
|
4421
4717
|
}
|
|
4422
4718
|
/**
|
|
4423
4719
|
* Returns the Safe smart-account address used for gasless submission.
|
|
@@ -4429,7 +4725,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4429
4725
|
* - Self + Self / Delegated + Self: returns undefined.
|
|
4430
4726
|
*/
|
|
4431
4727
|
getSmartAccountAddress() {
|
|
4432
|
-
return this.gasless
|
|
4728
|
+
return this.gasless ? this.effectiveSender : void 0;
|
|
4433
4729
|
}
|
|
4434
4730
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4435
4731
|
// Self + Gasless setup
|
|
@@ -4453,15 +4749,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4453
4749
|
* ```
|
|
4454
4750
|
*/
|
|
4455
4751
|
async setupGaslessDelegation() {
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
);
|
|
4461
|
-
}
|
|
4462
|
-
const safeAddress = this.submitter.effectiveSender;
|
|
4463
|
-
const encoded = encodeSetDelegate(safeAddress, this.contracts.trading);
|
|
4464
|
-
return this.submitDirectFromEoa(encoded);
|
|
4752
|
+
return this.submitDirectEoa(this.getSetupGaslessDelegationEncoded());
|
|
4753
|
+
}
|
|
4754
|
+
getSetupGaslessDelegationTx() {
|
|
4755
|
+
return this.buildDirectEoaTx(this.getSetupGaslessDelegationEncoded());
|
|
4465
4756
|
}
|
|
4466
4757
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4467
4758
|
// USDC helpers
|
|
@@ -4523,18 +4814,14 @@ var OstiumClient = class _OstiumClient {
|
|
|
4523
4814
|
* @param amount USD amount as decimal string (e.g. "1000"), or "max" for MaxUint256.
|
|
4524
4815
|
*/
|
|
4525
4816
|
async approveUsdc(amount) {
|
|
4526
|
-
|
|
4527
|
-
throw new OstiumError(
|
|
4528
|
-
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
4529
|
-
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
4530
|
-
);
|
|
4531
|
-
}
|
|
4532
|
-
const rawAmount = amount === "max" ? maxUint256 : parseUsdc(amount);
|
|
4533
|
-
const encoded = encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
4817
|
+
const encoded = this.getApproveUsdcEncoded(amount);
|
|
4534
4818
|
if (this.selfGasless) {
|
|
4535
|
-
return this.
|
|
4819
|
+
return this.submitDirectEoa(encoded);
|
|
4536
4820
|
}
|
|
4537
|
-
return this.
|
|
4821
|
+
return this.submitPrepared(encoded);
|
|
4822
|
+
}
|
|
4823
|
+
getApproveUsdcTx(amount) {
|
|
4824
|
+
return this.buildDirectEoaTx(this.getApproveUsdcEncoded(amount));
|
|
4538
4825
|
}
|
|
4539
4826
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4540
4827
|
// Delegation helpers
|
|
@@ -4548,16 +4835,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4548
4835
|
* update the trader's delegation on their behalf.
|
|
4549
4836
|
*/
|
|
4550
4837
|
async setDelegate(delegateAddress) {
|
|
4551
|
-
|
|
4552
|
-
|
|
4838
|
+
return this.submitPrepared(this.getSetDelegateEncoded(delegateAddress));
|
|
4839
|
+
}
|
|
4840
|
+
getSetDelegateTx(delegateAddress) {
|
|
4841
|
+
return this.buildPreparedTx(this.getSetDelegateEncoded(delegateAddress));
|
|
4553
4842
|
}
|
|
4554
4843
|
/**
|
|
4555
4844
|
* Remove the current delegate on the trading contract.
|
|
4556
4845
|
* Follows the same delegation-wrapping rules as setDelegate().
|
|
4557
4846
|
*/
|
|
4558
4847
|
async removeDelegate() {
|
|
4559
|
-
|
|
4560
|
-
|
|
4848
|
+
return this.submitPrepared(this.getRemoveDelegateEncoded());
|
|
4849
|
+
}
|
|
4850
|
+
getRemoveDelegateTx() {
|
|
4851
|
+
return this.buildPreparedTx(this.getRemoveDelegateEncoded());
|
|
4561
4852
|
}
|
|
4562
4853
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4563
4854
|
// Core trading methods
|
|
@@ -4570,6 +4861,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4570
4861
|
* the current allowance.
|
|
4571
4862
|
*/
|
|
4572
4863
|
async openTrade(params) {
|
|
4864
|
+
return this.submitPrepared(this.getOpenTradeEncoded(params));
|
|
4865
|
+
}
|
|
4866
|
+
getOpenTradeTx(params) {
|
|
4867
|
+
return this.buildPreparedTx(this.getOpenTradeEncoded(params));
|
|
4868
|
+
}
|
|
4869
|
+
getOpenTradeEncoded(params) {
|
|
4573
4870
|
const collateralNum = parseFloat(params.collateral);
|
|
4574
4871
|
if (collateralNum > MAX_COLLATERAL_USD) {
|
|
4575
4872
|
throw new OstiumError(
|
|
@@ -4578,7 +4875,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4578
4875
|
);
|
|
4579
4876
|
}
|
|
4580
4877
|
const apiOpen = {
|
|
4581
|
-
a: params.
|
|
4878
|
+
a: Number(params.pairId),
|
|
4582
4879
|
b: params.buy,
|
|
4583
4880
|
p: params.price,
|
|
4584
4881
|
s: params.collateral,
|
|
@@ -4601,8 +4898,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4601
4898
|
}
|
|
4602
4899
|
const builderFee = buildBuilderFee(apiBuilder);
|
|
4603
4900
|
const slippageP = params.type === "market" /* Market */ ? BigInt(this.resolveSlippage(params.slippage)) : 0n;
|
|
4604
|
-
|
|
4605
|
-
return this.execute(encoded);
|
|
4901
|
+
return encodeOpenTrade(trade, builderFee, orderType, slippageP, this.contracts.trading);
|
|
4606
4902
|
}
|
|
4607
4903
|
/**
|
|
4608
4904
|
* Close an open position (full or partial).
|
|
@@ -4617,6 +4913,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4617
4913
|
* ```
|
|
4618
4914
|
*/
|
|
4619
4915
|
async closeTrade(params) {
|
|
4916
|
+
return this.submitPrepared(this.getCloseTradeEncoded(params));
|
|
4917
|
+
}
|
|
4918
|
+
getCloseTradeTx(params) {
|
|
4919
|
+
return this.buildPreparedTx(this.getCloseTradeEncoded(params));
|
|
4920
|
+
}
|
|
4921
|
+
getCloseTradeEncoded(params) {
|
|
4620
4922
|
try {
|
|
4621
4923
|
validateCloseParams({
|
|
4622
4924
|
a: Number(params.pairId),
|
|
@@ -4636,7 +4938,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4636
4938
|
const closePercentage = BigInt(Math.round(params.closePercent * 100));
|
|
4637
4939
|
const marketPrice = parsePrice(params.price);
|
|
4638
4940
|
const slippageP = BigInt(this.resolveSlippage(params.slippage));
|
|
4639
|
-
|
|
4941
|
+
return encodeCloseTradeMarket(
|
|
4640
4942
|
pairIndex,
|
|
4641
4943
|
index,
|
|
4642
4944
|
closePercentage,
|
|
@@ -4644,7 +4946,6 @@ var OstiumClient = class _OstiumClient {
|
|
|
4644
4946
|
slippageP,
|
|
4645
4947
|
this.contracts.trading
|
|
4646
4948
|
);
|
|
4647
|
-
return this.execute(encoded);
|
|
4648
4949
|
}
|
|
4649
4950
|
/**
|
|
4650
4951
|
* Cancel a pending order.
|
|
@@ -4667,6 +4968,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4667
4968
|
* ```
|
|
4668
4969
|
*/
|
|
4669
4970
|
async cancelOrder(params) {
|
|
4971
|
+
return this.submitPrepared(this.getCancelOrderEncoded(params));
|
|
4972
|
+
}
|
|
4973
|
+
getCancelOrderTx(params) {
|
|
4974
|
+
return this.buildPreparedTx(this.getCancelOrderEncoded(params));
|
|
4975
|
+
}
|
|
4976
|
+
getCancelOrderEncoded(params) {
|
|
4670
4977
|
let encoded;
|
|
4671
4978
|
if (params.type === "limit" /* Limit */) {
|
|
4672
4979
|
const pairIdNum = Number(params.pairId);
|
|
@@ -4703,7 +5010,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4703
5010
|
}
|
|
4704
5011
|
encoded = encodeOpenTradeMarketTimeout(BigInt(params.orderId), this.contracts.trading);
|
|
4705
5012
|
}
|
|
4706
|
-
return
|
|
5013
|
+
return encoded;
|
|
4707
5014
|
}
|
|
4708
5015
|
/**
|
|
4709
5016
|
* Modify an open trade or a pending limit order.
|
|
@@ -4728,6 +5035,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4728
5035
|
* - Both `takeProfit` and `stopLoss` without `price` → throws (send two calls).
|
|
4729
5036
|
*/
|
|
4730
5037
|
async modifyOrder(params) {
|
|
5038
|
+
return this.submitPrepared(this.getModifyOrderEncoded(params));
|
|
5039
|
+
}
|
|
5040
|
+
getModifyOrderTx(params) {
|
|
5041
|
+
return this.buildPreparedTx(this.getModifyOrderEncoded(params));
|
|
5042
|
+
}
|
|
5043
|
+
getModifyOrderEncoded(params) {
|
|
4731
5044
|
try {
|
|
4732
5045
|
validateModifyParams({
|
|
4733
5046
|
a: Number(params.pairId),
|
|
@@ -4767,7 +5080,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4767
5080
|
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4768
5081
|
);
|
|
4769
5082
|
}
|
|
4770
|
-
return
|
|
5083
|
+
return encoded;
|
|
4771
5084
|
}
|
|
4772
5085
|
/**
|
|
4773
5086
|
* Update collateral on an open position (isolated margin).
|
|
@@ -4787,6 +5100,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4787
5100
|
* Checks USDC allowance before top-up operations.
|
|
4788
5101
|
*/
|
|
4789
5102
|
async updateCollateral(params) {
|
|
5103
|
+
return this.submitPrepared(await this.getUpdateCollateralEncoded(params));
|
|
5104
|
+
}
|
|
5105
|
+
async getUpdateCollateralTx(params) {
|
|
5106
|
+
return this.buildPreparedTx(await this.getUpdateCollateralEncoded(params));
|
|
5107
|
+
}
|
|
5108
|
+
async getUpdateCollateralEncoded(params) {
|
|
4790
5109
|
const amount = parseFloat(params.amount);
|
|
4791
5110
|
if (Number.isNaN(amount) || amount === 0) {
|
|
4792
5111
|
throw new OstiumError(
|
|
@@ -4810,7 +5129,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4810
5129
|
} else {
|
|
4811
5130
|
encoded = encodeRemoveCollateral(pairIndex, index, absAmount, this.contracts.trading);
|
|
4812
5131
|
}
|
|
4813
|
-
return
|
|
5132
|
+
return encoded;
|
|
4814
5133
|
}
|
|
4815
5134
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4816
5135
|
// Subgraph reads (Hyperliquid-style API)
|
|
@@ -4829,11 +5148,18 @@ var OstiumClient = class _OstiumClient {
|
|
|
4829
5148
|
/**
|
|
4830
5149
|
* Open positions + margin summary for a user. Defaults to the connected
|
|
4831
5150
|
* trader. Live prices and the current block number are fetched automatically.
|
|
5151
|
+
*
|
|
5152
|
+
* - `user`: pass an address to scope to a single trader, or `'ALL'` to fetch
|
|
5153
|
+
* positions across every trader (no trader filter).
|
|
5154
|
+
* - `limit`: cap the number of positions returned (default: all).
|
|
5155
|
+
* - `skip`: offset into the result set for pagination (default: `0`).
|
|
4832
5156
|
*/
|
|
4833
5157
|
async getOpenPositions(params) {
|
|
4834
5158
|
return this.subgraph.getOpenPositions({
|
|
4835
5159
|
user: params?.user ?? this.getTraderAddress(),
|
|
4836
|
-
blockNumber: params?.blockNumber ?? await this.publicClient.getBlockNumber()
|
|
5160
|
+
blockNumber: params?.blockNumber ?? await this.publicClient.getBlockNumber(),
|
|
5161
|
+
limit: params?.limit,
|
|
5162
|
+
skip: params?.skip
|
|
4837
5163
|
});
|
|
4838
5164
|
}
|
|
4839
5165
|
/**
|
|
@@ -4938,31 +5264,104 @@ var OstiumClient = class _OstiumClient {
|
|
|
4938
5264
|
streamPrices(pairIds) {
|
|
4939
5265
|
return this.subgraph.streamPrices(pairIds);
|
|
4940
5266
|
}
|
|
5267
|
+
/**
|
|
5268
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
5269
|
+
*
|
|
5270
|
+
* Subscribes only to the unique pairs referenced by the response and emits the
|
|
5271
|
+
* full updated positions payload on each relevant price update. Pass an
|
|
5272
|
+
* existing `priceStream` to reuse a websocket your app already owns, or omit
|
|
5273
|
+
* it to let the SDK create a dedicated connection.
|
|
5274
|
+
*/
|
|
5275
|
+
streamPositionUpdates(initial, priceStream) {
|
|
5276
|
+
return this.subgraph.streamPositionUpdates(initial, priceStream);
|
|
5277
|
+
}
|
|
4941
5278
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4942
5279
|
// Internal helpers
|
|
4943
5280
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
5281
|
+
getSetupGaslessDelegationEncoded() {
|
|
5282
|
+
if (!this.selfGasless || !this.effectiveSender) {
|
|
5283
|
+
throw new OstiumError(
|
|
5284
|
+
"setupGaslessDelegation() is only available in Self + Gasless mode.",
|
|
5285
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5286
|
+
);
|
|
5287
|
+
}
|
|
5288
|
+
return encodeSetDelegate(this.effectiveSender, this.contracts.trading);
|
|
5289
|
+
}
|
|
5290
|
+
getApproveUsdcEncoded(amount) {
|
|
5291
|
+
if (this.delegated) {
|
|
5292
|
+
throw new OstiumError(
|
|
5293
|
+
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
5294
|
+
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
5295
|
+
);
|
|
5296
|
+
}
|
|
5297
|
+
const rawAmount = amount === "max" ? maxUint256 : parseUsdc(amount);
|
|
5298
|
+
return encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
5299
|
+
}
|
|
5300
|
+
getSetDelegateEncoded(delegateAddress) {
|
|
5301
|
+
return encodeSetDelegate(delegateAddress, this.contracts.trading);
|
|
5302
|
+
}
|
|
5303
|
+
getRemoveDelegateEncoded() {
|
|
5304
|
+
return encodeRemoveDelegate(this.contracts.trading);
|
|
5305
|
+
}
|
|
5306
|
+
prepareEncoded(encoded) {
|
|
5307
|
+
const signer = this.requireBuildCapability();
|
|
5308
|
+
return signer.prepare(encoded);
|
|
5309
|
+
}
|
|
5310
|
+
buildPreparedTx(encoded) {
|
|
5311
|
+
return this.toBuiltTxRequest(this.prepareEncoded(encoded));
|
|
5312
|
+
}
|
|
5313
|
+
buildDirectEoaTx(encoded) {
|
|
5314
|
+
if (!this.canBuildTransactions() || !this.traderAddress) {
|
|
5315
|
+
throw new OstiumError(
|
|
5316
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5317
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5318
|
+
);
|
|
5319
|
+
}
|
|
5320
|
+
return {
|
|
5321
|
+
kind: "eoa",
|
|
5322
|
+
to: encoded.to,
|
|
5323
|
+
data: encoded.data,
|
|
5324
|
+
value: encoded.value,
|
|
5325
|
+
from: this.traderAddress,
|
|
5326
|
+
traderAddress: this.traderAddress
|
|
5327
|
+
};
|
|
5328
|
+
}
|
|
5329
|
+
toBuiltTxRequest(encoded) {
|
|
5330
|
+
if (!this.canBuildTransactions() || !this.traderAddress || !this.effectiveSender) {
|
|
4957
5331
|
throw new OstiumError(
|
|
4958
|
-
"This client
|
|
5332
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4959
5333
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4960
5334
|
);
|
|
4961
5335
|
}
|
|
4962
|
-
|
|
5336
|
+
if (this.gasless) {
|
|
5337
|
+
return {
|
|
5338
|
+
kind: "safe",
|
|
5339
|
+
safeAddress: this.effectiveSender,
|
|
5340
|
+
traderAddress: this.traderAddress,
|
|
5341
|
+
calls: [encoded]
|
|
5342
|
+
};
|
|
5343
|
+
}
|
|
5344
|
+
return {
|
|
5345
|
+
kind: "eoa",
|
|
5346
|
+
to: encoded.to,
|
|
5347
|
+
data: encoded.data,
|
|
5348
|
+
value: encoded.value,
|
|
5349
|
+
from: this.effectiveSender,
|
|
5350
|
+
traderAddress: this.traderAddress
|
|
5351
|
+
};
|
|
5352
|
+
}
|
|
5353
|
+
async submitPrepared(encoded) {
|
|
5354
|
+
const submitter = this.requireSubmitCapability();
|
|
5355
|
+
return submitter.submit(this.prepareEncoded(encoded));
|
|
4963
5356
|
}
|
|
4964
5357
|
/** Direct EOA submission — used only in Self + Gasless for approveUsdc and setupGaslessDelegation. */
|
|
4965
|
-
async
|
|
5358
|
+
async submitDirectEoa(encoded) {
|
|
5359
|
+
if (!this.eoaSubmit) {
|
|
5360
|
+
throw new OstiumError(
|
|
5361
|
+
"This client cannot submit direct EOA transactions.",
|
|
5362
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5363
|
+
);
|
|
5364
|
+
}
|
|
4966
5365
|
try {
|
|
4967
5366
|
return await this.eoaSubmit(encoded);
|
|
4968
5367
|
} catch (err) {
|
|
@@ -4978,11 +5377,29 @@ var OstiumClient = class _OstiumClient {
|
|
|
4978
5377
|
throw new OstiumError(`Transaction failed: ${msg}`, "SUBMISSION_FAILED" /* SUBMISSION_FAILED */, err);
|
|
4979
5378
|
}
|
|
4980
5379
|
}
|
|
5380
|
+
requireBuildCapability() {
|
|
5381
|
+
if (!this.signer) {
|
|
5382
|
+
throw new OstiumError(
|
|
5383
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5384
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5385
|
+
);
|
|
5386
|
+
}
|
|
5387
|
+
return this.signer;
|
|
5388
|
+
}
|
|
5389
|
+
requireSubmitCapability() {
|
|
5390
|
+
if (!this.submitter) {
|
|
5391
|
+
throw new OstiumError(
|
|
5392
|
+
"This client does not have submission credentials. Use the corresponding get*Tx() method or initialize with a private key.",
|
|
5393
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5394
|
+
);
|
|
5395
|
+
}
|
|
5396
|
+
return this.submitter;
|
|
5397
|
+
}
|
|
4981
5398
|
resolveSlippage(override) {
|
|
4982
5399
|
return override ?? this.defaultSlippageBps;
|
|
4983
5400
|
}
|
|
4984
5401
|
};
|
|
4985
5402
|
|
|
4986
|
-
export { CancelOrderType, DEFAULT_BUILDER_API_URL, DEFAULT_SLIPPAGE_PERCENTAGE, DEFAULT_SUBGRAPH_ENDPOINT, DEFAULT_SUBGRAPH_ENDPOINT_TESTNET, MAX_COLLATERAL_USD, MIN_COLLATERAL_USD, OrderType, OstiumClient, OstiumError, OstiumErrorCode, OstiumPriceStream, OstiumSubgraphClient, OstiumSubgraphError, OstiumSubgraphErrorCode, PRECISION_18, PRECISION_6, parseLeverage, parsePrice, parseUsdc };
|
|
5403
|
+
export { CancelOrderType, DEFAULT_BUILDER_API_URL, DEFAULT_SLIPPAGE_PERCENTAGE, DEFAULT_SUBGRAPH_ENDPOINT, DEFAULT_SUBGRAPH_ENDPOINT_TESTNET, MAX_COLLATERAL_USD, MIN_COLLATERAL_USD, OrderType, OstiumClient, OstiumError, OstiumErrorCode, OstiumPositionUpdatesStream, OstiumPriceStream, OstiumSubgraphClient, OstiumSubgraphError, OstiumSubgraphErrorCode, PRECISION_18, PRECISION_6, parseLeverage, parsePrice, parseUsdc };
|
|
4987
5404
|
//# sourceMappingURL=index.js.map
|
|
4988
5405
|
//# sourceMappingURL=index.js.map
|