@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.
Files changed (77) hide show
  1. package/dist/abstract/BaseAPI.cjs +3 -0
  2. package/dist/abstract/BaseAPI.cjs.map +1 -1
  3. package/dist/abstract/BaseAPI.d.cts +3 -2
  4. package/dist/abstract/BaseAPI.d.cts.map +1 -1
  5. package/dist/abstract/BaseAPI.d.mts +3 -2
  6. package/dist/abstract/BaseAPI.d.mts.map +1 -1
  7. package/dist/abstract/BaseAPI.mjs +3 -0
  8. package/dist/abstract/BaseAPI.mjs.map +1 -1
  9. package/dist/abstract/BaseDataAPI.cjs +48 -2
  10. package/dist/abstract/BaseDataAPI.cjs.map +1 -1
  11. package/dist/abstract/BaseDataAPI.d.cts +15 -2
  12. package/dist/abstract/BaseDataAPI.d.cts.map +1 -1
  13. package/dist/abstract/BaseDataAPI.d.mts +15 -2
  14. package/dist/abstract/BaseDataAPI.d.mts.map +1 -1
  15. package/dist/abstract/BaseDataAPI.mjs +48 -2
  16. package/dist/abstract/BaseDataAPI.mjs.map +1 -1
  17. package/dist/data.cjs +19 -7
  18. package/dist/data.cjs.map +1 -1
  19. package/dist/data.d.cts.map +1 -1
  20. package/dist/data.d.mts.map +1 -1
  21. package/dist/data.mjs +19 -7
  22. package/dist/data.mjs.map +1 -1
  23. package/dist/implementations/SLPDataAPI.cjs +26 -8
  24. package/dist/implementations/SLPDataAPI.cjs.map +1 -1
  25. package/dist/implementations/SLPDataAPI.d.cts.map +1 -1
  26. package/dist/implementations/SLPDataAPI.d.mts.map +1 -1
  27. package/dist/implementations/SLPDataAPI.mjs +26 -8
  28. package/dist/implementations/SLPDataAPI.mjs.map +1 -1
  29. package/dist/implementations/USDZDataAPI.cjs +26 -8
  30. package/dist/implementations/USDZDataAPI.cjs.map +1 -1
  31. package/dist/implementations/USDZDataAPI.d.cts.map +1 -1
  32. package/dist/implementations/USDZDataAPI.d.mts.map +1 -1
  33. package/dist/implementations/USDZDataAPI.mjs +26 -8
  34. package/dist/implementations/USDZDataAPI.mjs.map +1 -1
  35. package/dist/implementations/ZBTCVCDataAPI.cjs +19 -7
  36. package/dist/implementations/ZBTCVCDataAPI.cjs.map +1 -1
  37. package/dist/implementations/ZBTCVCDataAPI.d.cts.map +1 -1
  38. package/dist/implementations/ZBTCVCDataAPI.d.mts.map +1 -1
  39. package/dist/implementations/ZBTCVCDataAPI.mjs +19 -7
  40. package/dist/implementations/ZBTCVCDataAPI.mjs.map +1 -1
  41. package/dist/implementations/ZLPDataAPI.cjs +26 -8
  42. package/dist/implementations/ZLPDataAPI.cjs.map +1 -1
  43. package/dist/implementations/ZLPDataAPI.d.cts.map +1 -1
  44. package/dist/implementations/ZLPDataAPI.d.mts.map +1 -1
  45. package/dist/implementations/ZLPDataAPI.mjs +26 -8
  46. package/dist/implementations/ZLPDataAPI.mjs.map +1 -1
  47. package/dist/index.cjs +1 -0
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +1 -0
  50. package/dist/index.d.cts.map +1 -1
  51. package/dist/index.d.mts +1 -0
  52. package/dist/index.d.mts.map +1 -1
  53. package/dist/index.mjs +1 -0
  54. package/dist/index.mjs.map +1 -1
  55. package/dist/interfaces/base.d.cts +14 -1
  56. package/dist/interfaces/base.d.cts.map +1 -1
  57. package/dist/interfaces/base.d.mts +14 -1
  58. package/dist/interfaces/base.d.mts.map +1 -1
  59. package/dist/traderPositions.cjs +130 -0
  60. package/dist/traderPositions.cjs.map +1 -0
  61. package/dist/traderPositions.d.cts +72 -0
  62. package/dist/traderPositions.d.cts.map +1 -0
  63. package/dist/traderPositions.d.mts +72 -0
  64. package/dist/traderPositions.d.mts.map +1 -0
  65. package/dist/traderPositions.mjs +124 -0
  66. package/dist/traderPositions.mjs.map +1 -0
  67. package/package.json +1 -1
  68. package/src/abstract/BaseAPI.ts +6 -2
  69. package/src/abstract/BaseDataAPI.ts +67 -3
  70. package/src/data.ts +19 -7
  71. package/src/implementations/SLPDataAPI.ts +34 -16
  72. package/src/implementations/USDZDataAPI.ts +32 -14
  73. package/src/implementations/ZBTCVCDataAPI.ts +23 -11
  74. package/src/implementations/ZLPDataAPI.ts +37 -14
  75. package/src/index.ts +1 -0
  76. package/src/interfaces/base.ts +15 -1
  77. 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
- return ZLPDataAPI.parseMarketInfo(rawData)
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
- return await this.parseVaultInfo(rawData)
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
- return await this.parseSymbolInfo(rawData, long)
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
- return ZLPDataAPI.parsePositionConfig(rawData)
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
- return ZLPDataAPI.parseOiFundingState(rawData)
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
- return { ...model, exponent }
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
- positionInfo.reservingFeeAmount = ZLPDataAPI.calculatePositionReserveFee(positionInfo, await this.getVaultInfo(positionInfo.collateralToken), (await this.getVaultInfo(positionInfo.collateralToken)).reservingFeeModel, Date.now() / 1000)
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
- await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long),
1256
- (await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long)).fundingFeeModel,
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
@@ -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
+ }