@zofai/zo-sdk 0.2.30 → 0.2.31
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/dist/abstract/BaseAPI.cjs +3 -0
- package/dist/abstract/BaseAPI.cjs.map +1 -1
- package/dist/abstract/BaseAPI.d.cts +3 -2
- package/dist/abstract/BaseAPI.d.cts.map +1 -1
- package/dist/abstract/BaseAPI.d.mts +3 -2
- package/dist/abstract/BaseAPI.d.mts.map +1 -1
- package/dist/abstract/BaseAPI.mjs +3 -0
- package/dist/abstract/BaseAPI.mjs.map +1 -1
- package/dist/abstract/BaseDataAPI.cjs +48 -2
- package/dist/abstract/BaseDataAPI.cjs.map +1 -1
- package/dist/abstract/BaseDataAPI.d.cts +15 -2
- package/dist/abstract/BaseDataAPI.d.cts.map +1 -1
- package/dist/abstract/BaseDataAPI.d.mts +15 -2
- package/dist/abstract/BaseDataAPI.d.mts.map +1 -1
- package/dist/abstract/BaseDataAPI.mjs +48 -2
- package/dist/abstract/BaseDataAPI.mjs.map +1 -1
- package/dist/data.cjs +19 -7
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.cts.map +1 -1
- package/dist/data.d.mts.map +1 -1
- package/dist/data.mjs +19 -7
- package/dist/data.mjs.map +1 -1
- package/dist/implementations/SLPDataAPI.cjs +26 -8
- package/dist/implementations/SLPDataAPI.cjs.map +1 -1
- package/dist/implementations/SLPDataAPI.d.cts.map +1 -1
- package/dist/implementations/SLPDataAPI.d.mts.map +1 -1
- package/dist/implementations/SLPDataAPI.mjs +26 -8
- package/dist/implementations/SLPDataAPI.mjs.map +1 -1
- package/dist/implementations/USDZDataAPI.cjs +26 -8
- package/dist/implementations/USDZDataAPI.cjs.map +1 -1
- package/dist/implementations/USDZDataAPI.d.cts.map +1 -1
- package/dist/implementations/USDZDataAPI.d.mts.map +1 -1
- package/dist/implementations/USDZDataAPI.mjs +26 -8
- package/dist/implementations/USDZDataAPI.mjs.map +1 -1
- package/dist/implementations/ZBTCVCDataAPI.cjs +19 -7
- package/dist/implementations/ZBTCVCDataAPI.cjs.map +1 -1
- package/dist/implementations/ZBTCVCDataAPI.d.cts.map +1 -1
- package/dist/implementations/ZBTCVCDataAPI.d.mts.map +1 -1
- package/dist/implementations/ZBTCVCDataAPI.mjs +19 -7
- package/dist/implementations/ZBTCVCDataAPI.mjs.map +1 -1
- package/dist/implementations/ZLPDataAPI.cjs +26 -8
- package/dist/implementations/ZLPDataAPI.cjs.map +1 -1
- package/dist/implementations/ZLPDataAPI.d.cts.map +1 -1
- package/dist/implementations/ZLPDataAPI.d.mts.map +1 -1
- package/dist/implementations/ZLPDataAPI.mjs +26 -8
- package/dist/implementations/ZLPDataAPI.mjs.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/interfaces/base.d.cts +14 -1
- package/dist/interfaces/base.d.cts.map +1 -1
- package/dist/interfaces/base.d.mts +14 -1
- package/dist/interfaces/base.d.mts.map +1 -1
- package/dist/traderPositions.cjs +130 -0
- package/dist/traderPositions.cjs.map +1 -0
- package/dist/traderPositions.d.cts +72 -0
- package/dist/traderPositions.d.cts.map +1 -0
- package/dist/traderPositions.d.mts +72 -0
- package/dist/traderPositions.d.mts.map +1 -0
- package/dist/traderPositions.mjs +124 -0
- package/dist/traderPositions.mjs.map +1 -0
- package/package.json +1 -1
- package/src/abstract/BaseAPI.ts +6 -2
- package/src/abstract/BaseDataAPI.ts +67 -3
- package/src/data.ts +19 -7
- package/src/implementations/SLPDataAPI.ts +34 -16
- package/src/implementations/USDZDataAPI.ts +32 -14
- package/src/implementations/ZBTCVCDataAPI.ts +23 -11
- package/src/implementations/ZLPDataAPI.ts +37 -14
- package/src/index.ts +1 -0
- package/src/interfaces/base.ts +15 -1
- package/src/traderPositions.ts +218 -0
|
@@ -307,7 +307,7 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
307
307
|
public async getMarketInfo(): Promise<IZLPMarketInfo> {
|
|
308
308
|
this.validateCache()
|
|
309
309
|
if (this.marketInfoCache) {
|
|
310
|
-
return this.marketInfoCache
|
|
310
|
+
return this.marketInfoCache as IZLPMarketInfo
|
|
311
311
|
}
|
|
312
312
|
const rawData = await this.provider.getObject({
|
|
313
313
|
id: this.consts.zoCore.market,
|
|
@@ -315,7 +315,9 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
315
315
|
showContent: true,
|
|
316
316
|
},
|
|
317
317
|
})
|
|
318
|
-
|
|
318
|
+
const info = ZLPDataAPI.parseMarketInfo(rawData)
|
|
319
|
+
this.marketInfoCache = info
|
|
320
|
+
return info
|
|
319
321
|
}
|
|
320
322
|
|
|
321
323
|
/**
|
|
@@ -324,7 +326,7 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
324
326
|
public async getVaultInfo(vaultToken: string): Promise<IZLPVaultInfo> {
|
|
325
327
|
this.validateCache()
|
|
326
328
|
if (this.vaultInfoCache[vaultToken]) {
|
|
327
|
-
return this.vaultInfoCache[vaultToken]
|
|
329
|
+
return this.vaultInfoCache[vaultToken] as IZLPVaultInfo
|
|
328
330
|
}
|
|
329
331
|
|
|
330
332
|
const rawData = await this.provider.getDynamicFieldObject({
|
|
@@ -334,7 +336,9 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
334
336
|
value: { dummy_field: false },
|
|
335
337
|
},
|
|
336
338
|
})
|
|
337
|
-
|
|
339
|
+
const info = await this.parseVaultInfo(rawData)
|
|
340
|
+
this.vaultInfoCache[vaultToken] = info
|
|
341
|
+
return info
|
|
338
342
|
}
|
|
339
343
|
|
|
340
344
|
/**
|
|
@@ -344,7 +348,7 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
344
348
|
this.validateCache()
|
|
345
349
|
const symbol = joinSymbol(long ? 'long' : 'short', indexToken)
|
|
346
350
|
if (this.symbolInfoCache[symbol]) {
|
|
347
|
-
return this.symbolInfoCache[symbol]
|
|
351
|
+
return this.symbolInfoCache[symbol] as IZLPSymbolInfo
|
|
348
352
|
}
|
|
349
353
|
const rawData = await this.provider.getDynamicFieldObject({
|
|
350
354
|
parentId: this.consts.zoCore.symbolsParent,
|
|
@@ -353,14 +357,16 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
353
357
|
value: { dummy_field: false },
|
|
354
358
|
},
|
|
355
359
|
})
|
|
356
|
-
|
|
360
|
+
const info = await this.parseSymbolInfo(rawData, long)
|
|
361
|
+
this.symbolInfoCache[symbol] = info
|
|
362
|
+
return info
|
|
357
363
|
}
|
|
358
364
|
|
|
359
365
|
public async getPositionConfig(indexToken: string, long: boolean): Promise<IZLPPositionConfig> {
|
|
360
366
|
this.validateCache()
|
|
361
367
|
const symbol = joinSymbol(long ? 'long' : 'short', indexToken)
|
|
362
368
|
if (this.positionConfigCache[symbol]) {
|
|
363
|
-
return this.positionConfigCache[symbol]
|
|
369
|
+
return this.positionConfigCache[symbol] as IZLPPositionConfig
|
|
364
370
|
}
|
|
365
371
|
const rawData = await this.provider.getObject({
|
|
366
372
|
id: this.consts.zoCore.symbols[symbol].positionConfig,
|
|
@@ -368,7 +374,9 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
368
374
|
showContent: true,
|
|
369
375
|
},
|
|
370
376
|
})
|
|
371
|
-
|
|
377
|
+
const info = ZLPDataAPI.parsePositionConfig(rawData)
|
|
378
|
+
this.positionConfigCache[symbol] = info
|
|
379
|
+
return info
|
|
372
380
|
}
|
|
373
381
|
|
|
374
382
|
/**
|
|
@@ -398,6 +406,9 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
398
406
|
*/
|
|
399
407
|
public async getSymbolOiFundingState(indexToken: string): Promise<IZLPOiFundingState | null> {
|
|
400
408
|
this.validateCache()
|
|
409
|
+
if (Object.prototype.hasOwnProperty.call(this.oiFundingStateCache, indexToken)) {
|
|
410
|
+
return this.oiFundingStateCache[indexToken] as IZLPOiFundingState | null
|
|
411
|
+
}
|
|
401
412
|
try {
|
|
402
413
|
const rawData = await this.provider.getDynamicFieldObject({
|
|
403
414
|
parentId: this.consts.zoCore.market,
|
|
@@ -406,11 +417,14 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
406
417
|
value: { dummy_field: false },
|
|
407
418
|
},
|
|
408
419
|
})
|
|
409
|
-
|
|
420
|
+
const info = ZLPDataAPI.parseOiFundingState(rawData)
|
|
421
|
+
this.oiFundingStateCache[indexToken] = info
|
|
422
|
+
return info
|
|
410
423
|
}
|
|
411
424
|
catch (e: any) {
|
|
412
425
|
// If the dynamic field doesn't exist, return null
|
|
413
426
|
console.error('Error Fetching Symbol OI Funding State:', e)
|
|
427
|
+
this.oiFundingStateCache[indexToken] = null
|
|
414
428
|
return null
|
|
415
429
|
}
|
|
416
430
|
}
|
|
@@ -839,7 +853,7 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
839
853
|
public async getRebaseFeeModel(): Promise<IZLPRebaseFeeModel> {
|
|
840
854
|
this.validateCache()
|
|
841
855
|
if (this.rebaseFeeModelCache) {
|
|
842
|
-
return this.rebaseFeeModelCache
|
|
856
|
+
return this.rebaseFeeModelCache as IZLPRebaseFeeModel
|
|
843
857
|
}
|
|
844
858
|
const rawData = await this.provider.getObject({
|
|
845
859
|
id: this.consts.zoCore.rebaseFeeModel,
|
|
@@ -853,7 +867,9 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
853
867
|
this.consts.zoCore.rebaseFeeModel,
|
|
854
868
|
this.consts.zoCore.upgradedPackage,
|
|
855
869
|
)
|
|
856
|
-
|
|
870
|
+
const info = { ...model, exponent }
|
|
871
|
+
this.rebaseFeeModelCache = info
|
|
872
|
+
return info
|
|
857
873
|
}
|
|
858
874
|
|
|
859
875
|
public async fundingFeeRate(indexToken: string, long: boolean): Promise<number> {
|
|
@@ -1246,14 +1262,21 @@ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
|
|
|
1246
1262
|
openTimestamp: parseValue(positionFields.open_timestamp),
|
|
1247
1263
|
}
|
|
1248
1264
|
|
|
1249
|
-
|
|
1265
|
+
const vaultInfo = await this.getVaultInfo(positionInfo.collateralToken)
|
|
1266
|
+
positionInfo.reservingFeeAmount = ZLPDataAPI.calculatePositionReserveFee(
|
|
1267
|
+
positionInfo,
|
|
1268
|
+
vaultInfo,
|
|
1269
|
+
vaultInfo.reservingFeeModel,
|
|
1270
|
+
Date.now() / 1000,
|
|
1271
|
+
)
|
|
1250
1272
|
// OI context for funding: fetch state and paired side size when enabled
|
|
1251
1273
|
const oiState = await this.getSymbolOiFundingState(positionInfo.indexToken)
|
|
1252
1274
|
const pairedSymbol = await this.getSymbolInfo(positionInfo.indexToken, !positionInfo.long)
|
|
1275
|
+
const symbolInfo = await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long)
|
|
1253
1276
|
const computedFundingFeeValue = ZLPDataAPI.calculatePositionFundingFee(
|
|
1254
1277
|
positionInfo,
|
|
1255
|
-
|
|
1256
|
-
|
|
1278
|
+
symbolInfo,
|
|
1279
|
+
symbolInfo.fundingFeeModel,
|
|
1257
1280
|
(await this.getOraclePrice(positionInfo.indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked(),
|
|
1258
1281
|
(await this.getMarketInfo()).lpSupplyWithDecimals,
|
|
1259
1282
|
Date.now() / 1000,
|
package/src/index.ts
CHANGED
|
@@ -33,6 +33,7 @@ export * from './oraclePro'
|
|
|
33
33
|
export type { PythProChannel } from './pythProClient'
|
|
34
34
|
export * from './storkClient'
|
|
35
35
|
export * from './storkOracle'
|
|
36
|
+
export * from './traderPositions'
|
|
36
37
|
export * from './utils'
|
|
37
38
|
|
|
38
39
|
// Re-export commonly used types from Sui SDK
|
package/src/interfaces/base.ts
CHANGED
|
@@ -196,6 +196,15 @@ export interface IBasePositionCapInfo {
|
|
|
196
196
|
long: boolean
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
export interface IGetTraderOpenPositionsOptions {
|
|
200
|
+
indexToken?: string
|
|
201
|
+
collateralToken?: string
|
|
202
|
+
/** RPC batch size for getPositionInfoList (default 10). */
|
|
203
|
+
batchSize?: number
|
|
204
|
+
/** Max OPEN rows to load from the API (default: all pages). */
|
|
205
|
+
limit?: number
|
|
206
|
+
}
|
|
207
|
+
|
|
199
208
|
export interface IBaseOrderCapInfo {
|
|
200
209
|
orderCapId: string
|
|
201
210
|
symbol0: string
|
|
@@ -291,6 +300,11 @@ export interface IBaseDataAPI {
|
|
|
291
300
|
// User data methods
|
|
292
301
|
getPositionCapInfoList: (owner: string) => Promise<IBasePositionCapInfo[]>
|
|
293
302
|
getPositionInfoList: (positionCapInfoList: IBasePositionCapInfo[], owner: string, batchSize?: number) => Promise<IBasePositionInfo[]>
|
|
303
|
+
/**
|
|
304
|
+
* Fetch OPEN positions from zo API `/trader-positions`, then hydrate live details via RPC.
|
|
305
|
+
* Skips on-chain PositionCap discovery; uses indexed position ids + symbol metadata.
|
|
306
|
+
*/
|
|
307
|
+
getTraderOpenPositionInfoList: (owner: string, options?: IGetTraderOpenPositionsOptions) => Promise<IBasePositionInfo[]>
|
|
294
308
|
getOrderCapInfoList: (owner: string) => Promise<IBaseOrderCapInfo[]>
|
|
295
309
|
getOrderInfoList: (orderCapInfoList: IBaseOrderCapInfo[], owner: string, batchSize?: number) => Promise<IBaseOrderInfo[]>
|
|
296
310
|
getHistory: (trader: string, page: number, limit: number, orderType?: string, symbol?: string) => Promise<IBaseHistoryResponse>
|
|
@@ -330,7 +344,7 @@ export interface IBaseDataAPI {
|
|
|
330
344
|
|
|
331
345
|
resolveStorkAssetsForTokens: (tokens: string[]) => string[]
|
|
332
346
|
|
|
333
|
-
fetchStorkUpdateDataForTokens: (tokens: string[]) => Promise<import('../storkClient').IStorkUpdateData
|
|
347
|
+
fetchStorkUpdateDataForTokens: (tokens: string[]) => Promise<Array<import('../storkClient').IStorkUpdateData>>
|
|
334
348
|
|
|
335
349
|
estimateStorkUpdateFeeForTokens: (tokens: string[]) => bigint
|
|
336
350
|
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { IConsts } from './consts'
|
|
2
|
+
import type { IBasePositionCapInfo } from './interfaces'
|
|
3
|
+
|
|
4
|
+
export type TraderPositionStatus = 'OPEN' | 'CLOSED' | 'LIQUIDATED' | 'UNKNOWN'
|
|
5
|
+
|
|
6
|
+
/** Row shape from GET /trader-positions (zo API). */
|
|
7
|
+
export interface ITraderPositionRecord {
|
|
8
|
+
positionId: string
|
|
9
|
+
owner: string
|
|
10
|
+
address: string
|
|
11
|
+
chain?: string
|
|
12
|
+
pool?: string | null
|
|
13
|
+
collateralToken?: string | null
|
|
14
|
+
indexToken?: string | null
|
|
15
|
+
direction?: string | null
|
|
16
|
+
status: TraderPositionStatus
|
|
17
|
+
closed?: boolean | null
|
|
18
|
+
openedAt?: string | null
|
|
19
|
+
closedAt?: string | null
|
|
20
|
+
updatedAt?: string
|
|
21
|
+
lastOrderId?: string | null
|
|
22
|
+
lastEventName?: string | null
|
|
23
|
+
lastTxDigest?: string | null
|
|
24
|
+
lastEventSeq?: string | null
|
|
25
|
+
positionNameType?: string | null
|
|
26
|
+
openAmount?: string | null
|
|
27
|
+
decreaseAmount?: string | null
|
|
28
|
+
collateralAmount?: string | null
|
|
29
|
+
reserveAmount?: string | null
|
|
30
|
+
positionSize?: string | null
|
|
31
|
+
settledAmount?: string | null
|
|
32
|
+
collateralPrice?: number | null
|
|
33
|
+
indexPrice?: number | null
|
|
34
|
+
pnl?: number | null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ITraderPositionsPagination {
|
|
38
|
+
total: number
|
|
39
|
+
limit: number
|
|
40
|
+
offset: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ITraderPositionsQuery {
|
|
44
|
+
owner: string
|
|
45
|
+
status?: TraderPositionStatus
|
|
46
|
+
indexToken?: string
|
|
47
|
+
collateralToken?: string
|
|
48
|
+
limit?: number
|
|
49
|
+
offset?: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ITraderPositionsApiResponse {
|
|
53
|
+
success: boolean
|
|
54
|
+
data?: {
|
|
55
|
+
positions: ITraderPositionRecord[]
|
|
56
|
+
pagination: ITraderPositionsPagination
|
|
57
|
+
}
|
|
58
|
+
error?: string
|
|
59
|
+
message?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const DEFAULT_PAGE_LIMIT = 200
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parse `PositionName<collateral, index, LONG|SHORT>` from a Move type string.
|
|
66
|
+
*/
|
|
67
|
+
export function parsePositionNameType(positionNameType: string): {
|
|
68
|
+
symbol0: string
|
|
69
|
+
symbol1: string
|
|
70
|
+
long: boolean
|
|
71
|
+
} | null {
|
|
72
|
+
const marker = 'PositionName<'
|
|
73
|
+
const start = positionNameType.indexOf(marker)
|
|
74
|
+
if (start < 0) {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
const inner = positionNameType.slice(start + marker.length).split('>')[0]
|
|
78
|
+
if (!inner) {
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
const parts = inner.split(',').map(part => part.trim())
|
|
82
|
+
if (parts.length < 3) {
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
symbol0: parts[0],
|
|
87
|
+
symbol1: parts[1],
|
|
88
|
+
long: parts[2].includes('LONG'),
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function tokenKeyToMoveType(tokenKey: string, consts: IConsts): string | null {
|
|
93
|
+
const key = tokenKey.toLowerCase()
|
|
94
|
+
if (key === 'sui') {
|
|
95
|
+
return '0x2::sui::SUI'
|
|
96
|
+
}
|
|
97
|
+
const coin = consts.coins[key]
|
|
98
|
+
return coin?.module ?? null
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Map an indexed trader position row to PositionCap lookup fields for on-chain hydration. */
|
|
102
|
+
export function traderPositionToCapInfo(
|
|
103
|
+
record: ITraderPositionRecord,
|
|
104
|
+
consts: IConsts,
|
|
105
|
+
): IBasePositionCapInfo | null {
|
|
106
|
+
if (!record.positionId) {
|
|
107
|
+
return null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const fromType = record.positionNameType
|
|
111
|
+
? parsePositionNameType(record.positionNameType)
|
|
112
|
+
: null
|
|
113
|
+
if (fromType) {
|
|
114
|
+
return {
|
|
115
|
+
positionCapId: record.positionId,
|
|
116
|
+
symbol0: fromType.symbol0,
|
|
117
|
+
symbol1: fromType.symbol1,
|
|
118
|
+
long: fromType.long,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const collateral = record.collateralToken?.trim().toLowerCase()
|
|
123
|
+
const index = record.indexToken?.trim().toLowerCase()
|
|
124
|
+
if (!collateral || !index) {
|
|
125
|
+
return null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const symbol0 = tokenKeyToMoveType(collateral, consts)
|
|
129
|
+
const symbol1 = tokenKeyToMoveType(index, consts)
|
|
130
|
+
if (!symbol0 || !symbol1) {
|
|
131
|
+
return null
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
positionCapId: record.positionId,
|
|
136
|
+
symbol0,
|
|
137
|
+
symbol1,
|
|
138
|
+
long: (record.direction?.toUpperCase() ?? 'LONG') === 'LONG',
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function fetchTraderPositionsPage(
|
|
143
|
+
apiEndpoint: string,
|
|
144
|
+
query: ITraderPositionsQuery,
|
|
145
|
+
): Promise<{ positions: ITraderPositionRecord[], pagination: ITraderPositionsPagination }> {
|
|
146
|
+
if (!apiEndpoint) {
|
|
147
|
+
throw new Error('apiEndpoint is required to fetch trader positions')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const params = new URLSearchParams({
|
|
151
|
+
owner: query.owner.trim().toLowerCase(),
|
|
152
|
+
})
|
|
153
|
+
if (query.status) {
|
|
154
|
+
params.set('status', query.status)
|
|
155
|
+
}
|
|
156
|
+
if (query.indexToken) {
|
|
157
|
+
params.set('indexToken', query.indexToken.trim().toLowerCase())
|
|
158
|
+
}
|
|
159
|
+
if (query.collateralToken) {
|
|
160
|
+
params.set('collateralToken', query.collateralToken.trim().toLowerCase())
|
|
161
|
+
}
|
|
162
|
+
if (query.limit != null) {
|
|
163
|
+
params.set('limit', String(query.limit))
|
|
164
|
+
}
|
|
165
|
+
if (query.offset != null) {
|
|
166
|
+
params.set('offset', String(query.offset))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const url = `${apiEndpoint.replace(/\/$/, '')}/trader-positions?${params}`
|
|
170
|
+
const res = await fetch(url, {
|
|
171
|
+
method: 'GET',
|
|
172
|
+
headers: { 'Content-Type': 'application/json' },
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
const body = await res.json() as ITraderPositionsApiResponse
|
|
176
|
+
if (!res.ok || !body.success || !body.data) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
body.error || body.message || `Failed to fetch trader positions (${res.status})`,
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return body.data
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Fetch all pages matching the query (respects optional total `limit`). */
|
|
186
|
+
export async function fetchAllTraderPositions(
|
|
187
|
+
apiEndpoint: string,
|
|
188
|
+
query: Omit<ITraderPositionsQuery, 'limit' | 'offset'>,
|
|
189
|
+
maxRows?: number,
|
|
190
|
+
): Promise<ITraderPositionRecord[]> {
|
|
191
|
+
const rows: ITraderPositionRecord[] = []
|
|
192
|
+
let offset = 0
|
|
193
|
+
let total = Number.POSITIVE_INFINITY
|
|
194
|
+
|
|
195
|
+
while (offset < total) {
|
|
196
|
+
const remaining = maxRows != null ? maxRows - rows.length : DEFAULT_PAGE_LIMIT
|
|
197
|
+
if (remaining <= 0) {
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const pageLimit = Math.min(DEFAULT_PAGE_LIMIT, remaining)
|
|
202
|
+
const { positions, pagination } = await fetchTraderPositionsPage(apiEndpoint, {
|
|
203
|
+
...query,
|
|
204
|
+
limit: pageLimit,
|
|
205
|
+
offset,
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
rows.push(...positions)
|
|
209
|
+
total = pagination.total
|
|
210
|
+
offset += positions.length
|
|
211
|
+
|
|
212
|
+
if (positions.length === 0 || offset >= total) {
|
|
213
|
+
break
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return maxRows != null ? rows.slice(0, maxRows) : rows
|
|
218
|
+
}
|