zo-sdk 0.1.43 → 0.1.44

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.
@@ -11,7 +11,7 @@ import { Transaction } from '@mysten/sui/transactions'
11
11
  import { SUI_CLOCK_OBJECT_ID } from '@mysten/sui/utils'
12
12
 
13
13
  import { BaseDataAPI } from '../abstract'
14
- import { Rate, SRate, SymbolsValuation, VaultsValuation } from '../bcs'
14
+ import { Rate, SymbolsValuation, VaultsValuation } from '../bcs'
15
15
  import type { Network } from '../consts'
16
16
  import { LPToken, SLP_TOKEN_DECIMALS } from '../consts'
17
17
  import type {
@@ -21,6 +21,8 @@ import type {
21
21
  ISLPFundingFeeModel,
22
22
  ISLPMarketInfo,
23
23
  ISLPMarketValuationInfo,
24
+ ISLPOiFundingModel,
25
+ ISLPOiFundingState,
24
26
  ISLPOrderCapInfo,
25
27
  ISLPOrderInfo,
26
28
  ISLPPositionCapInfo,
@@ -346,6 +348,28 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
346
348
  }
347
349
  }
348
350
 
351
+ /**
352
+ * Gets SLP symbol OI funding state (global per symbol)
353
+ */
354
+ public async getSymbolOiFundingState(indexToken: string): Promise<ISLPOiFundingState | null> {
355
+ this.validateCache()
356
+ try {
357
+ const rawData = await this.provider.getDynamicFieldObject({
358
+ parentId: this.consts.sudoCore.market,
359
+ name: {
360
+ type: `${this.consts.sudoCore.upgradedPackage}::funding::FundingStateKey<${this.consts.coins[indexToken].module}>`,
361
+ value: { dummy_field: false },
362
+ },
363
+ })
364
+ return SLPDataAPI.parseOiFundingState(rawData)
365
+ }
366
+ catch (e: any) {
367
+ // If the dynamic field doesn't exist, return null
368
+ console.error('Error Fetching SLP Symbol OI Funding State:', e)
369
+ return null
370
+ }
371
+ }
372
+
349
373
  public async getPositionInfoList(
350
374
  positionCapInfoList: ISLPPositionCapInfo[],
351
375
  owner: string,
@@ -562,70 +586,38 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
562
586
  }
563
587
  }
564
588
 
565
- public async fundingFeeRate(indexToken: string, long: boolean, sender?: string): Promise<number> {
566
- if (!sender) {
567
- throw new Error('Sender address is required for fundingFeeRate calculation')
568
- }
569
- const tx = await this.initOracleTxb([indexToken])
570
- const symbol_ = joinSymbol(long ? 'long' : 'short', indexToken)
571
- const currentTimestamp = Number.parseInt((Date.now() / 1000).toFixed(0), 10)
572
- const symbol = tx.moveCall({
573
- target: `${this.consts.sudoCore.upgradedPackage}::market::symbol`,
574
- typeArguments: [
575
- `${this.consts.sudoCore.package}::slp::SLP`,
576
- this.consts.coins[indexToken].module,
577
- `${this.consts.sudoCore.package}::market::${long ? 'LONG' : 'SHORT'}`,
578
- ],
579
- arguments: [tx.object(this.consts.sudoCore.market)],
580
- })
581
- const aggPriceConfig = tx.moveCall({
582
- target: `${this.consts.sudoCore.package}::pool::symbol_price_config`,
583
- typeArguments: [],
584
- arguments: [symbol],
585
- })
586
- const aggPrice = tx.moveCall({
587
- target: `${this.consts.sudoCore.package}::agg_price::parse_pyth_feeder_v1_1`,
588
- typeArguments: [],
589
- arguments: [
590
- aggPriceConfig,
591
- tx.object(this.consts.pythFeeder.feeder[indexToken]),
592
- tx.pure.u64(currentTimestamp),
593
- ],
594
- })
595
- const deltaSize = tx.moveCall({
596
- target: `${this.consts.sudoCore.package}::pool::symbol_delta_size`,
597
- typeArguments: [],
598
- arguments: [symbol, aggPrice, tx.pure.bool(long)],
599
- })
600
- const LpSupplyAmount = tx.moveCall({
601
- target: `${this.consts.sudoCore.upgradedPackage}::market::lp_supply_amount`,
602
- typeArguments: [`${this.consts.sudoCore.package}::slp::SLP`],
603
- arguments: [tx.object(this.consts.sudoCore.market)],
604
- })
605
- const PnlPerLp = tx.moveCall({
606
- target: `${this.consts.sudoCore.package}::pool::symbol_pnl_per_lp`,
607
- typeArguments: [],
608
- arguments: [symbol, deltaSize, LpSupplyAmount],
609
- })
610
- tx.moveCall({
611
- target: `${this.consts.sudoCore.package}::model::compute_funding_fee_rate`,
612
- arguments: [
613
- tx.object(this.consts.sudoCore.symbols[symbol_].fundingFeeModel),
614
- PnlPerLp,
615
- tx.pure.u64(8 * 3600),
616
- ],
617
- })
589
+ public async fundingFeeRate(indexToken: string, long: boolean): Promise<number> {
590
+ const oiState = await this.getSymbolOiFundingState(indexToken)
591
+ if (oiState && oiState.enabled) {
592
+ const longSymbol = await this.getSymbolInfo(indexToken, true)
593
+ const shortSymbol = await this.getSymbolInfo(indexToken, false)
618
594
 
619
- const res: any = await this.provider.devInspectTransactionBlock({
620
- transactionBlock: tx,
621
- sender,
622
- })
595
+ if (longSymbol.lastUpdate <= 0 && shortSymbol.lastUpdate <= 0) {
596
+ return 0
597
+ }
623
598
 
624
- const de = SRate.parse(
625
- new Uint8Array(res.results.at(-1).returnValues[0][0]),
626
- )
599
+ const elapsed = SECONDS_PER_EIGHT_HOUR
600
+ const deltaRate = SLPDataAPI.calcOiFundingFeeRate(
601
+ oiState.model,
602
+ longSymbol.openingSize,
603
+ shortSymbol.openingSize,
604
+ elapsed,
605
+ )
606
+ return long ? deltaRate : -deltaRate
607
+ }
627
608
 
628
- return (Number(BigInt(de.value)) / 1e18) * (de.is_positive ? 1 : -1)
609
+ const symbol = await this.getSymbolInfo(indexToken, long)
610
+ if (symbol.lastUpdate <= 0) {
611
+ return 0
612
+ }
613
+ const price = (await this.getOraclePrice(indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked()
614
+ const lpSupplyAmount = (await this.getMarketInfo()).lpSupplyWithDecimals
615
+ const model = symbol.fundingFeeModel
616
+ const elapsed = SECONDS_PER_EIGHT_HOUR
617
+
618
+ const deltaSize = SLPDataAPI.calcDeltaSize(symbol, price, symbol.long)
619
+ const pnlPerLp = (symbol.realisedPnl + symbol.unrealisedFundingFeeValue + deltaSize) / lpSupplyAmount
620
+ return SLPDataAPI.calcFundingFeeRate(model, pnlPerLp, elapsed)
629
621
  }
630
622
 
631
623
  public async rebaseFeeRate(collateralToken: string, increase: boolean, amount: number, sender?: string): Promise<number> {
@@ -1000,15 +992,52 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
1000
992
  }
1001
993
 
1002
994
  // Private helper methods
1003
- private static calculatePositionFundingFee(position: ISLPPositionInfo, symbol: ISLPSymbolInfo, model: ISLPFundingFeeModel, price: number, lpSupplyAmount: number, timestamp: number): number {
1004
- const accFundingRate = this.calcAccFundingFeeRate(symbol, model, price, lpSupplyAmount, timestamp, position.long)
995
+ private static calculatePositionFundingFee(
996
+ position: ISLPPositionInfo,
997
+ symbol: ISLPSymbolInfo,
998
+ model: ISLPFundingFeeModel,
999
+ price: number,
1000
+ lpSupplyAmount: number,
1001
+ timestamp: number,
1002
+ oiModel?: ISLPOiFundingModel,
1003
+ pairedOpeningSize?: number,
1004
+ ): number {
1005
+ const accFundingRate = this.calcAccFundingFeeRate(
1006
+ symbol,
1007
+ model,
1008
+ price,
1009
+ lpSupplyAmount,
1010
+ timestamp,
1011
+ position.long,
1012
+ oiModel,
1013
+ pairedOpeningSize,
1014
+ )
1005
1015
  return position.fundingFeeValue + (accFundingRate - position.lastFundingRate) * position.positionSize
1006
1016
  }
1007
1017
 
1008
- private static calcAccFundingFeeRate(symbol: ISLPSymbolInfo, model: ISLPFundingFeeModel, price: number, lpSupplyAmount: number, timestamp: number, isLong: boolean): number {
1018
+ private static calcAccFundingFeeRate(
1019
+ symbol: ISLPSymbolInfo,
1020
+ model: ISLPFundingFeeModel,
1021
+ price: number,
1022
+ lpSupplyAmount: number,
1023
+ timestamp: number,
1024
+ isLong: boolean,
1025
+ oiModel?: ISLPOiFundingModel,
1026
+ pairedOpeningSize?: number,
1027
+ ): number {
1009
1028
  if (symbol.lastUpdate > 0) {
1010
1029
  const elapsed = timestamp - symbol.lastUpdate
1011
1030
  if (elapsed > 0) {
1031
+ // Prefer OI-based delta when OI model and paired side are available
1032
+ if (oiModel && typeof pairedOpeningSize === 'number') {
1033
+ const longSize = isLong ? symbol.openingSize : pairedOpeningSize
1034
+ const shortSize = isLong ? pairedOpeningSize : symbol.openingSize
1035
+ const deltaRate = SLPDataAPI.calcOiFundingFeeRate(oiModel, longSize, shortSize, elapsed)
1036
+ const appliedRate = isLong ? deltaRate : -deltaRate
1037
+ return symbol.accFundingRate + appliedRate
1038
+ }
1039
+
1040
+ // Fallback to PnL-based funding delta
1012
1041
  const deltaSize = this.calcDeltaSize(symbol, price, isLong)
1013
1042
  const pnlPerLp = (symbol.realisedPnl + symbol.unrealisedFundingFeeValue + deltaSize) / lpSupplyAmount
1014
1043
  return symbol.accFundingRate + SLPDataAPI.calcFundingFeeRate(model, pnlPerLp, elapsed)
@@ -1028,6 +1057,18 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
1028
1057
  return pnlPerRate >= 0 ? -secondsRate : secondsRate
1029
1058
  }
1030
1059
 
1060
+ private static calcOiFundingFeeRate(model: ISLPOiFundingModel, longSize: number, shortSize: number, elapsed: number): number {
1061
+ const imbalance = Math.abs(longSize - shortSize)
1062
+ const denom = longSize + shortSize > 0 ? longSize + shortSize : 1
1063
+
1064
+ const dailyRate = Math.min(
1065
+ (model.multiplier * (imbalance ** model.exponent)) / denom,
1066
+ model.max,
1067
+ )
1068
+ const secondsRate = dailyRate * elapsed / SECONDS_PER_EIGHT_HOUR
1069
+ return longSize >= shortSize ? secondsRate : -secondsRate
1070
+ }
1071
+
1031
1072
  private static calculatePositionReserveFee(position: ISLPPositionInfo, vault: ISLPVaultInfo, model: ISLPReservingFeeModel, timestamp: number): number {
1032
1073
  const accReservingRate = SLPDataAPI.calcAccReservingFeeRate(vault, model, timestamp)
1033
1074
  return position.reservingFeeAmount + (accReservingRate - position.lastReservingRate) * position.collateralAmount
@@ -1112,6 +1153,21 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
1112
1153
  }
1113
1154
  }
1114
1155
 
1156
+ private static parseOiFundingState(raw: any): ISLPOiFundingState {
1157
+ const content = raw.data.content.fields
1158
+ return {
1159
+ id: content.id.id,
1160
+ enabled: content.enabled,
1161
+ last_update: parseValue(content.last_update),
1162
+ model: {
1163
+ id: content.model.fields.id.id,
1164
+ multiplier: parseValue(content.model.fields.multiplier),
1165
+ exponent: parseValue(content.model.fields.exponent),
1166
+ max: parseValue(content.model.fields.max),
1167
+ },
1168
+ }
1169
+ }
1170
+
1115
1171
  private static parseMarketInfo(raw: any): ISLPMarketInfo {
1116
1172
  const content = raw.data.content.fields
1117
1173
 
@@ -1236,6 +1292,11 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
1236
1292
  (await this.getVaultInfo(positionInfo.collateralToken)).reservingFeeModel,
1237
1293
  Date.now() / 1000,
1238
1294
  )
1295
+
1296
+ // OI context for funding: fetch state and paired side size when enabled
1297
+ const oiState = await this.getSymbolOiFundingState(positionInfo.indexToken)
1298
+ const pairedSymbol = await this.getSymbolInfo(positionInfo.indexToken, !positionInfo.long)
1299
+
1239
1300
  positionInfo.fundingFeeValue = SLPDataAPI.calculatePositionFundingFee(
1240
1301
  positionInfo,
1241
1302
  await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long),
@@ -1243,6 +1304,8 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
1243
1304
  (await this.getOraclePrice(positionInfo.indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked(),
1244
1305
  (await this.getMarketInfo()).lpSupplyWithDecimals,
1245
1306
  Date.now() / 1000,
1307
+ oiState && oiState.enabled ? oiState.model : undefined,
1308
+ pairedSymbol.openingSize,
1246
1309
  )
1247
1310
  }
1248
1311
  catch (e) {
@@ -87,6 +87,20 @@ export interface ISLPSymbolConfig {
87
87
  instant_exit_fee_config: ISLPPositionInstantExitFeeConfig
88
88
  }
89
89
 
90
+ export interface ISLPOiFundingModel {
91
+ id: string
92
+ multiplier: number
93
+ exponent: number
94
+ max: number
95
+ }
96
+
97
+ export interface ISLPOiFundingState {
98
+ id: string
99
+ enabled: boolean
100
+ last_update: number
101
+ model: ISLPOiFundingModel
102
+ }
103
+
90
104
  // Sudo SDK specific data structures
91
105
  export interface ISudoMarket {
92
106
  lpSupply: bigint