@ostium/builder-sdk 0.1.0 → 0.2.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 +526 -139
- package/dist/index.cjs +517 -130
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +173 -106
- package/dist/index.d.ts +173 -106
- package/dist/index.js +517 -131
- 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) {
|
|
@@ -3220,10 +3241,11 @@ async function fetchLivePrices(builderApiUrl) {
|
|
|
3220
3241
|
}
|
|
3221
3242
|
return result;
|
|
3222
3243
|
}
|
|
3223
|
-
function rawTickToPublic(item) {
|
|
3244
|
+
function rawTickToPublic(item, pairId) {
|
|
3224
3245
|
const from = normalizePairName(item.from);
|
|
3225
3246
|
const to = normalizePairName(item.to);
|
|
3226
3247
|
return {
|
|
3248
|
+
pairId,
|
|
3227
3249
|
feedId: item.feed_id,
|
|
3228
3250
|
pair: `${from}-${to}`,
|
|
3229
3251
|
from,
|
|
@@ -3270,9 +3292,12 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3270
3292
|
snapshotHandlers = /* @__PURE__ */ new Set();
|
|
3271
3293
|
/** pairId (string) → raw "FROM-TO" name for the WS API. */
|
|
3272
3294
|
pairRawNameCache;
|
|
3273
|
-
|
|
3295
|
+
/** raw "FROM-TO" name → pairId (string) for mapping incoming ticks back to SDK ids. */
|
|
3296
|
+
rawNamePairIdCache;
|
|
3297
|
+
constructor(ws, pairRawNameCache, rawNamePairIdCache, initialSubscribeRawPairs) {
|
|
3274
3298
|
this.ws = ws;
|
|
3275
3299
|
this.pairRawNameCache = pairRawNameCache;
|
|
3300
|
+
this.rawNamePairIdCache = rawNamePairIdCache;
|
|
3276
3301
|
if (initialSubscribeRawPairs?.length) {
|
|
3277
3302
|
ws.once("open", () => {
|
|
3278
3303
|
ws.send(JSON.stringify({ type: "subscribe", pairs: initialSubscribeRawPairs }));
|
|
@@ -3286,10 +3311,13 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3286
3311
|
return;
|
|
3287
3312
|
}
|
|
3288
3313
|
if (msg.type === "snapshot" && Array.isArray(msg.data)) {
|
|
3289
|
-
const ticks = msg.data.map(
|
|
3314
|
+
const ticks = msg.data.map(
|
|
3315
|
+
(item) => rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`))
|
|
3316
|
+
);
|
|
3290
3317
|
for (const h of this.snapshotHandlers) h(ticks);
|
|
3291
3318
|
} else if (msg.type === "tick" && msg.data) {
|
|
3292
|
-
const
|
|
3319
|
+
const item = msg.data;
|
|
3320
|
+
const tick = rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`));
|
|
3293
3321
|
for (const h of this.tickHandlers) h(tick);
|
|
3294
3322
|
}
|
|
3295
3323
|
});
|
|
@@ -3315,7 +3343,11 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3315
3343
|
const ws = new WS(url, {
|
|
3316
3344
|
headers: { "User-Agent": "ostium-sdk", "Accept": "*/*" }
|
|
3317
3345
|
});
|
|
3318
|
-
|
|
3346
|
+
const rawNamePairIdCache = /* @__PURE__ */ new Map();
|
|
3347
|
+
for (const [pairId, rawName] of pairRawNameCache.entries()) {
|
|
3348
|
+
rawNamePairIdCache.set(rawName, pairId);
|
|
3349
|
+
}
|
|
3350
|
+
return new _OstiumPriceStream(ws, pairRawNameCache, rawNamePairIdCache, initial);
|
|
3319
3351
|
}
|
|
3320
3352
|
/**
|
|
3321
3353
|
* Register a callback that fires on every incoming price tick.
|
|
@@ -3391,6 +3423,37 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3391
3423
|
}
|
|
3392
3424
|
};
|
|
3393
3425
|
|
|
3426
|
+
// src/data/internal/aggregations.ts
|
|
3427
|
+
function aggregateMarginSummary(pairPositions) {
|
|
3428
|
+
let accountValue = 0;
|
|
3429
|
+
let totalCollateralUsed = 0;
|
|
3430
|
+
let totalNtlPos = 0;
|
|
3431
|
+
let totalRawPnlUsd = 0;
|
|
3432
|
+
let totalCumRollover = 0;
|
|
3433
|
+
let totalWithdrawable = 0;
|
|
3434
|
+
for (const { position } of pairPositions) {
|
|
3435
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3436
|
+
const ntl = parseFloat(position.ntl) || 0;
|
|
3437
|
+
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
3438
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3439
|
+
const withdrawable = parseFloat(position.maxWithdrawable) || 0;
|
|
3440
|
+
accountValue += collateral + pnl;
|
|
3441
|
+
totalCollateralUsed += collateral;
|
|
3442
|
+
totalNtlPos += ntl;
|
|
3443
|
+
totalRawPnlUsd += pnl;
|
|
3444
|
+
totalCumRollover += rollover;
|
|
3445
|
+
totalWithdrawable += withdrawable;
|
|
3446
|
+
}
|
|
3447
|
+
return {
|
|
3448
|
+
accountValue: accountValue.toString(),
|
|
3449
|
+
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
3450
|
+
totalNtlPos: totalNtlPos.toString(),
|
|
3451
|
+
totalRawPnlUsd: totalRawPnlUsd.toString(),
|
|
3452
|
+
totalCumRollover: totalCumRollover.toString(),
|
|
3453
|
+
totalWithdrawable: totalWithdrawable.toString()
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
|
|
3394
3457
|
// src/data/internal/formatters.ts
|
|
3395
3458
|
var MIN_NOTIONAL = "5.0";
|
|
3396
3459
|
var MIN_NOTIONAL_NUM = 5;
|
|
@@ -3492,14 +3555,14 @@ function formatPosition(raw, price, pnl, maxLeverage) {
|
|
|
3492
3555
|
returnOnEquity: roe.toString(),
|
|
3493
3556
|
liquidationPx: pnl.liquidationPrice.toString(),
|
|
3494
3557
|
collateralUsed: collateral.toString(),
|
|
3495
|
-
cumRollover: pnl.rollover.toString(),
|
|
3558
|
+
cumRollover: (pnl.rollover * -1).toString(),
|
|
3496
3559
|
...raw.takeProfitPrice && raw.takeProfitPrice !== "0" ? { tpPx: formatTokens(raw.takeProfitPrice).toString() } : {},
|
|
3497
3560
|
...raw.stopLossPrice && raw.stopLossPrice !== "0" ? { slPx: formatTokens(raw.stopLossPrice).toString() } : {},
|
|
3498
3561
|
openTimestamp: parseInt(raw.timestamp || "0") * 1e3,
|
|
3499
3562
|
isDayTrade: raw.isDayTrade ?? false,
|
|
3500
|
-
maxLeverage: maxLeverage.toString()
|
|
3501
|
-
|
|
3502
|
-
|
|
3563
|
+
maxLeverage: maxLeverage.toString(),
|
|
3564
|
+
maxWithdrawable: maxWithdrawForPosition(collateral, leverage, maxLeverage).toString()
|
|
3565
|
+
}
|
|
3503
3566
|
};
|
|
3504
3567
|
}
|
|
3505
3568
|
function formatFill(raw) {
|
|
@@ -3576,6 +3639,131 @@ function formatOpenOrder(raw) {
|
|
|
3576
3639
|
timestamp: parseInt(raw.initiatedAt || "0") * 1e3
|
|
3577
3640
|
};
|
|
3578
3641
|
}
|
|
3642
|
+
|
|
3643
|
+
// src/data/internal/position-updates.ts
|
|
3644
|
+
function cloneResponse(response) {
|
|
3645
|
+
return {
|
|
3646
|
+
pairPositions: response.pairPositions.map((pairPos) => ({
|
|
3647
|
+
position: { ...pairPos.position }
|
|
3648
|
+
})),
|
|
3649
|
+
marginSummary: { ...response.marginSummary },
|
|
3650
|
+
time: response.time
|
|
3651
|
+
};
|
|
3652
|
+
}
|
|
3653
|
+
function applyTickToPosition(position, tick) {
|
|
3654
|
+
const size = parseFloat(position.szi) || 0;
|
|
3655
|
+
const entry = parseFloat(position.entryPx) || 0;
|
|
3656
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3657
|
+
const maxLeverage = parseFloat(position.maxLeverage) || 0;
|
|
3658
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3659
|
+
const isLong = position.side === "B";
|
|
3660
|
+
const ntl = size * tick.mid;
|
|
3661
|
+
const rawPnl = isLong ? (tick.mid - entry) * size : (entry - tick.mid) * size;
|
|
3662
|
+
const netPnl = rawPnl + rollover;
|
|
3663
|
+
const roe = collateral > 0 ? netPnl / collateral : 0;
|
|
3664
|
+
const currentLeverage = collateral > 0 ? ntl / collateral : 0;
|
|
3665
|
+
const maxWithdrawable = maxWithdrawForPosition(collateral, currentLeverage, maxLeverage);
|
|
3666
|
+
return {
|
|
3667
|
+
...position,
|
|
3668
|
+
ntl: ntl.toString(),
|
|
3669
|
+
unrealizedPnl: netPnl.toString(),
|
|
3670
|
+
returnOnEquity: roe.toString(),
|
|
3671
|
+
maxWithdrawable: maxWithdrawable.toString()
|
|
3672
|
+
};
|
|
3673
|
+
}
|
|
3674
|
+
function updateResponseWithTick(current, tick, timestampMs) {
|
|
3675
|
+
if (!tick.pairId) return current;
|
|
3676
|
+
let changed = false;
|
|
3677
|
+
const pairPositions = current.pairPositions.map((pairPos) => {
|
|
3678
|
+
if (String(pairPos.position.pairId) !== String(tick.pairId)) {
|
|
3679
|
+
return pairPos;
|
|
3680
|
+
}
|
|
3681
|
+
changed = true;
|
|
3682
|
+
return {
|
|
3683
|
+
...pairPos,
|
|
3684
|
+
position: applyTickToPosition(pairPos.position, tick)
|
|
3685
|
+
};
|
|
3686
|
+
});
|
|
3687
|
+
if (!changed) return current;
|
|
3688
|
+
return {
|
|
3689
|
+
pairPositions,
|
|
3690
|
+
marginSummary: aggregateMarginSummary(pairPositions),
|
|
3691
|
+
time: timestampMs
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3694
|
+
var OstiumPositionUpdatesStream = class {
|
|
3695
|
+
priceStream;
|
|
3696
|
+
updateHandlers = /* @__PURE__ */ new Set();
|
|
3697
|
+
current;
|
|
3698
|
+
trackedPairIds;
|
|
3699
|
+
constructor(initial, trackedPairIds, priceStream) {
|
|
3700
|
+
this.current = cloneResponse(initial);
|
|
3701
|
+
this.priceStream = priceStream;
|
|
3702
|
+
this.trackedPairIds = new Set(trackedPairIds);
|
|
3703
|
+
if (!priceStream) return;
|
|
3704
|
+
priceStream.onSnapshot((ticks) => {
|
|
3705
|
+
this.ingestSnapshot(ticks);
|
|
3706
|
+
});
|
|
3707
|
+
priceStream.onTick((tick) => {
|
|
3708
|
+
this.ingestTick(tick);
|
|
3709
|
+
});
|
|
3710
|
+
}
|
|
3711
|
+
onUpdate(handler) {
|
|
3712
|
+
this.updateHandlers.add(handler);
|
|
3713
|
+
return () => {
|
|
3714
|
+
this.updateHandlers.delete(handler);
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
onOpen(handler) {
|
|
3718
|
+
this.priceStream?.onOpen(handler);
|
|
3719
|
+
return this;
|
|
3720
|
+
}
|
|
3721
|
+
onError(handler) {
|
|
3722
|
+
this.priceStream?.onError(handler);
|
|
3723
|
+
return this;
|
|
3724
|
+
}
|
|
3725
|
+
onClose(handler) {
|
|
3726
|
+
this.priceStream?.onClose(handler);
|
|
3727
|
+
return this;
|
|
3728
|
+
}
|
|
3729
|
+
getCurrent() {
|
|
3730
|
+
return cloneResponse(this.current);
|
|
3731
|
+
}
|
|
3732
|
+
/**
|
|
3733
|
+
* Apply a single externally sourced price tick to the tracked positions.
|
|
3734
|
+
*
|
|
3735
|
+
* Use this when your app already has its own websocket connection and you want
|
|
3736
|
+
* the SDK to handle only the position recalculation logic.
|
|
3737
|
+
*/
|
|
3738
|
+
ingestTick(tick) {
|
|
3739
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) return;
|
|
3740
|
+
const next = updateResponseWithTick(this.current, tick, tick.timestampSeconds * 1e3);
|
|
3741
|
+
if (next !== this.current) {
|
|
3742
|
+
this.current = next;
|
|
3743
|
+
this.emit(next);
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
/**
|
|
3747
|
+
* Apply a batch of externally sourced ticks, such as a websocket snapshot.
|
|
3748
|
+
*/
|
|
3749
|
+
ingestSnapshot(ticks) {
|
|
3750
|
+
let next = this.current;
|
|
3751
|
+
for (const tick of ticks) {
|
|
3752
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) continue;
|
|
3753
|
+
next = updateResponseWithTick(next, tick, tick.timestampSeconds * 1e3);
|
|
3754
|
+
}
|
|
3755
|
+
if (next !== this.current) {
|
|
3756
|
+
this.current = next;
|
|
3757
|
+
this.emit(next);
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
close() {
|
|
3761
|
+
this.priceStream?.close();
|
|
3762
|
+
}
|
|
3763
|
+
emit(positions) {
|
|
3764
|
+
for (const handler of this.updateHandlers) handler(cloneResponse(positions));
|
|
3765
|
+
}
|
|
3766
|
+
};
|
|
3579
3767
|
var EMPTY_RESULT = { long: [], short: [] };
|
|
3580
3768
|
var ORDERBOOK_MAX_LEVELS = 20;
|
|
3581
3769
|
function generateLogSpacedNotionals(levels, capacity) {
|
|
@@ -3771,20 +3959,16 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
3771
3959
|
}),
|
|
3772
3960
|
this.fetchLivePricesSafe()
|
|
3773
3961
|
]);
|
|
3774
|
-
let totalWithdrawable = 0;
|
|
3775
3962
|
const pairPositions = trades.map((trade) => {
|
|
3776
3963
|
const price = prices[`${trade.pair.from}/${trade.pair.to}`];
|
|
3777
3964
|
const pnl = price && blockNumber ? getTradePnL(trade, price, blockNumber) : EMPTY_PNL;
|
|
3778
3965
|
const maxLev = trade.isDayTrade ? formatLeverage(trade.pair.overnightMaxLeverage) : pairMaxLeverage(trade.pair);
|
|
3779
|
-
|
|
3780
|
-
totalWithdrawable += parseFloat(pairPos.maxWithdrawable);
|
|
3781
|
-
return pairPos;
|
|
3966
|
+
return formatPosition(trade, price, pnl, maxLev);
|
|
3782
3967
|
});
|
|
3783
3968
|
const marginSummary = aggregateMarginSummary(pairPositions);
|
|
3784
3969
|
return {
|
|
3785
3970
|
pairPositions,
|
|
3786
3971
|
marginSummary,
|
|
3787
|
-
withdrawable: totalWithdrawable.toString(),
|
|
3788
3972
|
time: Date.now()
|
|
3789
3973
|
};
|
|
3790
3974
|
}
|
|
@@ -4035,6 +4219,24 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4035
4219
|
});
|
|
4036
4220
|
return OstiumPriceStream.connect(this.builderApiUrl, rawNames, cache);
|
|
4037
4221
|
}
|
|
4222
|
+
/**
|
|
4223
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
4224
|
+
*
|
|
4225
|
+
* The SDK subscribes only to the unique pairs present in `initial.pairPositions`,
|
|
4226
|
+
* recalculates price-sensitive fields for affected positions on each tick, and
|
|
4227
|
+
* emits the full updated response.
|
|
4228
|
+
*
|
|
4229
|
+
* Pass an existing `priceStream` to reuse a websocket connection your app has
|
|
4230
|
+
* already opened. Omit it to let the SDK open and manage a dedicated
|
|
4231
|
+
* connection for the tracked pair ids.
|
|
4232
|
+
*/
|
|
4233
|
+
streamPositionUpdates(initial, priceStream) {
|
|
4234
|
+
const pairIds = [...new Set(initial.pairPositions.map(({ position }) => String(position.pairId)))];
|
|
4235
|
+
if (pairIds.length === 0) {
|
|
4236
|
+
return new OstiumPositionUpdatesStream(initial, []);
|
|
4237
|
+
}
|
|
4238
|
+
return new OstiumPositionUpdatesStream(initial, pairIds, priceStream ?? this.streamPrices(pairIds));
|
|
4239
|
+
}
|
|
4038
4240
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4039
4241
|
// Internal — fetchers, cache, query wrapper
|
|
4040
4242
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -4093,43 +4295,28 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4093
4295
|
}
|
|
4094
4296
|
}
|
|
4095
4297
|
};
|
|
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
4298
|
|
|
4118
4299
|
// src/client.ts
|
|
4119
4300
|
var HEX64_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
4120
4301
|
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
4121
4302
|
function validateConfig(config) {
|
|
4122
|
-
if (!HEX64_RE.test(config.privateKey)) {
|
|
4303
|
+
if (config.privateKey && !HEX64_RE.test(config.privateKey)) {
|
|
4123
4304
|
throw new OstiumError(
|
|
4124
4305
|
"privateKey must be a 64-character hex string prefixed with 0x",
|
|
4125
4306
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4126
4307
|
);
|
|
4127
4308
|
}
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4309
|
+
for (const [label, value] of [
|
|
4310
|
+
["traderAddress", config.traderAddress],
|
|
4311
|
+
["delegateAddress", config.delegateAddress],
|
|
4312
|
+
["safeAddress", config.safeAddress]
|
|
4313
|
+
]) {
|
|
4314
|
+
if (value && !ADDRESS_RE.test(value)) {
|
|
4315
|
+
throw new OstiumError(
|
|
4316
|
+
`${label} must be a valid 42-character Ethereum address (0x + 40 hex chars)`,
|
|
4317
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4318
|
+
);
|
|
4319
|
+
}
|
|
4133
4320
|
}
|
|
4134
4321
|
if (config.slippageBps !== void 0 && (config.slippageBps < 0 || config.slippageBps > 500)) {
|
|
4135
4322
|
throw new OstiumError(
|
|
@@ -4143,10 +4330,45 @@ function validateConfig(config) {
|
|
|
4143
4330
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4144
4331
|
);
|
|
4145
4332
|
}
|
|
4333
|
+
let mode;
|
|
4334
|
+
try {
|
|
4335
|
+
mode = resolveMode(config);
|
|
4336
|
+
} catch {
|
|
4337
|
+
throw new OstiumError(
|
|
4338
|
+
"Could not infer client mode from config. Provide the required addresses for the selected factory.",
|
|
4339
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4340
|
+
);
|
|
4341
|
+
}
|
|
4342
|
+
if (mode === "self-self" && !config.privateKey && !config.traderAddress) {
|
|
4343
|
+
throw new OstiumError(
|
|
4344
|
+
"Self + Self mode requires traderPrivateKey for submission or traderAddress for build-only usage.",
|
|
4345
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4346
|
+
);
|
|
4347
|
+
}
|
|
4348
|
+
if (mode === "self-gasless" && !config.privateKey && (!config.traderAddress || !config.safeAddress)) {
|
|
4349
|
+
throw new OstiumError(
|
|
4350
|
+
"Self + Gasless build-only mode requires traderAddress and safeAddress.",
|
|
4351
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4352
|
+
);
|
|
4353
|
+
}
|
|
4354
|
+
if (mode === "delegated-self" && (!config.traderAddress || !config.privateKey && !config.delegateAddress)) {
|
|
4355
|
+
throw new OstiumError(
|
|
4356
|
+
"Delegated + Self mode requires traderAddress and either delegatePrivateKey or delegateAddress.",
|
|
4357
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
if (mode === "delegated-gasless" && (!config.traderAddress || !config.privateKey && (!config.delegateAddress || !config.safeAddress))) {
|
|
4361
|
+
throw new OstiumError(
|
|
4362
|
+
"Delegated + Gasless mode requires traderAddress and either delegatePrivateKey or both delegateAddress and safeAddress.",
|
|
4363
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4146
4366
|
}
|
|
4147
4367
|
var OstiumClient = class _OstiumClient {
|
|
4148
4368
|
signer;
|
|
4149
4369
|
submitter;
|
|
4370
|
+
traderAddress;
|
|
4371
|
+
effectiveSender;
|
|
4150
4372
|
contracts;
|
|
4151
4373
|
publicClient;
|
|
4152
4374
|
defaultSlippageBps;
|
|
@@ -4161,6 +4383,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4161
4383
|
constructor(internals) {
|
|
4162
4384
|
this.signer = internals.signer;
|
|
4163
4385
|
this.submitter = internals.submitter;
|
|
4386
|
+
this.traderAddress = internals.traderAddress;
|
|
4387
|
+
this.effectiveSender = internals.effectiveSender;
|
|
4164
4388
|
this.contracts = internals.contracts;
|
|
4165
4389
|
this.publicClient = internals.publicClient;
|
|
4166
4390
|
this.defaultSlippageBps = internals.defaultSlippageBps;
|
|
@@ -4185,17 +4409,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4185
4409
|
* });
|
|
4186
4410
|
* await client.openTrade({ ... });
|
|
4187
4411
|
* ```
|
|
4188
|
-
|
|
4412
|
+
*/
|
|
4189
4413
|
static async createSelfAndSelf(params) {
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
rpcUrl: params.rpcUrl,
|
|
4414
|
+
const base = {
|
|
4415
|
+
mode: "self-self",
|
|
4193
4416
|
testnet: params.testnet,
|
|
4194
4417
|
slippageBps: params.slippageBps,
|
|
4195
4418
|
builder: params.builder,
|
|
4196
4419
|
subgraphUrl: params.subgraphUrl,
|
|
4197
|
-
builderApiUrl: params.builderApiUrl
|
|
4198
|
-
|
|
4420
|
+
builderApiUrl: params.builderApiUrl,
|
|
4421
|
+
rpcUrl: params.rpcUrl
|
|
4422
|
+
};
|
|
4423
|
+
return _OstiumClient._fromConfig(
|
|
4424
|
+
"traderPrivateKey" in params ? { ...base, privateKey: params.traderPrivateKey } : { ...base, traderAddress: params.traderAddress }
|
|
4425
|
+
);
|
|
4199
4426
|
}
|
|
4200
4427
|
/**
|
|
4201
4428
|
* **Self + Gasless** — your EOA owns everything; a Safe relays trades for free.
|
|
@@ -4219,17 +4446,27 @@ var OstiumClient = class _OstiumClient {
|
|
|
4219
4446
|
*/
|
|
4220
4447
|
static async createSelfAndGasless(params) {
|
|
4221
4448
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4449
|
+
const base = {
|
|
4450
|
+
mode: "self-gasless",
|
|
4225
4451
|
rpcUrl: params.rpcUrl,
|
|
4226
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4227
4452
|
testnet: params.testnet,
|
|
4228
4453
|
slippageBps: params.slippageBps,
|
|
4229
4454
|
builder: params.builder,
|
|
4230
4455
|
subgraphUrl: params.subgraphUrl,
|
|
4231
4456
|
builderApiUrl: params.builderApiUrl
|
|
4232
|
-
}
|
|
4457
|
+
};
|
|
4458
|
+
return _OstiumClient._fromConfig(
|
|
4459
|
+
"traderPrivateKey" in params ? {
|
|
4460
|
+
...base,
|
|
4461
|
+
privateKey: params.traderPrivateKey,
|
|
4462
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4463
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4464
|
+
} : {
|
|
4465
|
+
...base,
|
|
4466
|
+
traderAddress: params.traderAddress,
|
|
4467
|
+
safeAddress: params.safeAddress
|
|
4468
|
+
}
|
|
4469
|
+
);
|
|
4233
4470
|
}
|
|
4234
4471
|
/**
|
|
4235
4472
|
* **Delegated + Self** — a delegate EOA signs and pays gas on behalf of a trader address.
|
|
@@ -4249,8 +4486,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4249
4486
|
* ```
|
|
4250
4487
|
*/
|
|
4251
4488
|
static async createDelegatedAndSelf(params) {
|
|
4252
|
-
|
|
4253
|
-
|
|
4489
|
+
const base = {
|
|
4490
|
+
mode: "delegated-self",
|
|
4254
4491
|
traderAddress: params.traderAddress,
|
|
4255
4492
|
rpcUrl: params.rpcUrl,
|
|
4256
4493
|
testnet: params.testnet,
|
|
@@ -4258,7 +4495,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4258
4495
|
builder: params.builder,
|
|
4259
4496
|
subgraphUrl: params.subgraphUrl,
|
|
4260
4497
|
builderApiUrl: params.builderApiUrl
|
|
4261
|
-
}
|
|
4498
|
+
};
|
|
4499
|
+
return _OstiumClient._fromConfig(
|
|
4500
|
+
"delegatePrivateKey" in params ? { ...base, privateKey: params.delegatePrivateKey } : { ...base, delegateAddress: params.delegateAddress }
|
|
4501
|
+
);
|
|
4262
4502
|
}
|
|
4263
4503
|
/**
|
|
4264
4504
|
* **Delegated + Gasless** — a Safe derived from the delegate key submits sponsored
|
|
@@ -4279,18 +4519,28 @@ var OstiumClient = class _OstiumClient {
|
|
|
4279
4519
|
*/
|
|
4280
4520
|
static async createDelegatedAndGasless(params) {
|
|
4281
4521
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4282
|
-
|
|
4283
|
-
|
|
4522
|
+
const base = {
|
|
4523
|
+
mode: "delegated-gasless",
|
|
4284
4524
|
traderAddress: params.traderAddress,
|
|
4285
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4286
4525
|
rpcUrl: params.rpcUrl,
|
|
4287
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4288
4526
|
testnet: params.testnet,
|
|
4289
4527
|
slippageBps: params.slippageBps,
|
|
4290
4528
|
builder: params.builder,
|
|
4291
4529
|
subgraphUrl: params.subgraphUrl,
|
|
4292
4530
|
builderApiUrl: params.builderApiUrl
|
|
4293
|
-
}
|
|
4531
|
+
};
|
|
4532
|
+
return _OstiumClient._fromConfig(
|
|
4533
|
+
"delegatePrivateKey" in params ? {
|
|
4534
|
+
...base,
|
|
4535
|
+
privateKey: params.delegatePrivateKey,
|
|
4536
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4537
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4538
|
+
} : {
|
|
4539
|
+
...base,
|
|
4540
|
+
delegateAddress: params.delegateAddress,
|
|
4541
|
+
safeAddress: params.safeAddress
|
|
4542
|
+
}
|
|
4543
|
+
);
|
|
4294
4544
|
}
|
|
4295
4545
|
/**
|
|
4296
4546
|
* **Read-only client** — no signer, no submitter, no privateKey required.
|
|
@@ -4335,6 +4585,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4335
4585
|
/** Internal factory shared by all public constructors. */
|
|
4336
4586
|
static async _fromConfig(config) {
|
|
4337
4587
|
validateConfig(config);
|
|
4588
|
+
const mode = resolveMode(config);
|
|
4338
4589
|
const testnet = config.testnet ?? false;
|
|
4339
4590
|
const networkConfig = getNetworkConfig(testnet);
|
|
4340
4591
|
const chain = testnet ? arbitrumSepolia : arbitrum;
|
|
@@ -4344,18 +4595,29 @@ var OstiumClient = class _OstiumClient {
|
|
|
4344
4595
|
tradingStorage: networkConfig.contracts.tradingStorage,
|
|
4345
4596
|
usdc: networkConfig.contracts.usdc
|
|
4346
4597
|
};
|
|
4347
|
-
const selfGasless =
|
|
4348
|
-
const submitter = await createSubmitter(config, testnet);
|
|
4598
|
+
const selfGasless = mode === "self-gasless";
|
|
4349
4599
|
let traderAddress;
|
|
4350
|
-
if (
|
|
4600
|
+
if (mode === "self-self" || mode === "self-gasless") {
|
|
4601
|
+
traderAddress = config.traderAddress ?? privateKeyToAccount(config.privateKey).address;
|
|
4602
|
+
} else {
|
|
4351
4603
|
traderAddress = config.traderAddress;
|
|
4604
|
+
}
|
|
4605
|
+
const signer = createSigner(mode, traderAddress);
|
|
4606
|
+
let submitter;
|
|
4607
|
+
let effectiveSender;
|
|
4608
|
+
if (hasPrivateKey(config)) {
|
|
4609
|
+
submitter = await createSubmitter(config, testnet, mode);
|
|
4610
|
+
effectiveSender = submitter.effectiveSender;
|
|
4611
|
+
} else if (mode === "self-self") {
|
|
4612
|
+
effectiveSender = traderAddress;
|
|
4613
|
+
} else if (mode === "delegated-self") {
|
|
4614
|
+
effectiveSender = config.delegateAddress;
|
|
4352
4615
|
} else {
|
|
4353
|
-
|
|
4616
|
+
effectiveSender = config.safeAddress;
|
|
4354
4617
|
}
|
|
4355
|
-
const signer = createSigner(config, traderAddress, selfGasless);
|
|
4356
4618
|
const publicRpc = testnet ? "https://sepolia-rollup.arbitrum.io/rpc" : "https://arb1.arbitrum.io/rpc";
|
|
4357
|
-
const rpcUrl = config.rpcUrl ??
|
|
4358
|
-
if (
|
|
4619
|
+
const rpcUrl = config.rpcUrl ?? publicRpc;
|
|
4620
|
+
if (hasPrivateKey(config) && mode !== "self-gasless" && mode !== "delegated-gasless" && !config.rpcUrl) {
|
|
4359
4621
|
throw new OstiumError(
|
|
4360
4622
|
"rpcUrl is required for self-submission mode",
|
|
4361
4623
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
@@ -4363,7 +4625,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4363
4625
|
}
|
|
4364
4626
|
const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });
|
|
4365
4627
|
let eoaSubmit;
|
|
4366
|
-
if (selfGasless) {
|
|
4628
|
+
if (selfGasless && hasPrivateKey(config)) {
|
|
4367
4629
|
const eoaAccount = privateKeyToAccount(config.privateKey);
|
|
4368
4630
|
const eoaRpcUrl = config.rpcUrl ?? publicRpc;
|
|
4369
4631
|
const eoaWalletClient = createWalletClient({ account: eoaAccount, chain, transport: http(eoaRpcUrl) });
|
|
@@ -4385,11 +4647,13 @@ var OstiumClient = class _OstiumClient {
|
|
|
4385
4647
|
return new _OstiumClient({
|
|
4386
4648
|
signer,
|
|
4387
4649
|
submitter,
|
|
4650
|
+
traderAddress,
|
|
4651
|
+
effectiveSender,
|
|
4388
4652
|
contracts,
|
|
4389
4653
|
publicClient,
|
|
4390
4654
|
defaultSlippageBps,
|
|
4391
|
-
delegated:
|
|
4392
|
-
gasless:
|
|
4655
|
+
delegated: mode === "delegated-self" || mode === "delegated-gasless",
|
|
4656
|
+
gasless: mode === "self-gasless" || mode === "delegated-gasless",
|
|
4393
4657
|
selfGasless,
|
|
4394
4658
|
eoaSubmit,
|
|
4395
4659
|
builderAddress: config.builder?.address,
|
|
@@ -4407,17 +4671,25 @@ var OstiumClient = class _OstiumClient {
|
|
|
4407
4671
|
* - Self + Gasless: EOA derived from privateKey (NOT the Safe — USDC lives here).
|
|
4408
4672
|
*/
|
|
4409
4673
|
getTraderAddress() {
|
|
4410
|
-
if (!this.
|
|
4674
|
+
if (!this.traderAddress) {
|
|
4411
4675
|
throw new OstiumError(
|
|
4412
4676
|
"No connected trader \u2014 pass `user` explicitly or use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4413
4677
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4414
4678
|
);
|
|
4415
4679
|
}
|
|
4416
|
-
return this.
|
|
4680
|
+
return this.traderAddress;
|
|
4417
4681
|
}
|
|
4418
|
-
/** True when the client
|
|
4682
|
+
/** True when the client cannot submit transactions directly. */
|
|
4419
4683
|
isReadOnly() {
|
|
4420
|
-
return !this.
|
|
4684
|
+
return !this.submitter;
|
|
4685
|
+
}
|
|
4686
|
+
/** True when the client can build mode-correct unsigned transaction requests. */
|
|
4687
|
+
canBuildTransactions() {
|
|
4688
|
+
return !!this.signer && !!this.effectiveSender;
|
|
4689
|
+
}
|
|
4690
|
+
/** True when the client has the credentials needed for SDK-managed submission. */
|
|
4691
|
+
canSubmitTransactions() {
|
|
4692
|
+
return !!this.submitter;
|
|
4421
4693
|
}
|
|
4422
4694
|
/**
|
|
4423
4695
|
* Returns the Safe smart-account address used for gasless submission.
|
|
@@ -4429,7 +4701,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4429
4701
|
* - Self + Self / Delegated + Self: returns undefined.
|
|
4430
4702
|
*/
|
|
4431
4703
|
getSmartAccountAddress() {
|
|
4432
|
-
return this.gasless
|
|
4704
|
+
return this.gasless ? this.effectiveSender : void 0;
|
|
4433
4705
|
}
|
|
4434
4706
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4435
4707
|
// Self + Gasless setup
|
|
@@ -4453,15 +4725,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4453
4725
|
* ```
|
|
4454
4726
|
*/
|
|
4455
4727
|
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);
|
|
4728
|
+
return this.submitDirectEoa(this.getSetupGaslessDelegationEncoded());
|
|
4729
|
+
}
|
|
4730
|
+
getSetupGaslessDelegationTx() {
|
|
4731
|
+
return this.buildDirectEoaTx(this.getSetupGaslessDelegationEncoded());
|
|
4465
4732
|
}
|
|
4466
4733
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4467
4734
|
// USDC helpers
|
|
@@ -4523,18 +4790,14 @@ var OstiumClient = class _OstiumClient {
|
|
|
4523
4790
|
* @param amount USD amount as decimal string (e.g. "1000"), or "max" for MaxUint256.
|
|
4524
4791
|
*/
|
|
4525
4792
|
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);
|
|
4793
|
+
const encoded = this.getApproveUsdcEncoded(amount);
|
|
4534
4794
|
if (this.selfGasless) {
|
|
4535
|
-
return this.
|
|
4795
|
+
return this.submitDirectEoa(encoded);
|
|
4536
4796
|
}
|
|
4537
|
-
return this.
|
|
4797
|
+
return this.submitPrepared(encoded);
|
|
4798
|
+
}
|
|
4799
|
+
getApproveUsdcTx(amount) {
|
|
4800
|
+
return this.buildDirectEoaTx(this.getApproveUsdcEncoded(amount));
|
|
4538
4801
|
}
|
|
4539
4802
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4540
4803
|
// Delegation helpers
|
|
@@ -4548,16 +4811,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4548
4811
|
* update the trader's delegation on their behalf.
|
|
4549
4812
|
*/
|
|
4550
4813
|
async setDelegate(delegateAddress) {
|
|
4551
|
-
|
|
4552
|
-
|
|
4814
|
+
return this.submitPrepared(this.getSetDelegateEncoded(delegateAddress));
|
|
4815
|
+
}
|
|
4816
|
+
getSetDelegateTx(delegateAddress) {
|
|
4817
|
+
return this.buildPreparedTx(this.getSetDelegateEncoded(delegateAddress));
|
|
4553
4818
|
}
|
|
4554
4819
|
/**
|
|
4555
4820
|
* Remove the current delegate on the trading contract.
|
|
4556
4821
|
* Follows the same delegation-wrapping rules as setDelegate().
|
|
4557
4822
|
*/
|
|
4558
4823
|
async removeDelegate() {
|
|
4559
|
-
|
|
4560
|
-
|
|
4824
|
+
return this.submitPrepared(this.getRemoveDelegateEncoded());
|
|
4825
|
+
}
|
|
4826
|
+
getRemoveDelegateTx() {
|
|
4827
|
+
return this.buildPreparedTx(this.getRemoveDelegateEncoded());
|
|
4561
4828
|
}
|
|
4562
4829
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4563
4830
|
// Core trading methods
|
|
@@ -4570,6 +4837,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4570
4837
|
* the current allowance.
|
|
4571
4838
|
*/
|
|
4572
4839
|
async openTrade(params) {
|
|
4840
|
+
return this.submitPrepared(this.getOpenTradeEncoded(params));
|
|
4841
|
+
}
|
|
4842
|
+
getOpenTradeTx(params) {
|
|
4843
|
+
return this.buildPreparedTx(this.getOpenTradeEncoded(params));
|
|
4844
|
+
}
|
|
4845
|
+
getOpenTradeEncoded(params) {
|
|
4573
4846
|
const collateralNum = parseFloat(params.collateral);
|
|
4574
4847
|
if (collateralNum > MAX_COLLATERAL_USD) {
|
|
4575
4848
|
throw new OstiumError(
|
|
@@ -4578,7 +4851,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4578
4851
|
);
|
|
4579
4852
|
}
|
|
4580
4853
|
const apiOpen = {
|
|
4581
|
-
a: params.
|
|
4854
|
+
a: Number(params.pairId),
|
|
4582
4855
|
b: params.buy,
|
|
4583
4856
|
p: params.price,
|
|
4584
4857
|
s: params.collateral,
|
|
@@ -4601,8 +4874,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4601
4874
|
}
|
|
4602
4875
|
const builderFee = buildBuilderFee(apiBuilder);
|
|
4603
4876
|
const slippageP = params.type === "market" /* Market */ ? BigInt(this.resolveSlippage(params.slippage)) : 0n;
|
|
4604
|
-
|
|
4605
|
-
return this.execute(encoded);
|
|
4877
|
+
return encodeOpenTrade(trade, builderFee, orderType, slippageP, this.contracts.trading);
|
|
4606
4878
|
}
|
|
4607
4879
|
/**
|
|
4608
4880
|
* Close an open position (full or partial).
|
|
@@ -4617,6 +4889,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4617
4889
|
* ```
|
|
4618
4890
|
*/
|
|
4619
4891
|
async closeTrade(params) {
|
|
4892
|
+
return this.submitPrepared(this.getCloseTradeEncoded(params));
|
|
4893
|
+
}
|
|
4894
|
+
getCloseTradeTx(params) {
|
|
4895
|
+
return this.buildPreparedTx(this.getCloseTradeEncoded(params));
|
|
4896
|
+
}
|
|
4897
|
+
getCloseTradeEncoded(params) {
|
|
4620
4898
|
try {
|
|
4621
4899
|
validateCloseParams({
|
|
4622
4900
|
a: Number(params.pairId),
|
|
@@ -4636,7 +4914,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4636
4914
|
const closePercentage = BigInt(Math.round(params.closePercent * 100));
|
|
4637
4915
|
const marketPrice = parsePrice(params.price);
|
|
4638
4916
|
const slippageP = BigInt(this.resolveSlippage(params.slippage));
|
|
4639
|
-
|
|
4917
|
+
return encodeCloseTradeMarket(
|
|
4640
4918
|
pairIndex,
|
|
4641
4919
|
index,
|
|
4642
4920
|
closePercentage,
|
|
@@ -4644,7 +4922,6 @@ var OstiumClient = class _OstiumClient {
|
|
|
4644
4922
|
slippageP,
|
|
4645
4923
|
this.contracts.trading
|
|
4646
4924
|
);
|
|
4647
|
-
return this.execute(encoded);
|
|
4648
4925
|
}
|
|
4649
4926
|
/**
|
|
4650
4927
|
* Cancel a pending order.
|
|
@@ -4667,6 +4944,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4667
4944
|
* ```
|
|
4668
4945
|
*/
|
|
4669
4946
|
async cancelOrder(params) {
|
|
4947
|
+
return this.submitPrepared(this.getCancelOrderEncoded(params));
|
|
4948
|
+
}
|
|
4949
|
+
getCancelOrderTx(params) {
|
|
4950
|
+
return this.buildPreparedTx(this.getCancelOrderEncoded(params));
|
|
4951
|
+
}
|
|
4952
|
+
getCancelOrderEncoded(params) {
|
|
4670
4953
|
let encoded;
|
|
4671
4954
|
if (params.type === "limit" /* Limit */) {
|
|
4672
4955
|
const pairIdNum = Number(params.pairId);
|
|
@@ -4703,7 +4986,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4703
4986
|
}
|
|
4704
4987
|
encoded = encodeOpenTradeMarketTimeout(BigInt(params.orderId), this.contracts.trading);
|
|
4705
4988
|
}
|
|
4706
|
-
return
|
|
4989
|
+
return encoded;
|
|
4707
4990
|
}
|
|
4708
4991
|
/**
|
|
4709
4992
|
* Modify an open trade or a pending limit order.
|
|
@@ -4728,6 +5011,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4728
5011
|
* - Both `takeProfit` and `stopLoss` without `price` → throws (send two calls).
|
|
4729
5012
|
*/
|
|
4730
5013
|
async modifyOrder(params) {
|
|
5014
|
+
return this.submitPrepared(this.getModifyOrderEncoded(params));
|
|
5015
|
+
}
|
|
5016
|
+
getModifyOrderTx(params) {
|
|
5017
|
+
return this.buildPreparedTx(this.getModifyOrderEncoded(params));
|
|
5018
|
+
}
|
|
5019
|
+
getModifyOrderEncoded(params) {
|
|
4731
5020
|
try {
|
|
4732
5021
|
validateModifyParams({
|
|
4733
5022
|
a: Number(params.pairId),
|
|
@@ -4767,7 +5056,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4767
5056
|
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4768
5057
|
);
|
|
4769
5058
|
}
|
|
4770
|
-
return
|
|
5059
|
+
return encoded;
|
|
4771
5060
|
}
|
|
4772
5061
|
/**
|
|
4773
5062
|
* Update collateral on an open position (isolated margin).
|
|
@@ -4787,6 +5076,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4787
5076
|
* Checks USDC allowance before top-up operations.
|
|
4788
5077
|
*/
|
|
4789
5078
|
async updateCollateral(params) {
|
|
5079
|
+
return this.submitPrepared(await this.getUpdateCollateralEncoded(params));
|
|
5080
|
+
}
|
|
5081
|
+
async getUpdateCollateralTx(params) {
|
|
5082
|
+
return this.buildPreparedTx(await this.getUpdateCollateralEncoded(params));
|
|
5083
|
+
}
|
|
5084
|
+
async getUpdateCollateralEncoded(params) {
|
|
4790
5085
|
const amount = parseFloat(params.amount);
|
|
4791
5086
|
if (Number.isNaN(amount) || amount === 0) {
|
|
4792
5087
|
throw new OstiumError(
|
|
@@ -4810,7 +5105,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4810
5105
|
} else {
|
|
4811
5106
|
encoded = encodeRemoveCollateral(pairIndex, index, absAmount, this.contracts.trading);
|
|
4812
5107
|
}
|
|
4813
|
-
return
|
|
5108
|
+
return encoded;
|
|
4814
5109
|
}
|
|
4815
5110
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4816
5111
|
// Subgraph reads (Hyperliquid-style API)
|
|
@@ -4938,31 +5233,104 @@ var OstiumClient = class _OstiumClient {
|
|
|
4938
5233
|
streamPrices(pairIds) {
|
|
4939
5234
|
return this.subgraph.streamPrices(pairIds);
|
|
4940
5235
|
}
|
|
5236
|
+
/**
|
|
5237
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
5238
|
+
*
|
|
5239
|
+
* Subscribes only to the unique pairs referenced by the response and emits the
|
|
5240
|
+
* full updated positions payload on each relevant price update. Pass an
|
|
5241
|
+
* existing `priceStream` to reuse a websocket your app already owns, or omit
|
|
5242
|
+
* it to let the SDK create a dedicated connection.
|
|
5243
|
+
*/
|
|
5244
|
+
streamPositionUpdates(initial, priceStream) {
|
|
5245
|
+
return this.subgraph.streamPositionUpdates(initial, priceStream);
|
|
5246
|
+
}
|
|
4941
5247
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4942
5248
|
// Internal helpers
|
|
4943
5249
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
* 1. signer.prepare() — optionally wraps in delegatedAction.
|
|
4947
|
-
* 2. submitter.submit() — sends via EOA walletClient or Pimlico UserOp.
|
|
4948
|
-
*/
|
|
4949
|
-
async execute(encoded) {
|
|
4950
|
-
const { signer, submitter } = this.requireWriter();
|
|
4951
|
-
const prepared = signer.prepare(encoded);
|
|
4952
|
-
return submitter.submit(prepared);
|
|
4953
|
-
}
|
|
4954
|
-
/** Throws if the client is read-only; otherwise returns the signer + submitter. */
|
|
4955
|
-
requireWriter() {
|
|
4956
|
-
if (!this.signer || !this.submitter) {
|
|
5250
|
+
getSetupGaslessDelegationEncoded() {
|
|
5251
|
+
if (!this.selfGasless || !this.effectiveSender) {
|
|
4957
5252
|
throw new OstiumError(
|
|
4958
|
-
"
|
|
5253
|
+
"setupGaslessDelegation() is only available in Self + Gasless mode.",
|
|
4959
5254
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4960
5255
|
);
|
|
4961
5256
|
}
|
|
4962
|
-
return
|
|
5257
|
+
return encodeSetDelegate(this.effectiveSender, this.contracts.trading);
|
|
5258
|
+
}
|
|
5259
|
+
getApproveUsdcEncoded(amount) {
|
|
5260
|
+
if (this.delegated) {
|
|
5261
|
+
throw new OstiumError(
|
|
5262
|
+
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
5263
|
+
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
5264
|
+
);
|
|
5265
|
+
}
|
|
5266
|
+
const rawAmount = amount === "max" ? maxUint256 : parseUsdc(amount);
|
|
5267
|
+
return encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
5268
|
+
}
|
|
5269
|
+
getSetDelegateEncoded(delegateAddress) {
|
|
5270
|
+
return encodeSetDelegate(delegateAddress, this.contracts.trading);
|
|
5271
|
+
}
|
|
5272
|
+
getRemoveDelegateEncoded() {
|
|
5273
|
+
return encodeRemoveDelegate(this.contracts.trading);
|
|
5274
|
+
}
|
|
5275
|
+
prepareEncoded(encoded) {
|
|
5276
|
+
const signer = this.requireBuildCapability();
|
|
5277
|
+
return signer.prepare(encoded);
|
|
5278
|
+
}
|
|
5279
|
+
buildPreparedTx(encoded) {
|
|
5280
|
+
return this.toBuiltTxRequest(this.prepareEncoded(encoded));
|
|
5281
|
+
}
|
|
5282
|
+
buildDirectEoaTx(encoded) {
|
|
5283
|
+
if (!this.canBuildTransactions() || !this.traderAddress) {
|
|
5284
|
+
throw new OstiumError(
|
|
5285
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5286
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5287
|
+
);
|
|
5288
|
+
}
|
|
5289
|
+
return {
|
|
5290
|
+
kind: "eoa",
|
|
5291
|
+
to: encoded.to,
|
|
5292
|
+
data: encoded.data,
|
|
5293
|
+
value: encoded.value,
|
|
5294
|
+
from: this.traderAddress,
|
|
5295
|
+
traderAddress: this.traderAddress
|
|
5296
|
+
};
|
|
5297
|
+
}
|
|
5298
|
+
toBuiltTxRequest(encoded) {
|
|
5299
|
+
if (!this.canBuildTransactions() || !this.traderAddress || !this.effectiveSender) {
|
|
5300
|
+
throw new OstiumError(
|
|
5301
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5302
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5303
|
+
);
|
|
5304
|
+
}
|
|
5305
|
+
if (this.gasless) {
|
|
5306
|
+
return {
|
|
5307
|
+
kind: "safe",
|
|
5308
|
+
safeAddress: this.effectiveSender,
|
|
5309
|
+
traderAddress: this.traderAddress,
|
|
5310
|
+
calls: [encoded]
|
|
5311
|
+
};
|
|
5312
|
+
}
|
|
5313
|
+
return {
|
|
5314
|
+
kind: "eoa",
|
|
5315
|
+
to: encoded.to,
|
|
5316
|
+
data: encoded.data,
|
|
5317
|
+
value: encoded.value,
|
|
5318
|
+
from: this.effectiveSender,
|
|
5319
|
+
traderAddress: this.traderAddress
|
|
5320
|
+
};
|
|
5321
|
+
}
|
|
5322
|
+
async submitPrepared(encoded) {
|
|
5323
|
+
const submitter = this.requireSubmitCapability();
|
|
5324
|
+
return submitter.submit(this.prepareEncoded(encoded));
|
|
4963
5325
|
}
|
|
4964
5326
|
/** Direct EOA submission — used only in Self + Gasless for approveUsdc and setupGaslessDelegation. */
|
|
4965
|
-
async
|
|
5327
|
+
async submitDirectEoa(encoded) {
|
|
5328
|
+
if (!this.eoaSubmit) {
|
|
5329
|
+
throw new OstiumError(
|
|
5330
|
+
"This client cannot submit direct EOA transactions.",
|
|
5331
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5332
|
+
);
|
|
5333
|
+
}
|
|
4966
5334
|
try {
|
|
4967
5335
|
return await this.eoaSubmit(encoded);
|
|
4968
5336
|
} catch (err) {
|
|
@@ -4978,11 +5346,29 @@ var OstiumClient = class _OstiumClient {
|
|
|
4978
5346
|
throw new OstiumError(`Transaction failed: ${msg}`, "SUBMISSION_FAILED" /* SUBMISSION_FAILED */, err);
|
|
4979
5347
|
}
|
|
4980
5348
|
}
|
|
5349
|
+
requireBuildCapability() {
|
|
5350
|
+
if (!this.signer) {
|
|
5351
|
+
throw new OstiumError(
|
|
5352
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5353
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5354
|
+
);
|
|
5355
|
+
}
|
|
5356
|
+
return this.signer;
|
|
5357
|
+
}
|
|
5358
|
+
requireSubmitCapability() {
|
|
5359
|
+
if (!this.submitter) {
|
|
5360
|
+
throw new OstiumError(
|
|
5361
|
+
"This client does not have submission credentials. Use the corresponding get*Tx() method or initialize with a private key.",
|
|
5362
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5363
|
+
);
|
|
5364
|
+
}
|
|
5365
|
+
return this.submitter;
|
|
5366
|
+
}
|
|
4981
5367
|
resolveSlippage(override) {
|
|
4982
5368
|
return override ?? this.defaultSlippageBps;
|
|
4983
5369
|
}
|
|
4984
5370
|
};
|
|
4985
5371
|
|
|
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 };
|
|
5372
|
+
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
5373
|
//# sourceMappingURL=index.js.map
|
|
4988
5374
|
//# sourceMappingURL=index.js.map
|