@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.cjs
CHANGED
|
@@ -260,11 +260,20 @@ var CancelOrderType = /* @__PURE__ */ ((CancelOrderType2) => {
|
|
|
260
260
|
// src/config.ts
|
|
261
261
|
var DEFAULT_PIMLICO_URL = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=42161";
|
|
262
262
|
var DEFAULT_PIMLICO_URL_TESTNET = "https://builder.ostium.io/v1/pimlico/sponsor?chainId=421614";
|
|
263
|
-
function
|
|
264
|
-
return
|
|
263
|
+
function hasPrivateKey(config) {
|
|
264
|
+
return typeof config.privateKey === "string" && config.privateKey.length > 0;
|
|
265
265
|
}
|
|
266
|
-
function
|
|
267
|
-
return
|
|
266
|
+
function resolveMode(config) {
|
|
267
|
+
if (config.mode) return config.mode;
|
|
268
|
+
if (config.privateKey) {
|
|
269
|
+
if (config.pimlicoUrl) return config.traderAddress ? "delegated-gasless" : "self-gasless";
|
|
270
|
+
return config.traderAddress ? "delegated-self" : "self-self";
|
|
271
|
+
}
|
|
272
|
+
if (config.safeAddress && config.delegateAddress && config.traderAddress) return "delegated-gasless";
|
|
273
|
+
if (config.safeAddress && config.traderAddress) return "self-gasless";
|
|
274
|
+
if (config.delegateAddress && config.traderAddress) return "delegated-self";
|
|
275
|
+
if (config.traderAddress) return "self-self";
|
|
276
|
+
throw new Error("Unable to infer client mode from config");
|
|
268
277
|
}
|
|
269
278
|
|
|
270
279
|
// src/errors.ts
|
|
@@ -2667,8 +2676,8 @@ var DelegatedSignerStrategy = class {
|
|
|
2667
2676
|
};
|
|
2668
2677
|
|
|
2669
2678
|
// src/signer/index.ts
|
|
2670
|
-
function createSigner(
|
|
2671
|
-
if (
|
|
2679
|
+
function createSigner(mode, traderAddress) {
|
|
2680
|
+
if (mode !== "self-self") {
|
|
2672
2681
|
return new DelegatedSignerStrategy(traderAddress);
|
|
2673
2682
|
}
|
|
2674
2683
|
return new SelfSignerStrategy(traderAddress);
|
|
@@ -2692,6 +2701,12 @@ var SelfSubmissionStrategy = class {
|
|
|
2692
2701
|
effectiveSender;
|
|
2693
2702
|
send;
|
|
2694
2703
|
constructor(config, testnet) {
|
|
2704
|
+
if (!config.privateKey) {
|
|
2705
|
+
throw new OstiumError(
|
|
2706
|
+
"privateKey is required for self-submission mode",
|
|
2707
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2695
2710
|
if (!config.rpcUrl) {
|
|
2696
2711
|
throw new OstiumError(
|
|
2697
2712
|
"rpcUrl is required for self-submission mode",
|
|
@@ -2757,6 +2772,12 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2757
2772
|
this.effectiveSender = safeAccount.address;
|
|
2758
2773
|
}
|
|
2759
2774
|
static async create(config, testnet) {
|
|
2775
|
+
if (!config.privateKey) {
|
|
2776
|
+
throw new OstiumError(
|
|
2777
|
+
"privateKey is required for gasless submission mode",
|
|
2778
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
2779
|
+
);
|
|
2780
|
+
}
|
|
2760
2781
|
if (!config.pimlicoUrl) {
|
|
2761
2782
|
throw new OstiumError(
|
|
2762
2783
|
"pimlicoUrl is required for gasless mode",
|
|
@@ -2825,8 +2846,8 @@ var GaslessSubmissionStrategy = class _GaslessSubmissionStrategy {
|
|
|
2825
2846
|
};
|
|
2826
2847
|
|
|
2827
2848
|
// src/submitter/index.ts
|
|
2828
|
-
async function createSubmitter(config, testnet) {
|
|
2829
|
-
if (
|
|
2849
|
+
async function createSubmitter(config, testnet, mode) {
|
|
2850
|
+
if (mode === "self-gasless" || mode === "delegated-gasless") {
|
|
2830
2851
|
return GaslessSubmissionStrategy.create(config, testnet);
|
|
2831
2852
|
}
|
|
2832
2853
|
if (!config.rpcUrl) {
|
|
@@ -3226,10 +3247,11 @@ async function fetchLivePrices(builderApiUrl) {
|
|
|
3226
3247
|
}
|
|
3227
3248
|
return result;
|
|
3228
3249
|
}
|
|
3229
|
-
function rawTickToPublic(item) {
|
|
3250
|
+
function rawTickToPublic(item, pairId) {
|
|
3230
3251
|
const from = normalizePairName(item.from);
|
|
3231
3252
|
const to = normalizePairName(item.to);
|
|
3232
3253
|
return {
|
|
3254
|
+
pairId,
|
|
3233
3255
|
feedId: item.feed_id,
|
|
3234
3256
|
pair: `${from}-${to}`,
|
|
3235
3257
|
from,
|
|
@@ -3276,9 +3298,12 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3276
3298
|
snapshotHandlers = /* @__PURE__ */ new Set();
|
|
3277
3299
|
/** pairId (string) → raw "FROM-TO" name for the WS API. */
|
|
3278
3300
|
pairRawNameCache;
|
|
3279
|
-
|
|
3301
|
+
/** raw "FROM-TO" name → pairId (string) for mapping incoming ticks back to SDK ids. */
|
|
3302
|
+
rawNamePairIdCache;
|
|
3303
|
+
constructor(ws, pairRawNameCache, rawNamePairIdCache, initialSubscribeRawPairs) {
|
|
3280
3304
|
this.ws = ws;
|
|
3281
3305
|
this.pairRawNameCache = pairRawNameCache;
|
|
3306
|
+
this.rawNamePairIdCache = rawNamePairIdCache;
|
|
3282
3307
|
if (initialSubscribeRawPairs?.length) {
|
|
3283
3308
|
ws.once("open", () => {
|
|
3284
3309
|
ws.send(JSON.stringify({ type: "subscribe", pairs: initialSubscribeRawPairs }));
|
|
@@ -3292,10 +3317,13 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3292
3317
|
return;
|
|
3293
3318
|
}
|
|
3294
3319
|
if (msg.type === "snapshot" && Array.isArray(msg.data)) {
|
|
3295
|
-
const ticks = msg.data.map(
|
|
3320
|
+
const ticks = msg.data.map(
|
|
3321
|
+
(item) => rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`))
|
|
3322
|
+
);
|
|
3296
3323
|
for (const h of this.snapshotHandlers) h(ticks);
|
|
3297
3324
|
} else if (msg.type === "tick" && msg.data) {
|
|
3298
|
-
const
|
|
3325
|
+
const item = msg.data;
|
|
3326
|
+
const tick = rawTickToPublic(item, this.rawNamePairIdCache.get(`${item.from}-${item.to}`));
|
|
3299
3327
|
for (const h of this.tickHandlers) h(tick);
|
|
3300
3328
|
}
|
|
3301
3329
|
});
|
|
@@ -3321,7 +3349,11 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3321
3349
|
const ws = new WS__default.default(url, {
|
|
3322
3350
|
headers: { "User-Agent": "ostium-sdk", "Accept": "*/*" }
|
|
3323
3351
|
});
|
|
3324
|
-
|
|
3352
|
+
const rawNamePairIdCache = /* @__PURE__ */ new Map();
|
|
3353
|
+
for (const [pairId, rawName] of pairRawNameCache.entries()) {
|
|
3354
|
+
rawNamePairIdCache.set(rawName, pairId);
|
|
3355
|
+
}
|
|
3356
|
+
return new _OstiumPriceStream(ws, pairRawNameCache, rawNamePairIdCache, initial);
|
|
3325
3357
|
}
|
|
3326
3358
|
/**
|
|
3327
3359
|
* Register a callback that fires on every incoming price tick.
|
|
@@ -3397,6 +3429,37 @@ var OstiumPriceStream = class _OstiumPriceStream {
|
|
|
3397
3429
|
}
|
|
3398
3430
|
};
|
|
3399
3431
|
|
|
3432
|
+
// src/data/internal/aggregations.ts
|
|
3433
|
+
function aggregateMarginSummary(pairPositions) {
|
|
3434
|
+
let accountValue = 0;
|
|
3435
|
+
let totalCollateralUsed = 0;
|
|
3436
|
+
let totalNtlPos = 0;
|
|
3437
|
+
let totalRawPnlUsd = 0;
|
|
3438
|
+
let totalCumRollover = 0;
|
|
3439
|
+
let totalWithdrawable = 0;
|
|
3440
|
+
for (const { position } of pairPositions) {
|
|
3441
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3442
|
+
const ntl = parseFloat(position.ntl) || 0;
|
|
3443
|
+
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
3444
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3445
|
+
const withdrawable = parseFloat(position.maxWithdrawable) || 0;
|
|
3446
|
+
accountValue += collateral + pnl;
|
|
3447
|
+
totalCollateralUsed += collateral;
|
|
3448
|
+
totalNtlPos += ntl;
|
|
3449
|
+
totalRawPnlUsd += pnl;
|
|
3450
|
+
totalCumRollover += rollover;
|
|
3451
|
+
totalWithdrawable += withdrawable;
|
|
3452
|
+
}
|
|
3453
|
+
return {
|
|
3454
|
+
accountValue: accountValue.toString(),
|
|
3455
|
+
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
3456
|
+
totalNtlPos: totalNtlPos.toString(),
|
|
3457
|
+
totalRawPnlUsd: totalRawPnlUsd.toString(),
|
|
3458
|
+
totalCumRollover: totalCumRollover.toString(),
|
|
3459
|
+
totalWithdrawable: totalWithdrawable.toString()
|
|
3460
|
+
};
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3400
3463
|
// src/data/internal/formatters.ts
|
|
3401
3464
|
var MIN_NOTIONAL = "5.0";
|
|
3402
3465
|
var MIN_NOTIONAL_NUM = 5;
|
|
@@ -3498,14 +3561,14 @@ function formatPosition(raw, price, pnl, maxLeverage) {
|
|
|
3498
3561
|
returnOnEquity: roe.toString(),
|
|
3499
3562
|
liquidationPx: pnl.liquidationPrice.toString(),
|
|
3500
3563
|
collateralUsed: collateral.toString(),
|
|
3501
|
-
cumRollover: pnl.rollover.toString(),
|
|
3564
|
+
cumRollover: (pnl.rollover * -1).toString(),
|
|
3502
3565
|
...raw.takeProfitPrice && raw.takeProfitPrice !== "0" ? { tpPx: formatTokens(raw.takeProfitPrice).toString() } : {},
|
|
3503
3566
|
...raw.stopLossPrice && raw.stopLossPrice !== "0" ? { slPx: formatTokens(raw.stopLossPrice).toString() } : {},
|
|
3504
3567
|
openTimestamp: parseInt(raw.timestamp || "0") * 1e3,
|
|
3505
3568
|
isDayTrade: raw.isDayTrade ?? false,
|
|
3506
|
-
maxLeverage: maxLeverage.toString()
|
|
3507
|
-
|
|
3508
|
-
|
|
3569
|
+
maxLeverage: maxLeverage.toString(),
|
|
3570
|
+
maxWithdrawable: maxWithdrawForPosition(collateral, leverage, maxLeverage).toString()
|
|
3571
|
+
}
|
|
3509
3572
|
};
|
|
3510
3573
|
}
|
|
3511
3574
|
function formatFill(raw) {
|
|
@@ -3582,6 +3645,131 @@ function formatOpenOrder(raw) {
|
|
|
3582
3645
|
timestamp: parseInt(raw.initiatedAt || "0") * 1e3
|
|
3583
3646
|
};
|
|
3584
3647
|
}
|
|
3648
|
+
|
|
3649
|
+
// src/data/internal/position-updates.ts
|
|
3650
|
+
function cloneResponse(response) {
|
|
3651
|
+
return {
|
|
3652
|
+
pairPositions: response.pairPositions.map((pairPos) => ({
|
|
3653
|
+
position: { ...pairPos.position }
|
|
3654
|
+
})),
|
|
3655
|
+
marginSummary: { ...response.marginSummary },
|
|
3656
|
+
time: response.time
|
|
3657
|
+
};
|
|
3658
|
+
}
|
|
3659
|
+
function applyTickToPosition(position, tick) {
|
|
3660
|
+
const size = parseFloat(position.szi) || 0;
|
|
3661
|
+
const entry = parseFloat(position.entryPx) || 0;
|
|
3662
|
+
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
3663
|
+
const maxLeverage = parseFloat(position.maxLeverage) || 0;
|
|
3664
|
+
const rollover = parseFloat(position.cumRollover) || 0;
|
|
3665
|
+
const isLong = position.side === "B";
|
|
3666
|
+
const ntl = size * tick.mid;
|
|
3667
|
+
const rawPnl = isLong ? (tick.mid - entry) * size : (entry - tick.mid) * size;
|
|
3668
|
+
const netPnl = rawPnl + rollover;
|
|
3669
|
+
const roe = collateral > 0 ? netPnl / collateral : 0;
|
|
3670
|
+
const currentLeverage = collateral > 0 ? ntl / collateral : 0;
|
|
3671
|
+
const maxWithdrawable = maxWithdrawForPosition(collateral, currentLeverage, maxLeverage);
|
|
3672
|
+
return {
|
|
3673
|
+
...position,
|
|
3674
|
+
ntl: ntl.toString(),
|
|
3675
|
+
unrealizedPnl: netPnl.toString(),
|
|
3676
|
+
returnOnEquity: roe.toString(),
|
|
3677
|
+
maxWithdrawable: maxWithdrawable.toString()
|
|
3678
|
+
};
|
|
3679
|
+
}
|
|
3680
|
+
function updateResponseWithTick(current, tick, timestampMs) {
|
|
3681
|
+
if (!tick.pairId) return current;
|
|
3682
|
+
let changed = false;
|
|
3683
|
+
const pairPositions = current.pairPositions.map((pairPos) => {
|
|
3684
|
+
if (String(pairPos.position.pairId) !== String(tick.pairId)) {
|
|
3685
|
+
return pairPos;
|
|
3686
|
+
}
|
|
3687
|
+
changed = true;
|
|
3688
|
+
return {
|
|
3689
|
+
...pairPos,
|
|
3690
|
+
position: applyTickToPosition(pairPos.position, tick)
|
|
3691
|
+
};
|
|
3692
|
+
});
|
|
3693
|
+
if (!changed) return current;
|
|
3694
|
+
return {
|
|
3695
|
+
pairPositions,
|
|
3696
|
+
marginSummary: aggregateMarginSummary(pairPositions),
|
|
3697
|
+
time: timestampMs
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
var OstiumPositionUpdatesStream = class {
|
|
3701
|
+
priceStream;
|
|
3702
|
+
updateHandlers = /* @__PURE__ */ new Set();
|
|
3703
|
+
current;
|
|
3704
|
+
trackedPairIds;
|
|
3705
|
+
constructor(initial, trackedPairIds, priceStream) {
|
|
3706
|
+
this.current = cloneResponse(initial);
|
|
3707
|
+
this.priceStream = priceStream;
|
|
3708
|
+
this.trackedPairIds = new Set(trackedPairIds);
|
|
3709
|
+
if (!priceStream) return;
|
|
3710
|
+
priceStream.onSnapshot((ticks) => {
|
|
3711
|
+
this.ingestSnapshot(ticks);
|
|
3712
|
+
});
|
|
3713
|
+
priceStream.onTick((tick) => {
|
|
3714
|
+
this.ingestTick(tick);
|
|
3715
|
+
});
|
|
3716
|
+
}
|
|
3717
|
+
onUpdate(handler) {
|
|
3718
|
+
this.updateHandlers.add(handler);
|
|
3719
|
+
return () => {
|
|
3720
|
+
this.updateHandlers.delete(handler);
|
|
3721
|
+
};
|
|
3722
|
+
}
|
|
3723
|
+
onOpen(handler) {
|
|
3724
|
+
this.priceStream?.onOpen(handler);
|
|
3725
|
+
return this;
|
|
3726
|
+
}
|
|
3727
|
+
onError(handler) {
|
|
3728
|
+
this.priceStream?.onError(handler);
|
|
3729
|
+
return this;
|
|
3730
|
+
}
|
|
3731
|
+
onClose(handler) {
|
|
3732
|
+
this.priceStream?.onClose(handler);
|
|
3733
|
+
return this;
|
|
3734
|
+
}
|
|
3735
|
+
getCurrent() {
|
|
3736
|
+
return cloneResponse(this.current);
|
|
3737
|
+
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Apply a single externally sourced price tick to the tracked positions.
|
|
3740
|
+
*
|
|
3741
|
+
* Use this when your app already has its own websocket connection and you want
|
|
3742
|
+
* the SDK to handle only the position recalculation logic.
|
|
3743
|
+
*/
|
|
3744
|
+
ingestTick(tick) {
|
|
3745
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) return;
|
|
3746
|
+
const next = updateResponseWithTick(this.current, tick, tick.timestampSeconds * 1e3);
|
|
3747
|
+
if (next !== this.current) {
|
|
3748
|
+
this.current = next;
|
|
3749
|
+
this.emit(next);
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
/**
|
|
3753
|
+
* Apply a batch of externally sourced ticks, such as a websocket snapshot.
|
|
3754
|
+
*/
|
|
3755
|
+
ingestSnapshot(ticks) {
|
|
3756
|
+
let next = this.current;
|
|
3757
|
+
for (const tick of ticks) {
|
|
3758
|
+
if (!tick.pairId || !this.trackedPairIds.has(String(tick.pairId))) continue;
|
|
3759
|
+
next = updateResponseWithTick(next, tick, tick.timestampSeconds * 1e3);
|
|
3760
|
+
}
|
|
3761
|
+
if (next !== this.current) {
|
|
3762
|
+
this.current = next;
|
|
3763
|
+
this.emit(next);
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
close() {
|
|
3767
|
+
this.priceStream?.close();
|
|
3768
|
+
}
|
|
3769
|
+
emit(positions) {
|
|
3770
|
+
for (const handler of this.updateHandlers) handler(cloneResponse(positions));
|
|
3771
|
+
}
|
|
3772
|
+
};
|
|
3585
3773
|
var EMPTY_RESULT = { long: [], short: [] };
|
|
3586
3774
|
var ORDERBOOK_MAX_LEVELS = 20;
|
|
3587
3775
|
function generateLogSpacedNotionals(levels, capacity) {
|
|
@@ -3777,20 +3965,16 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
3777
3965
|
}),
|
|
3778
3966
|
this.fetchLivePricesSafe()
|
|
3779
3967
|
]);
|
|
3780
|
-
let totalWithdrawable = 0;
|
|
3781
3968
|
const pairPositions = trades.map((trade) => {
|
|
3782
3969
|
const price = prices[`${trade.pair.from}/${trade.pair.to}`];
|
|
3783
3970
|
const pnl = price && blockNumber ? getTradePnL(trade, price, blockNumber) : EMPTY_PNL;
|
|
3784
3971
|
const maxLev = trade.isDayTrade ? formatLeverage(trade.pair.overnightMaxLeverage) : pairMaxLeverage(trade.pair);
|
|
3785
|
-
|
|
3786
|
-
totalWithdrawable += parseFloat(pairPos.maxWithdrawable);
|
|
3787
|
-
return pairPos;
|
|
3972
|
+
return formatPosition(trade, price, pnl, maxLev);
|
|
3788
3973
|
});
|
|
3789
3974
|
const marginSummary = aggregateMarginSummary(pairPositions);
|
|
3790
3975
|
return {
|
|
3791
3976
|
pairPositions,
|
|
3792
3977
|
marginSummary,
|
|
3793
|
-
withdrawable: totalWithdrawable.toString(),
|
|
3794
3978
|
time: Date.now()
|
|
3795
3979
|
};
|
|
3796
3980
|
}
|
|
@@ -4041,6 +4225,24 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4041
4225
|
});
|
|
4042
4226
|
return OstiumPriceStream.connect(this.builderApiUrl, rawNames, cache);
|
|
4043
4227
|
}
|
|
4228
|
+
/**
|
|
4229
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
4230
|
+
*
|
|
4231
|
+
* The SDK subscribes only to the unique pairs present in `initial.pairPositions`,
|
|
4232
|
+
* recalculates price-sensitive fields for affected positions on each tick, and
|
|
4233
|
+
* emits the full updated response.
|
|
4234
|
+
*
|
|
4235
|
+
* Pass an existing `priceStream` to reuse a websocket connection your app has
|
|
4236
|
+
* already opened. Omit it to let the SDK open and manage a dedicated
|
|
4237
|
+
* connection for the tracked pair ids.
|
|
4238
|
+
*/
|
|
4239
|
+
streamPositionUpdates(initial, priceStream) {
|
|
4240
|
+
const pairIds = [...new Set(initial.pairPositions.map(({ position }) => String(position.pairId)))];
|
|
4241
|
+
if (pairIds.length === 0) {
|
|
4242
|
+
return new OstiumPositionUpdatesStream(initial, []);
|
|
4243
|
+
}
|
|
4244
|
+
return new OstiumPositionUpdatesStream(initial, pairIds, priceStream ?? this.streamPrices(pairIds));
|
|
4245
|
+
}
|
|
4044
4246
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4045
4247
|
// Internal — fetchers, cache, query wrapper
|
|
4046
4248
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -4099,43 +4301,28 @@ var OstiumSubgraphClient = class _OstiumSubgraphClient {
|
|
|
4099
4301
|
}
|
|
4100
4302
|
}
|
|
4101
4303
|
};
|
|
4102
|
-
function aggregateMarginSummary(pairPositions) {
|
|
4103
|
-
let accountValue = 0;
|
|
4104
|
-
let totalCollateralUsed = 0;
|
|
4105
|
-
let totalNtlPos = 0;
|
|
4106
|
-
let totalRawPnlUsd = 0;
|
|
4107
|
-
for (const { position } of pairPositions) {
|
|
4108
|
-
const collateral = parseFloat(position.collateralUsed) || 0;
|
|
4109
|
-
const ntl = parseFloat(position.ntl) || 0;
|
|
4110
|
-
const pnl = parseFloat(position.unrealizedPnl) || 0;
|
|
4111
|
-
accountValue += collateral + pnl;
|
|
4112
|
-
totalCollateralUsed += collateral;
|
|
4113
|
-
totalNtlPos += ntl;
|
|
4114
|
-
totalRawPnlUsd += pnl;
|
|
4115
|
-
}
|
|
4116
|
-
return {
|
|
4117
|
-
accountValue: accountValue.toString(),
|
|
4118
|
-
totalCollateralUsed: totalCollateralUsed.toString(),
|
|
4119
|
-
totalNtlPos: totalNtlPos.toString(),
|
|
4120
|
-
totalRawPnlUsd: totalRawPnlUsd.toString()
|
|
4121
|
-
};
|
|
4122
|
-
}
|
|
4123
4304
|
|
|
4124
4305
|
// src/client.ts
|
|
4125
4306
|
var HEX64_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
4126
4307
|
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
4127
4308
|
function validateConfig(config) {
|
|
4128
|
-
if (!HEX64_RE.test(config.privateKey)) {
|
|
4309
|
+
if (config.privateKey && !HEX64_RE.test(config.privateKey)) {
|
|
4129
4310
|
throw new OstiumError(
|
|
4130
4311
|
"privateKey must be a 64-character hex string prefixed with 0x",
|
|
4131
4312
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4132
4313
|
);
|
|
4133
4314
|
}
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4315
|
+
for (const [label, value] of [
|
|
4316
|
+
["traderAddress", config.traderAddress],
|
|
4317
|
+
["delegateAddress", config.delegateAddress],
|
|
4318
|
+
["safeAddress", config.safeAddress]
|
|
4319
|
+
]) {
|
|
4320
|
+
if (value && !ADDRESS_RE.test(value)) {
|
|
4321
|
+
throw new OstiumError(
|
|
4322
|
+
`${label} must be a valid 42-character Ethereum address (0x + 40 hex chars)`,
|
|
4323
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
4139
4326
|
}
|
|
4140
4327
|
if (config.slippageBps !== void 0 && (config.slippageBps < 0 || config.slippageBps > 500)) {
|
|
4141
4328
|
throw new OstiumError(
|
|
@@ -4149,10 +4336,45 @@ function validateConfig(config) {
|
|
|
4149
4336
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4150
4337
|
);
|
|
4151
4338
|
}
|
|
4339
|
+
let mode;
|
|
4340
|
+
try {
|
|
4341
|
+
mode = resolveMode(config);
|
|
4342
|
+
} catch {
|
|
4343
|
+
throw new OstiumError(
|
|
4344
|
+
"Could not infer client mode from config. Provide the required addresses for the selected factory.",
|
|
4345
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4346
|
+
);
|
|
4347
|
+
}
|
|
4348
|
+
if (mode === "self-self" && !config.privateKey && !config.traderAddress) {
|
|
4349
|
+
throw new OstiumError(
|
|
4350
|
+
"Self + Self mode requires traderPrivateKey for submission or traderAddress for build-only usage.",
|
|
4351
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4352
|
+
);
|
|
4353
|
+
}
|
|
4354
|
+
if (mode === "self-gasless" && !config.privateKey && (!config.traderAddress || !config.safeAddress)) {
|
|
4355
|
+
throw new OstiumError(
|
|
4356
|
+
"Self + Gasless build-only mode requires traderAddress and safeAddress.",
|
|
4357
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
if (mode === "delegated-self" && (!config.traderAddress || !config.privateKey && !config.delegateAddress)) {
|
|
4361
|
+
throw new OstiumError(
|
|
4362
|
+
"Delegated + Self mode requires traderAddress and either delegatePrivateKey or delegateAddress.",
|
|
4363
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4364
|
+
);
|
|
4365
|
+
}
|
|
4366
|
+
if (mode === "delegated-gasless" && (!config.traderAddress || !config.privateKey && (!config.delegateAddress || !config.safeAddress))) {
|
|
4367
|
+
throw new OstiumError(
|
|
4368
|
+
"Delegated + Gasless mode requires traderAddress and either delegatePrivateKey or both delegateAddress and safeAddress.",
|
|
4369
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4370
|
+
);
|
|
4371
|
+
}
|
|
4152
4372
|
}
|
|
4153
4373
|
var OstiumClient = class _OstiumClient {
|
|
4154
4374
|
signer;
|
|
4155
4375
|
submitter;
|
|
4376
|
+
traderAddress;
|
|
4377
|
+
effectiveSender;
|
|
4156
4378
|
contracts;
|
|
4157
4379
|
publicClient;
|
|
4158
4380
|
defaultSlippageBps;
|
|
@@ -4167,6 +4389,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4167
4389
|
constructor(internals) {
|
|
4168
4390
|
this.signer = internals.signer;
|
|
4169
4391
|
this.submitter = internals.submitter;
|
|
4392
|
+
this.traderAddress = internals.traderAddress;
|
|
4393
|
+
this.effectiveSender = internals.effectiveSender;
|
|
4170
4394
|
this.contracts = internals.contracts;
|
|
4171
4395
|
this.publicClient = internals.publicClient;
|
|
4172
4396
|
this.defaultSlippageBps = internals.defaultSlippageBps;
|
|
@@ -4191,17 +4415,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4191
4415
|
* });
|
|
4192
4416
|
* await client.openTrade({ ... });
|
|
4193
4417
|
* ```
|
|
4194
|
-
|
|
4418
|
+
*/
|
|
4195
4419
|
static async createSelfAndSelf(params) {
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
rpcUrl: params.rpcUrl,
|
|
4420
|
+
const base = {
|
|
4421
|
+
mode: "self-self",
|
|
4199
4422
|
testnet: params.testnet,
|
|
4200
4423
|
slippageBps: params.slippageBps,
|
|
4201
4424
|
builder: params.builder,
|
|
4202
4425
|
subgraphUrl: params.subgraphUrl,
|
|
4203
|
-
builderApiUrl: params.builderApiUrl
|
|
4204
|
-
|
|
4426
|
+
builderApiUrl: params.builderApiUrl,
|
|
4427
|
+
rpcUrl: params.rpcUrl
|
|
4428
|
+
};
|
|
4429
|
+
return _OstiumClient._fromConfig(
|
|
4430
|
+
"traderPrivateKey" in params ? { ...base, privateKey: params.traderPrivateKey } : { ...base, traderAddress: params.traderAddress }
|
|
4431
|
+
);
|
|
4205
4432
|
}
|
|
4206
4433
|
/**
|
|
4207
4434
|
* **Self + Gasless** — your EOA owns everything; a Safe relays trades for free.
|
|
@@ -4225,17 +4452,27 @@ var OstiumClient = class _OstiumClient {
|
|
|
4225
4452
|
*/
|
|
4226
4453
|
static async createSelfAndGasless(params) {
|
|
4227
4454
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4455
|
+
const base = {
|
|
4456
|
+
mode: "self-gasless",
|
|
4231
4457
|
rpcUrl: params.rpcUrl,
|
|
4232
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4233
4458
|
testnet: params.testnet,
|
|
4234
4459
|
slippageBps: params.slippageBps,
|
|
4235
4460
|
builder: params.builder,
|
|
4236
4461
|
subgraphUrl: params.subgraphUrl,
|
|
4237
4462
|
builderApiUrl: params.builderApiUrl
|
|
4238
|
-
}
|
|
4463
|
+
};
|
|
4464
|
+
return _OstiumClient._fromConfig(
|
|
4465
|
+
"traderPrivateKey" in params ? {
|
|
4466
|
+
...base,
|
|
4467
|
+
privateKey: params.traderPrivateKey,
|
|
4468
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4469
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4470
|
+
} : {
|
|
4471
|
+
...base,
|
|
4472
|
+
traderAddress: params.traderAddress,
|
|
4473
|
+
safeAddress: params.safeAddress
|
|
4474
|
+
}
|
|
4475
|
+
);
|
|
4239
4476
|
}
|
|
4240
4477
|
/**
|
|
4241
4478
|
* **Delegated + Self** — a delegate EOA signs and pays gas on behalf of a trader address.
|
|
@@ -4255,8 +4492,8 @@ var OstiumClient = class _OstiumClient {
|
|
|
4255
4492
|
* ```
|
|
4256
4493
|
*/
|
|
4257
4494
|
static async createDelegatedAndSelf(params) {
|
|
4258
|
-
|
|
4259
|
-
|
|
4495
|
+
const base = {
|
|
4496
|
+
mode: "delegated-self",
|
|
4260
4497
|
traderAddress: params.traderAddress,
|
|
4261
4498
|
rpcUrl: params.rpcUrl,
|
|
4262
4499
|
testnet: params.testnet,
|
|
@@ -4264,7 +4501,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4264
4501
|
builder: params.builder,
|
|
4265
4502
|
subgraphUrl: params.subgraphUrl,
|
|
4266
4503
|
builderApiUrl: params.builderApiUrl
|
|
4267
|
-
}
|
|
4504
|
+
};
|
|
4505
|
+
return _OstiumClient._fromConfig(
|
|
4506
|
+
"delegatePrivateKey" in params ? { ...base, privateKey: params.delegatePrivateKey } : { ...base, delegateAddress: params.delegateAddress }
|
|
4507
|
+
);
|
|
4268
4508
|
}
|
|
4269
4509
|
/**
|
|
4270
4510
|
* **Delegated + Gasless** — a Safe derived from the delegate key submits sponsored
|
|
@@ -4285,18 +4525,28 @@ var OstiumClient = class _OstiumClient {
|
|
|
4285
4525
|
*/
|
|
4286
4526
|
static async createDelegatedAndGasless(params) {
|
|
4287
4527
|
const defaultUrl = params.testnet ? DEFAULT_PIMLICO_URL_TESTNET : DEFAULT_PIMLICO_URL;
|
|
4288
|
-
|
|
4289
|
-
|
|
4528
|
+
const base = {
|
|
4529
|
+
mode: "delegated-gasless",
|
|
4290
4530
|
traderAddress: params.traderAddress,
|
|
4291
|
-
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4292
4531
|
rpcUrl: params.rpcUrl,
|
|
4293
|
-
sponsorshipPolicyId: params.sponsorshipPolicyId,
|
|
4294
4532
|
testnet: params.testnet,
|
|
4295
4533
|
slippageBps: params.slippageBps,
|
|
4296
4534
|
builder: params.builder,
|
|
4297
4535
|
subgraphUrl: params.subgraphUrl,
|
|
4298
4536
|
builderApiUrl: params.builderApiUrl
|
|
4299
|
-
}
|
|
4537
|
+
};
|
|
4538
|
+
return _OstiumClient._fromConfig(
|
|
4539
|
+
"delegatePrivateKey" in params ? {
|
|
4540
|
+
...base,
|
|
4541
|
+
privateKey: params.delegatePrivateKey,
|
|
4542
|
+
pimlicoUrl: params.pimlicoUrl ?? defaultUrl,
|
|
4543
|
+
sponsorshipPolicyId: params.sponsorshipPolicyId
|
|
4544
|
+
} : {
|
|
4545
|
+
...base,
|
|
4546
|
+
delegateAddress: params.delegateAddress,
|
|
4547
|
+
safeAddress: params.safeAddress
|
|
4548
|
+
}
|
|
4549
|
+
);
|
|
4300
4550
|
}
|
|
4301
4551
|
/**
|
|
4302
4552
|
* **Read-only client** — no signer, no submitter, no privateKey required.
|
|
@@ -4341,6 +4591,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4341
4591
|
/** Internal factory shared by all public constructors. */
|
|
4342
4592
|
static async _fromConfig(config) {
|
|
4343
4593
|
validateConfig(config);
|
|
4594
|
+
const mode = resolveMode(config);
|
|
4344
4595
|
const testnet = config.testnet ?? false;
|
|
4345
4596
|
const networkConfig = getNetworkConfig(testnet);
|
|
4346
4597
|
const chain = testnet ? chains.arbitrumSepolia : chains.arbitrum;
|
|
@@ -4350,18 +4601,29 @@ var OstiumClient = class _OstiumClient {
|
|
|
4350
4601
|
tradingStorage: networkConfig.contracts.tradingStorage,
|
|
4351
4602
|
usdc: networkConfig.contracts.usdc
|
|
4352
4603
|
};
|
|
4353
|
-
const selfGasless =
|
|
4354
|
-
const submitter = await createSubmitter(config, testnet);
|
|
4604
|
+
const selfGasless = mode === "self-gasless";
|
|
4355
4605
|
let traderAddress;
|
|
4356
|
-
if (
|
|
4606
|
+
if (mode === "self-self" || mode === "self-gasless") {
|
|
4607
|
+
traderAddress = config.traderAddress ?? accounts.privateKeyToAccount(config.privateKey).address;
|
|
4608
|
+
} else {
|
|
4357
4609
|
traderAddress = config.traderAddress;
|
|
4610
|
+
}
|
|
4611
|
+
const signer = createSigner(mode, traderAddress);
|
|
4612
|
+
let submitter;
|
|
4613
|
+
let effectiveSender;
|
|
4614
|
+
if (hasPrivateKey(config)) {
|
|
4615
|
+
submitter = await createSubmitter(config, testnet, mode);
|
|
4616
|
+
effectiveSender = submitter.effectiveSender;
|
|
4617
|
+
} else if (mode === "self-self") {
|
|
4618
|
+
effectiveSender = traderAddress;
|
|
4619
|
+
} else if (mode === "delegated-self") {
|
|
4620
|
+
effectiveSender = config.delegateAddress;
|
|
4358
4621
|
} else {
|
|
4359
|
-
|
|
4622
|
+
effectiveSender = config.safeAddress;
|
|
4360
4623
|
}
|
|
4361
|
-
const signer = createSigner(config, traderAddress, selfGasless);
|
|
4362
4624
|
const publicRpc = testnet ? "https://sepolia-rollup.arbitrum.io/rpc" : "https://arb1.arbitrum.io/rpc";
|
|
4363
|
-
const rpcUrl = config.rpcUrl ??
|
|
4364
|
-
if (
|
|
4625
|
+
const rpcUrl = config.rpcUrl ?? publicRpc;
|
|
4626
|
+
if (hasPrivateKey(config) && mode !== "self-gasless" && mode !== "delegated-gasless" && !config.rpcUrl) {
|
|
4365
4627
|
throw new OstiumError(
|
|
4366
4628
|
"rpcUrl is required for self-submission mode",
|
|
4367
4629
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
@@ -4369,7 +4631,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4369
4631
|
}
|
|
4370
4632
|
const publicClient = viem.createPublicClient({ chain, transport: viem.http(rpcUrl) });
|
|
4371
4633
|
let eoaSubmit;
|
|
4372
|
-
if (selfGasless) {
|
|
4634
|
+
if (selfGasless && hasPrivateKey(config)) {
|
|
4373
4635
|
const eoaAccount = accounts.privateKeyToAccount(config.privateKey);
|
|
4374
4636
|
const eoaRpcUrl = config.rpcUrl ?? publicRpc;
|
|
4375
4637
|
const eoaWalletClient = viem.createWalletClient({ account: eoaAccount, chain, transport: viem.http(eoaRpcUrl) });
|
|
@@ -4391,11 +4653,13 @@ var OstiumClient = class _OstiumClient {
|
|
|
4391
4653
|
return new _OstiumClient({
|
|
4392
4654
|
signer,
|
|
4393
4655
|
submitter,
|
|
4656
|
+
traderAddress,
|
|
4657
|
+
effectiveSender,
|
|
4394
4658
|
contracts,
|
|
4395
4659
|
publicClient,
|
|
4396
4660
|
defaultSlippageBps,
|
|
4397
|
-
delegated:
|
|
4398
|
-
gasless:
|
|
4661
|
+
delegated: mode === "delegated-self" || mode === "delegated-gasless",
|
|
4662
|
+
gasless: mode === "self-gasless" || mode === "delegated-gasless",
|
|
4399
4663
|
selfGasless,
|
|
4400
4664
|
eoaSubmit,
|
|
4401
4665
|
builderAddress: config.builder?.address,
|
|
@@ -4413,17 +4677,25 @@ var OstiumClient = class _OstiumClient {
|
|
|
4413
4677
|
* - Self + Gasless: EOA derived from privateKey (NOT the Safe — USDC lives here).
|
|
4414
4678
|
*/
|
|
4415
4679
|
getTraderAddress() {
|
|
4416
|
-
if (!this.
|
|
4680
|
+
if (!this.traderAddress) {
|
|
4417
4681
|
throw new OstiumError(
|
|
4418
4682
|
"No connected trader \u2014 pass `user` explicitly or use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
4419
4683
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4420
4684
|
);
|
|
4421
4685
|
}
|
|
4422
|
-
return this.
|
|
4686
|
+
return this.traderAddress;
|
|
4423
4687
|
}
|
|
4424
|
-
/** True when the client
|
|
4688
|
+
/** True when the client cannot submit transactions directly. */
|
|
4425
4689
|
isReadOnly() {
|
|
4426
|
-
return !this.
|
|
4690
|
+
return !this.submitter;
|
|
4691
|
+
}
|
|
4692
|
+
/** True when the client can build mode-correct unsigned transaction requests. */
|
|
4693
|
+
canBuildTransactions() {
|
|
4694
|
+
return !!this.signer && !!this.effectiveSender;
|
|
4695
|
+
}
|
|
4696
|
+
/** True when the client has the credentials needed for SDK-managed submission. */
|
|
4697
|
+
canSubmitTransactions() {
|
|
4698
|
+
return !!this.submitter;
|
|
4427
4699
|
}
|
|
4428
4700
|
/**
|
|
4429
4701
|
* Returns the Safe smart-account address used for gasless submission.
|
|
@@ -4435,7 +4707,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4435
4707
|
* - Self + Self / Delegated + Self: returns undefined.
|
|
4436
4708
|
*/
|
|
4437
4709
|
getSmartAccountAddress() {
|
|
4438
|
-
return this.gasless
|
|
4710
|
+
return this.gasless ? this.effectiveSender : void 0;
|
|
4439
4711
|
}
|
|
4440
4712
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4441
4713
|
// Self + Gasless setup
|
|
@@ -4459,15 +4731,10 @@ var OstiumClient = class _OstiumClient {
|
|
|
4459
4731
|
* ```
|
|
4460
4732
|
*/
|
|
4461
4733
|
async setupGaslessDelegation() {
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
);
|
|
4467
|
-
}
|
|
4468
|
-
const safeAddress = this.submitter.effectiveSender;
|
|
4469
|
-
const encoded = encodeSetDelegate(safeAddress, this.contracts.trading);
|
|
4470
|
-
return this.submitDirectFromEoa(encoded);
|
|
4734
|
+
return this.submitDirectEoa(this.getSetupGaslessDelegationEncoded());
|
|
4735
|
+
}
|
|
4736
|
+
getSetupGaslessDelegationTx() {
|
|
4737
|
+
return this.buildDirectEoaTx(this.getSetupGaslessDelegationEncoded());
|
|
4471
4738
|
}
|
|
4472
4739
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4473
4740
|
// USDC helpers
|
|
@@ -4529,18 +4796,14 @@ var OstiumClient = class _OstiumClient {
|
|
|
4529
4796
|
* @param amount USD amount as decimal string (e.g. "1000"), or "max" for MaxUint256.
|
|
4530
4797
|
*/
|
|
4531
4798
|
async approveUsdc(amount) {
|
|
4532
|
-
|
|
4533
|
-
throw new OstiumError(
|
|
4534
|
-
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
4535
|
-
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
4536
|
-
);
|
|
4537
|
-
}
|
|
4538
|
-
const rawAmount = amount === "max" ? viem.maxUint256 : parseUsdc(amount);
|
|
4539
|
-
const encoded = encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
4799
|
+
const encoded = this.getApproveUsdcEncoded(amount);
|
|
4540
4800
|
if (this.selfGasless) {
|
|
4541
|
-
return this.
|
|
4801
|
+
return this.submitDirectEoa(encoded);
|
|
4542
4802
|
}
|
|
4543
|
-
return this.
|
|
4803
|
+
return this.submitPrepared(encoded);
|
|
4804
|
+
}
|
|
4805
|
+
getApproveUsdcTx(amount) {
|
|
4806
|
+
return this.buildDirectEoaTx(this.getApproveUsdcEncoded(amount));
|
|
4544
4807
|
}
|
|
4545
4808
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4546
4809
|
// Delegation helpers
|
|
@@ -4554,16 +4817,20 @@ var OstiumClient = class _OstiumClient {
|
|
|
4554
4817
|
* update the trader's delegation on their behalf.
|
|
4555
4818
|
*/
|
|
4556
4819
|
async setDelegate(delegateAddress) {
|
|
4557
|
-
|
|
4558
|
-
|
|
4820
|
+
return this.submitPrepared(this.getSetDelegateEncoded(delegateAddress));
|
|
4821
|
+
}
|
|
4822
|
+
getSetDelegateTx(delegateAddress) {
|
|
4823
|
+
return this.buildPreparedTx(this.getSetDelegateEncoded(delegateAddress));
|
|
4559
4824
|
}
|
|
4560
4825
|
/**
|
|
4561
4826
|
* Remove the current delegate on the trading contract.
|
|
4562
4827
|
* Follows the same delegation-wrapping rules as setDelegate().
|
|
4563
4828
|
*/
|
|
4564
4829
|
async removeDelegate() {
|
|
4565
|
-
|
|
4566
|
-
|
|
4830
|
+
return this.submitPrepared(this.getRemoveDelegateEncoded());
|
|
4831
|
+
}
|
|
4832
|
+
getRemoveDelegateTx() {
|
|
4833
|
+
return this.buildPreparedTx(this.getRemoveDelegateEncoded());
|
|
4567
4834
|
}
|
|
4568
4835
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4569
4836
|
// Core trading methods
|
|
@@ -4576,6 +4843,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4576
4843
|
* the current allowance.
|
|
4577
4844
|
*/
|
|
4578
4845
|
async openTrade(params) {
|
|
4846
|
+
return this.submitPrepared(this.getOpenTradeEncoded(params));
|
|
4847
|
+
}
|
|
4848
|
+
getOpenTradeTx(params) {
|
|
4849
|
+
return this.buildPreparedTx(this.getOpenTradeEncoded(params));
|
|
4850
|
+
}
|
|
4851
|
+
getOpenTradeEncoded(params) {
|
|
4579
4852
|
const collateralNum = parseFloat(params.collateral);
|
|
4580
4853
|
if (collateralNum > MAX_COLLATERAL_USD) {
|
|
4581
4854
|
throw new OstiumError(
|
|
@@ -4584,7 +4857,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4584
4857
|
);
|
|
4585
4858
|
}
|
|
4586
4859
|
const apiOpen = {
|
|
4587
|
-
a: params.
|
|
4860
|
+
a: Number(params.pairId),
|
|
4588
4861
|
b: params.buy,
|
|
4589
4862
|
p: params.price,
|
|
4590
4863
|
s: params.collateral,
|
|
@@ -4607,8 +4880,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4607
4880
|
}
|
|
4608
4881
|
const builderFee = buildBuilderFee(apiBuilder);
|
|
4609
4882
|
const slippageP = params.type === "market" /* Market */ ? BigInt(this.resolveSlippage(params.slippage)) : 0n;
|
|
4610
|
-
|
|
4611
|
-
return this.execute(encoded);
|
|
4883
|
+
return encodeOpenTrade(trade, builderFee, orderType, slippageP, this.contracts.trading);
|
|
4612
4884
|
}
|
|
4613
4885
|
/**
|
|
4614
4886
|
* Close an open position (full or partial).
|
|
@@ -4623,6 +4895,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4623
4895
|
* ```
|
|
4624
4896
|
*/
|
|
4625
4897
|
async closeTrade(params) {
|
|
4898
|
+
return this.submitPrepared(this.getCloseTradeEncoded(params));
|
|
4899
|
+
}
|
|
4900
|
+
getCloseTradeTx(params) {
|
|
4901
|
+
return this.buildPreparedTx(this.getCloseTradeEncoded(params));
|
|
4902
|
+
}
|
|
4903
|
+
getCloseTradeEncoded(params) {
|
|
4626
4904
|
try {
|
|
4627
4905
|
validateCloseParams({
|
|
4628
4906
|
a: Number(params.pairId),
|
|
@@ -4642,7 +4920,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4642
4920
|
const closePercentage = BigInt(Math.round(params.closePercent * 100));
|
|
4643
4921
|
const marketPrice = parsePrice(params.price);
|
|
4644
4922
|
const slippageP = BigInt(this.resolveSlippage(params.slippage));
|
|
4645
|
-
|
|
4923
|
+
return encodeCloseTradeMarket(
|
|
4646
4924
|
pairIndex,
|
|
4647
4925
|
index,
|
|
4648
4926
|
closePercentage,
|
|
@@ -4650,7 +4928,6 @@ var OstiumClient = class _OstiumClient {
|
|
|
4650
4928
|
slippageP,
|
|
4651
4929
|
this.contracts.trading
|
|
4652
4930
|
);
|
|
4653
|
-
return this.execute(encoded);
|
|
4654
4931
|
}
|
|
4655
4932
|
/**
|
|
4656
4933
|
* Cancel a pending order.
|
|
@@ -4673,6 +4950,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4673
4950
|
* ```
|
|
4674
4951
|
*/
|
|
4675
4952
|
async cancelOrder(params) {
|
|
4953
|
+
return this.submitPrepared(this.getCancelOrderEncoded(params));
|
|
4954
|
+
}
|
|
4955
|
+
getCancelOrderTx(params) {
|
|
4956
|
+
return this.buildPreparedTx(this.getCancelOrderEncoded(params));
|
|
4957
|
+
}
|
|
4958
|
+
getCancelOrderEncoded(params) {
|
|
4676
4959
|
let encoded;
|
|
4677
4960
|
if (params.type === "limit" /* Limit */) {
|
|
4678
4961
|
const pairIdNum = Number(params.pairId);
|
|
@@ -4709,7 +4992,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4709
4992
|
}
|
|
4710
4993
|
encoded = encodeOpenTradeMarketTimeout(BigInt(params.orderId), this.contracts.trading);
|
|
4711
4994
|
}
|
|
4712
|
-
return
|
|
4995
|
+
return encoded;
|
|
4713
4996
|
}
|
|
4714
4997
|
/**
|
|
4715
4998
|
* Modify an open trade or a pending limit order.
|
|
@@ -4734,6 +5017,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4734
5017
|
* - Both `takeProfit` and `stopLoss` without `price` → throws (send two calls).
|
|
4735
5018
|
*/
|
|
4736
5019
|
async modifyOrder(params) {
|
|
5020
|
+
return this.submitPrepared(this.getModifyOrderEncoded(params));
|
|
5021
|
+
}
|
|
5022
|
+
getModifyOrderTx(params) {
|
|
5023
|
+
return this.buildPreparedTx(this.getModifyOrderEncoded(params));
|
|
5024
|
+
}
|
|
5025
|
+
getModifyOrderEncoded(params) {
|
|
4737
5026
|
try {
|
|
4738
5027
|
validateModifyParams({
|
|
4739
5028
|
a: Number(params.pairId),
|
|
@@ -4773,7 +5062,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4773
5062
|
"VALIDATION_FAILED" /* VALIDATION_FAILED */
|
|
4774
5063
|
);
|
|
4775
5064
|
}
|
|
4776
|
-
return
|
|
5065
|
+
return encoded;
|
|
4777
5066
|
}
|
|
4778
5067
|
/**
|
|
4779
5068
|
* Update collateral on an open position (isolated margin).
|
|
@@ -4793,6 +5082,12 @@ var OstiumClient = class _OstiumClient {
|
|
|
4793
5082
|
* Checks USDC allowance before top-up operations.
|
|
4794
5083
|
*/
|
|
4795
5084
|
async updateCollateral(params) {
|
|
5085
|
+
return this.submitPrepared(await this.getUpdateCollateralEncoded(params));
|
|
5086
|
+
}
|
|
5087
|
+
async getUpdateCollateralTx(params) {
|
|
5088
|
+
return this.buildPreparedTx(await this.getUpdateCollateralEncoded(params));
|
|
5089
|
+
}
|
|
5090
|
+
async getUpdateCollateralEncoded(params) {
|
|
4796
5091
|
const amount = parseFloat(params.amount);
|
|
4797
5092
|
if (Number.isNaN(amount) || amount === 0) {
|
|
4798
5093
|
throw new OstiumError(
|
|
@@ -4816,7 +5111,7 @@ var OstiumClient = class _OstiumClient {
|
|
|
4816
5111
|
} else {
|
|
4817
5112
|
encoded = encodeRemoveCollateral(pairIndex, index, absAmount, this.contracts.trading);
|
|
4818
5113
|
}
|
|
4819
|
-
return
|
|
5114
|
+
return encoded;
|
|
4820
5115
|
}
|
|
4821
5116
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4822
5117
|
// Subgraph reads (Hyperliquid-style API)
|
|
@@ -4944,31 +5239,104 @@ var OstiumClient = class _OstiumClient {
|
|
|
4944
5239
|
streamPrices(pairIds) {
|
|
4945
5240
|
return this.subgraph.streamPrices(pairIds);
|
|
4946
5241
|
}
|
|
5242
|
+
/**
|
|
5243
|
+
* Stream price-driven updates for an existing `getOpenPositions()` response.
|
|
5244
|
+
*
|
|
5245
|
+
* Subscribes only to the unique pairs referenced by the response and emits the
|
|
5246
|
+
* full updated positions payload on each relevant price update. Pass an
|
|
5247
|
+
* existing `priceStream` to reuse a websocket your app already owns, or omit
|
|
5248
|
+
* it to let the SDK create a dedicated connection.
|
|
5249
|
+
*/
|
|
5250
|
+
streamPositionUpdates(initial, priceStream) {
|
|
5251
|
+
return this.subgraph.streamPositionUpdates(initial, priceStream);
|
|
5252
|
+
}
|
|
4947
5253
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4948
5254
|
// Internal helpers
|
|
4949
5255
|
// ─────────────────────────────────────────────────────────────────────────
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
* 1. signer.prepare() — optionally wraps in delegatedAction.
|
|
4953
|
-
* 2. submitter.submit() — sends via EOA walletClient or Pimlico UserOp.
|
|
4954
|
-
*/
|
|
4955
|
-
async execute(encoded) {
|
|
4956
|
-
const { signer, submitter } = this.requireWriter();
|
|
4957
|
-
const prepared = signer.prepare(encoded);
|
|
4958
|
-
return submitter.submit(prepared);
|
|
4959
|
-
}
|
|
4960
|
-
/** Throws if the client is read-only; otherwise returns the signer + submitter. */
|
|
4961
|
-
requireWriter() {
|
|
4962
|
-
if (!this.signer || !this.submitter) {
|
|
5256
|
+
getSetupGaslessDelegationEncoded() {
|
|
5257
|
+
if (!this.selfGasless || !this.effectiveSender) {
|
|
4963
5258
|
throw new OstiumError(
|
|
4964
|
-
"
|
|
5259
|
+
"setupGaslessDelegation() is only available in Self + Gasless mode.",
|
|
4965
5260
|
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
4966
5261
|
);
|
|
4967
5262
|
}
|
|
4968
|
-
return
|
|
5263
|
+
return encodeSetDelegate(this.effectiveSender, this.contracts.trading);
|
|
5264
|
+
}
|
|
5265
|
+
getApproveUsdcEncoded(amount) {
|
|
5266
|
+
if (this.delegated) {
|
|
5267
|
+
throw new OstiumError(
|
|
5268
|
+
"approveUsdc is not available in delegated mode. The trader must approve USDC directly from their own account.",
|
|
5269
|
+
"DELEGATION_FAILED" /* DELEGATION_FAILED */
|
|
5270
|
+
);
|
|
5271
|
+
}
|
|
5272
|
+
const rawAmount = amount === "max" ? viem.maxUint256 : parseUsdc(amount);
|
|
5273
|
+
return encodeUsdcApprove(this.contracts.tradingStorage, rawAmount, this.contracts.usdc);
|
|
5274
|
+
}
|
|
5275
|
+
getSetDelegateEncoded(delegateAddress) {
|
|
5276
|
+
return encodeSetDelegate(delegateAddress, this.contracts.trading);
|
|
5277
|
+
}
|
|
5278
|
+
getRemoveDelegateEncoded() {
|
|
5279
|
+
return encodeRemoveDelegate(this.contracts.trading);
|
|
5280
|
+
}
|
|
5281
|
+
prepareEncoded(encoded) {
|
|
5282
|
+
const signer = this.requireBuildCapability();
|
|
5283
|
+
return signer.prepare(encoded);
|
|
5284
|
+
}
|
|
5285
|
+
buildPreparedTx(encoded) {
|
|
5286
|
+
return this.toBuiltTxRequest(this.prepareEncoded(encoded));
|
|
5287
|
+
}
|
|
5288
|
+
buildDirectEoaTx(encoded) {
|
|
5289
|
+
if (!this.canBuildTransactions() || !this.traderAddress) {
|
|
5290
|
+
throw new OstiumError(
|
|
5291
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5292
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5293
|
+
);
|
|
5294
|
+
}
|
|
5295
|
+
return {
|
|
5296
|
+
kind: "eoa",
|
|
5297
|
+
to: encoded.to,
|
|
5298
|
+
data: encoded.data,
|
|
5299
|
+
value: encoded.value,
|
|
5300
|
+
from: this.traderAddress,
|
|
5301
|
+
traderAddress: this.traderAddress
|
|
5302
|
+
};
|
|
5303
|
+
}
|
|
5304
|
+
toBuiltTxRequest(encoded) {
|
|
5305
|
+
if (!this.canBuildTransactions() || !this.traderAddress || !this.effectiveSender) {
|
|
5306
|
+
throw new OstiumError(
|
|
5307
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5308
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5309
|
+
);
|
|
5310
|
+
}
|
|
5311
|
+
if (this.gasless) {
|
|
5312
|
+
return {
|
|
5313
|
+
kind: "safe",
|
|
5314
|
+
safeAddress: this.effectiveSender,
|
|
5315
|
+
traderAddress: this.traderAddress,
|
|
5316
|
+
calls: [encoded]
|
|
5317
|
+
};
|
|
5318
|
+
}
|
|
5319
|
+
return {
|
|
5320
|
+
kind: "eoa",
|
|
5321
|
+
to: encoded.to,
|
|
5322
|
+
data: encoded.data,
|
|
5323
|
+
value: encoded.value,
|
|
5324
|
+
from: this.effectiveSender,
|
|
5325
|
+
traderAddress: this.traderAddress
|
|
5326
|
+
};
|
|
5327
|
+
}
|
|
5328
|
+
async submitPrepared(encoded) {
|
|
5329
|
+
const submitter = this.requireSubmitCapability();
|
|
5330
|
+
return submitter.submit(this.prepareEncoded(encoded));
|
|
4969
5331
|
}
|
|
4970
5332
|
/** Direct EOA submission — used only in Self + Gasless for approveUsdc and setupGaslessDelegation. */
|
|
4971
|
-
async
|
|
5333
|
+
async submitDirectEoa(encoded) {
|
|
5334
|
+
if (!this.eoaSubmit) {
|
|
5335
|
+
throw new OstiumError(
|
|
5336
|
+
"This client cannot submit direct EOA transactions.",
|
|
5337
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5338
|
+
);
|
|
5339
|
+
}
|
|
4972
5340
|
try {
|
|
4973
5341
|
return await this.eoaSubmit(encoded);
|
|
4974
5342
|
} catch (err) {
|
|
@@ -4984,6 +5352,24 @@ var OstiumClient = class _OstiumClient {
|
|
|
4984
5352
|
throw new OstiumError(`Transaction failed: ${msg}`, "SUBMISSION_FAILED" /* SUBMISSION_FAILED */, err);
|
|
4985
5353
|
}
|
|
4986
5354
|
}
|
|
5355
|
+
requireBuildCapability() {
|
|
5356
|
+
if (!this.signer) {
|
|
5357
|
+
throw new OstiumError(
|
|
5358
|
+
"This client cannot build transactions. Use one of the createSelfAnd*/createDelegatedAnd* factories.",
|
|
5359
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5360
|
+
);
|
|
5361
|
+
}
|
|
5362
|
+
return this.signer;
|
|
5363
|
+
}
|
|
5364
|
+
requireSubmitCapability() {
|
|
5365
|
+
if (!this.submitter) {
|
|
5366
|
+
throw new OstiumError(
|
|
5367
|
+
"This client does not have submission credentials. Use the corresponding get*Tx() method or initialize with a private key.",
|
|
5368
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */
|
|
5369
|
+
);
|
|
5370
|
+
}
|
|
5371
|
+
return this.submitter;
|
|
5372
|
+
}
|
|
4987
5373
|
resolveSlippage(override) {
|
|
4988
5374
|
return override ?? this.defaultSlippageBps;
|
|
4989
5375
|
}
|
|
@@ -5000,6 +5386,7 @@ exports.OrderType = OrderType;
|
|
|
5000
5386
|
exports.OstiumClient = OstiumClient;
|
|
5001
5387
|
exports.OstiumError = OstiumError;
|
|
5002
5388
|
exports.OstiumErrorCode = OstiumErrorCode;
|
|
5389
|
+
exports.OstiumPositionUpdatesStream = OstiumPositionUpdatesStream;
|
|
5003
5390
|
exports.OstiumPriceStream = OstiumPriceStream;
|
|
5004
5391
|
exports.OstiumSubgraphClient = OstiumSubgraphClient;
|
|
5005
5392
|
exports.OstiumSubgraphError = OstiumSubgraphError;
|