@zofai/zo-sdk 0.1.92 → 0.1.94
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/consts/deployments-slp-mainnet.json +1 -1
- package/dist/consts/deployments-usdz-mainnet.json +1 -1
- package/dist/consts/deployments-zlp-mainnet.json +1 -1
- package/dist/implementations/SLPDataAPI.cjs +227 -51
- package/dist/implementations/SLPDataAPI.cjs.map +1 -1
- package/dist/implementations/SLPDataAPI.d.cts +8 -1
- package/dist/implementations/SLPDataAPI.d.cts.map +1 -1
- package/dist/implementations/SLPDataAPI.d.mts +8 -1
- package/dist/implementations/SLPDataAPI.d.mts.map +1 -1
- package/dist/implementations/SLPDataAPI.mjs +227 -51
- package/dist/implementations/SLPDataAPI.mjs.map +1 -1
- package/dist/implementations/USDZDataAPI.cjs +208 -48
- package/dist/implementations/USDZDataAPI.cjs.map +1 -1
- package/dist/implementations/USDZDataAPI.d.cts +8 -1
- package/dist/implementations/USDZDataAPI.d.cts.map +1 -1
- package/dist/implementations/USDZDataAPI.d.mts +8 -1
- package/dist/implementations/USDZDataAPI.d.mts.map +1 -1
- package/dist/implementations/USDZDataAPI.mjs +208 -48
- package/dist/implementations/USDZDataAPI.mjs.map +1 -1
- package/dist/implementations/ZLPDataAPI.cjs +211 -50
- package/dist/implementations/ZLPDataAPI.cjs.map +1 -1
- package/dist/implementations/ZLPDataAPI.d.cts +8 -1
- package/dist/implementations/ZLPDataAPI.d.cts.map +1 -1
- package/dist/implementations/ZLPDataAPI.d.mts +8 -1
- package/dist/implementations/ZLPDataAPI.d.mts.map +1 -1
- package/dist/implementations/ZLPDataAPI.mjs +211 -50
- package/dist/implementations/ZLPDataAPI.mjs.map +1 -1
- package/dist/interfaces/base.d.cts +22 -0
- package/dist/interfaces/base.d.cts.map +1 -1
- package/dist/interfaces/base.d.mts +22 -0
- package/dist/interfaces/base.d.mts.map +1 -1
- package/dist/interfaces/slp.d.cts +8 -1
- package/dist/interfaces/slp.d.cts.map +1 -1
- package/dist/interfaces/slp.d.mts +8 -1
- package/dist/interfaces/slp.d.mts.map +1 -1
- package/dist/interfaces/usdz.d.cts +8 -1
- package/dist/interfaces/usdz.d.cts.map +1 -1
- package/dist/interfaces/usdz.d.mts +8 -1
- package/dist/interfaces/usdz.d.mts.map +1 -1
- package/dist/interfaces/zlp.d.cts +8 -1
- package/dist/interfaces/zlp.d.cts.map +1 -1
- package/dist/interfaces/zlp.d.mts +8 -1
- package/dist/interfaces/zlp.d.mts.map +1 -1
- package/package.json +4 -1
- package/src/consts/deployments-slp-mainnet.json +1 -1
- package/src/consts/deployments-usdz-mainnet.json +1 -1
- package/src/consts/deployments-zlp-mainnet.json +1 -1
- package/src/implementations/SLPDataAPI.ts +253 -23
- package/src/implementations/USDZDataAPI.ts +232 -20
- package/src/implementations/ZLPDataAPI.ts +233 -21
- package/src/interfaces/base.ts +26 -0
- package/src/interfaces/slp.ts +10 -1
- package/src/interfaces/usdz.ts +10 -1
- package/src/interfaces/zlp.ts +10 -1
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
ISLPSymbolConfig,
|
|
38
38
|
ISLPSymbolInfo,
|
|
39
39
|
ISLPVaultInfo,
|
|
40
|
+
ISwapFeeBreakdown,
|
|
40
41
|
} from '../interfaces'
|
|
41
42
|
import { joinSymbol, parseSymbolKey, parseValue, suiSymbolToSymbol } from '../utils'
|
|
42
43
|
|
|
@@ -49,6 +50,20 @@ let aprResponse: GetCumulativeAprResponse = {}
|
|
|
49
50
|
|
|
50
51
|
const SECONDS_PER_EIGHT_HOUR = 8 * 60 * 60 // 28800 seconds
|
|
51
52
|
|
|
53
|
+
interface SwapImpactConfig {
|
|
54
|
+
id: string
|
|
55
|
+
enabled: boolean
|
|
56
|
+
impactMultiplier: number
|
|
57
|
+
maxImpactRate: number
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface EmaVolatilityFeeConfig {
|
|
61
|
+
id: string
|
|
62
|
+
enabled: boolean
|
|
63
|
+
multiplier: number
|
|
64
|
+
maxFeeRate: number
|
|
65
|
+
}
|
|
66
|
+
|
|
52
67
|
export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
53
68
|
constructor(
|
|
54
69
|
network: Network,
|
|
@@ -76,7 +91,7 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
76
91
|
price: number,
|
|
77
92
|
lpSupplyAmount: number,
|
|
78
93
|
timestamp: number,
|
|
79
|
-
|
|
94
|
+
oiState?: ISLPOiFundingState,
|
|
80
95
|
pairedOpeningSize?: number,
|
|
81
96
|
): number {
|
|
82
97
|
const accFundingRate = SLPDataAPI.calcAccFundingFeeRate(
|
|
@@ -86,7 +101,7 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
86
101
|
lpSupplyAmount,
|
|
87
102
|
timestamp,
|
|
88
103
|
symbol.long,
|
|
89
|
-
|
|
104
|
+
oiState,
|
|
90
105
|
pairedOpeningSize,
|
|
91
106
|
)
|
|
92
107
|
return symbol.unrealisedFundingFeeValue + (accFundingRate - symbol.accFundingRate) * symbol.openingSize
|
|
@@ -203,15 +218,18 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
203
218
|
let slpPrice = 0
|
|
204
219
|
let value = 0
|
|
205
220
|
|
|
206
|
-
const
|
|
221
|
+
const vaultKeys = Object.keys(this.consts.sudoCore.vaults)
|
|
222
|
+
const vaultData = await Promise.all(vaultKeys.map(async (vault) => {
|
|
207
223
|
const vaultInfo = await this.getVaultInfo(vault)
|
|
208
224
|
const reservingFeeDelta = SLPDataAPI.calculateVaultReservingFee(vaultInfo, vaultInfo.reservingFeeModel, Date.now() / 1000)
|
|
209
225
|
const totalVaultAmount = reservingFeeDelta + vaultInfo.liquidity + vaultInfo.reservedAmount
|
|
210
226
|
const oraclePrice = (await this.getOraclePrice(vault)).getPriceUnchecked().getPriceAsNumberUnchecked()
|
|
211
227
|
const { decimals } = this.consts.coins[vault]
|
|
212
228
|
const vaultValue = totalVaultAmount * oraclePrice / (10 ** decimals)
|
|
213
|
-
return vaultValue
|
|
214
|
-
})
|
|
229
|
+
return { vault, oraclePrice, vaultValue }
|
|
230
|
+
}))
|
|
231
|
+
const vaultPrices = Object.fromEntries(vaultData.map(d => [d.vault, d.oraclePrice]))
|
|
232
|
+
const vaultValues = vaultData.map(d => d.vaultValue)
|
|
215
233
|
|
|
216
234
|
const symbolPromises = Object.keys(this.consts.sudoCore.symbols).map(async (symbol) => {
|
|
217
235
|
const [direction, tokenId] = parseSymbolKey(symbol)
|
|
@@ -227,14 +245,14 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
227
245
|
price,
|
|
228
246
|
marketInfo.lpSupplyWithDecimals,
|
|
229
247
|
Date.now() / 1000,
|
|
230
|
-
oiState && oiState.enabled ? oiState
|
|
248
|
+
oiState && oiState.enabled ? oiState : undefined,
|
|
231
249
|
pairedInfo.openingSize,
|
|
232
250
|
)
|
|
233
251
|
const symbolValue = fundingFeeDelta + deltaSize
|
|
234
252
|
return symbolValue
|
|
235
253
|
})
|
|
236
254
|
|
|
237
|
-
const
|
|
255
|
+
const symbolValues = await Promise.all(symbolPromises)
|
|
238
256
|
|
|
239
257
|
const totalVaultValue = vaultValues.reduce((acc: number, curr: number) => acc + curr, 0)
|
|
240
258
|
const totalSymbolValue = symbolValues.reduce((acc: number, curr: number) => acc + curr, 0)
|
|
@@ -247,6 +265,7 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
247
265
|
price: slpPrice,
|
|
248
266
|
supply: marketInfo.lpSupplyWithDecimals,
|
|
249
267
|
apr: Number(marketInfo.apr),
|
|
268
|
+
vaultPrices,
|
|
250
269
|
}
|
|
251
270
|
}
|
|
252
271
|
|
|
@@ -332,11 +351,21 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
332
351
|
const value = await this.simValuateVaults(this.consts.sudoCore.adminCap)
|
|
333
352
|
const slpPrice = value / marketInfo.lpSupplyWithDecimals
|
|
334
353
|
|
|
354
|
+
const vaultKeys = Object.keys(this.consts.sudoCore.vaults)
|
|
355
|
+
const vaultPricesEntries = await Promise.all(
|
|
356
|
+
vaultKeys.map(async (vault) => {
|
|
357
|
+
const oraclePrice = (await this.getOraclePrice(vault)).getPriceUnchecked().getPriceAsNumberUnchecked()
|
|
358
|
+
return [vault, oraclePrice] as const
|
|
359
|
+
}),
|
|
360
|
+
)
|
|
361
|
+
const vaultPrices = Object.fromEntries(vaultPricesEntries)
|
|
362
|
+
|
|
335
363
|
return {
|
|
336
364
|
marketCap: value,
|
|
337
365
|
price: slpPrice,
|
|
338
366
|
supply: marketInfo.lpSupplyWithDecimals,
|
|
339
367
|
apr: Number(marketInfo.apr),
|
|
368
|
+
vaultPrices,
|
|
340
369
|
}
|
|
341
370
|
}
|
|
342
371
|
|
|
@@ -471,6 +500,177 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
471
500
|
}
|
|
472
501
|
}
|
|
473
502
|
|
|
503
|
+
public async calculateSwapFeeBreakdown(fromToken: string, toToken: string, fromAmount: number): Promise<ISwapFeeBreakdown> {
|
|
504
|
+
if (!this.consts.sudoCore) {
|
|
505
|
+
throw new Error('Sudo Core configuration not found. Make sure you are using LPToken.SLP')
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const timestamp = Date.now() / 1000
|
|
509
|
+
|
|
510
|
+
const fromDecimals = this.consts.coins[fromToken]?.decimals
|
|
511
|
+
const toDecimals = this.consts.coins[toToken]?.decimals
|
|
512
|
+
if (fromDecimals === undefined || toDecimals === undefined) {
|
|
513
|
+
throw new Error(`Unknown token decimals for swap: ${fromToken} -> ${toToken}`)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const fromFeed = await this.getOraclePrice(fromToken)
|
|
517
|
+
const toFeed = await this.getOraclePrice(toToken)
|
|
518
|
+
const fromPrice = fromFeed.getPriceUnchecked().getPriceAsNumberUnchecked()
|
|
519
|
+
const toPrice = toFeed.getPriceUnchecked().getPriceAsNumberUnchecked()
|
|
520
|
+
|
|
521
|
+
const swapValue = (fromAmount * fromPrice) / (10 ** fromDecimals)
|
|
522
|
+
const totalVaultsValue = await this.#getTotalVaultsValueUsd(timestamp)
|
|
523
|
+
|
|
524
|
+
const rebaseFeeInRate = await this.rebaseFeeRate(fromToken, true, fromAmount)
|
|
525
|
+
const rebaseFeeInValue = swapValue * rebaseFeeInRate
|
|
526
|
+
|
|
527
|
+
const estimatedToAmount = toPrice !== 0
|
|
528
|
+
? (swapValue * (10 ** toDecimals)) / toPrice
|
|
529
|
+
: 0
|
|
530
|
+
const rebaseFeeOutRate = await this.rebaseFeeRate(toToken, false, estimatedToAmount)
|
|
531
|
+
const rebaseFeeOutValue = swapValue * rebaseFeeOutRate
|
|
532
|
+
|
|
533
|
+
const swapImpactCfg = await this.#getSwapImpactConfig()
|
|
534
|
+
const swapImpactFeeValue = swapImpactCfg?.enabled
|
|
535
|
+
? SLPDataAPI.#computeSwapImpactFeeValue(swapValue, totalVaultsValue, swapImpactCfg.impactMultiplier, swapImpactCfg.maxImpactRate)
|
|
536
|
+
: 0
|
|
537
|
+
|
|
538
|
+
const emaCfg = await this.#getEmaVolatilityFeeConfig()
|
|
539
|
+
const emaVolatilityFeeValue = emaCfg?.enabled
|
|
540
|
+
? SLPDataAPI.#computeEmaVolatilityFeeValue(
|
|
541
|
+
swapValue,
|
|
542
|
+
SLPDataAPI.#maxEmaDivergenceRate(fromFeed, toFeed),
|
|
543
|
+
emaCfg.multiplier,
|
|
544
|
+
emaCfg.maxFeeRate,
|
|
545
|
+
)
|
|
546
|
+
: 0
|
|
547
|
+
|
|
548
|
+
const totalFeeValue = rebaseFeeInValue + rebaseFeeOutValue + swapImpactFeeValue + emaVolatilityFeeValue
|
|
549
|
+
const totalFeeRate = swapValue !== 0 ? totalFeeValue / swapValue : 0
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
swapValue,
|
|
553
|
+
totalVaultsValue,
|
|
554
|
+
rebaseFeeInRate,
|
|
555
|
+
rebaseFeeOutRate,
|
|
556
|
+
rebaseFeeInValue,
|
|
557
|
+
rebaseFeeOutValue,
|
|
558
|
+
swapImpactFeeValue,
|
|
559
|
+
emaVolatilityFeeValue,
|
|
560
|
+
totalFeeValue,
|
|
561
|
+
totalFeeRate,
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async #getTotalVaultsValueUsd(timestamp: number): Promise<number> {
|
|
566
|
+
if (!this.consts.sudoCore) {
|
|
567
|
+
return 0
|
|
568
|
+
}
|
|
569
|
+
const vaultKeys = Object.keys(this.consts.sudoCore.vaults)
|
|
570
|
+
const vaultValues = await Promise.all(vaultKeys.map(async (vault) => {
|
|
571
|
+
const vaultInfo = await this.getVaultInfo(vault)
|
|
572
|
+
const reservingFeeDelta = SLPDataAPI.calculateVaultReservingFee(vaultInfo, vaultInfo.reservingFeeModel, timestamp)
|
|
573
|
+
const totalVaultAmount = reservingFeeDelta + vaultInfo.liquidity + vaultInfo.reservedAmount
|
|
574
|
+
const oraclePrice = (await this.getOraclePrice(vault)).getPriceUnchecked().getPriceAsNumberUnchecked()
|
|
575
|
+
const { decimals } = this.consts.coins[vault]
|
|
576
|
+
return totalVaultAmount * oraclePrice / (10 ** decimals)
|
|
577
|
+
}))
|
|
578
|
+
return vaultValues.reduce((acc, curr) => acc + curr, 0)
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
async #getSwapImpactConfig(): Promise<SwapImpactConfig | null> {
|
|
582
|
+
if (!this.consts.sudoCore) {
|
|
583
|
+
return null
|
|
584
|
+
}
|
|
585
|
+
const raw = await this.#getMarketDynamicFieldObjectByKeySuffix(this.consts.sudoCore.market, 'SwapImpactConfigKey')
|
|
586
|
+
if (!raw)
|
|
587
|
+
return null
|
|
588
|
+
return SLPDataAPI.#parseSwapImpactConfig(raw)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async #getEmaVolatilityFeeConfig(): Promise<EmaVolatilityFeeConfig | null> {
|
|
592
|
+
if (!this.consts.sudoCore) {
|
|
593
|
+
return null
|
|
594
|
+
}
|
|
595
|
+
const raw = await this.#getMarketDynamicFieldObjectByKeySuffix(this.consts.sudoCore.market, 'EmaVolatilityFeeConfigKey')
|
|
596
|
+
if (!raw)
|
|
597
|
+
return null
|
|
598
|
+
return SLPDataAPI.#parseEmaVolatilityFeeConfig(raw)
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async #getMarketDynamicFieldObjectByKeySuffix(parentId: string, keyTypeSuffix: string): Promise<any | null> {
|
|
602
|
+
let cursor: string | null | undefined
|
|
603
|
+
let hasNextPage = true
|
|
604
|
+
|
|
605
|
+
while (hasNextPage) {
|
|
606
|
+
const page = await this.provider.getDynamicFields({ parentId, cursor })
|
|
607
|
+
for (const field of page.data) {
|
|
608
|
+
const type = (field.name as any)?.type
|
|
609
|
+
if (typeof type === 'string' && type.endsWith(`::${keyTypeSuffix}`)) {
|
|
610
|
+
return await this.provider.getDynamicFieldObject({
|
|
611
|
+
parentId,
|
|
612
|
+
name: field.name as any,
|
|
613
|
+
})
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
hasNextPage = page.hasNextPage
|
|
617
|
+
cursor = page.nextCursor
|
|
618
|
+
}
|
|
619
|
+
return null
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
static #parseSwapImpactConfig(raw: any): SwapImpactConfig {
|
|
623
|
+
const { fields } = raw.data.content
|
|
624
|
+
return {
|
|
625
|
+
id: fields.id.id,
|
|
626
|
+
enabled: fields.enabled,
|
|
627
|
+
impactMultiplier: parseValue(fields.impact_multiplier),
|
|
628
|
+
maxImpactRate: parseValue(fields.max_impact_rate),
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
static #parseEmaVolatilityFeeConfig(raw: any): EmaVolatilityFeeConfig {
|
|
633
|
+
const { fields } = raw.data.content
|
|
634
|
+
return {
|
|
635
|
+
id: fields.id.id,
|
|
636
|
+
enabled: fields.enabled,
|
|
637
|
+
multiplier: parseValue(fields.multiplier),
|
|
638
|
+
maxFeeRate: parseValue(fields.max_fee_rate),
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
static #computeSwapImpactFeeValue(swapValue: number, totalVaultsValue: number, impactMultiplier: number, maxImpactRate: number): number {
|
|
643
|
+
if (swapValue <= 0 || totalVaultsValue <= 0)
|
|
644
|
+
return 0
|
|
645
|
+
const utilization = swapValue / totalVaultsValue
|
|
646
|
+
const rawImpactRate = impactMultiplier * utilization
|
|
647
|
+
const impactRate = Math.min(rawImpactRate, maxImpactRate)
|
|
648
|
+
return swapValue * impactRate
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
static #computeEmaVolatilityFeeValue(swapValue: number, maxDiv: number, multiplier: number, maxFeeRate: number): number {
|
|
652
|
+
if (swapValue <= 0 || maxDiv <= 0)
|
|
653
|
+
return 0
|
|
654
|
+
const rawFeeRate = multiplier * maxDiv
|
|
655
|
+
const feeRate = Math.min(rawFeeRate, maxFeeRate)
|
|
656
|
+
return swapValue * feeRate
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
static #maxEmaDivergenceRate(sourceFeed: any, destFeed: any): number {
|
|
660
|
+
const sourceDiv = SLPDataAPI.#emaDivergenceRate(sourceFeed)
|
|
661
|
+
const destDiv = SLPDataAPI.#emaDivergenceRate(destFeed)
|
|
662
|
+
return Math.max(sourceDiv, destDiv)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
static #emaDivergenceRate(priceFeed: any): number {
|
|
666
|
+
const price = priceFeed.getPriceUnchecked().getPriceAsNumberUnchecked()
|
|
667
|
+
const ema = priceFeed.getEmaPriceUnchecked().getPriceAsNumberUnchecked()
|
|
668
|
+
const denom = Math.abs(price)
|
|
669
|
+
if (denom === 0)
|
|
670
|
+
return 0
|
|
671
|
+
return Math.abs(price - ema) / denom
|
|
672
|
+
}
|
|
673
|
+
|
|
474
674
|
public async getPositionInfoList(
|
|
475
675
|
positionCapInfoList: ISLPPositionCapInfo[],
|
|
476
676
|
owner: string,
|
|
@@ -703,6 +903,8 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
703
903
|
longSymbol.openingSize,
|
|
704
904
|
shortSymbol.openingSize,
|
|
705
905
|
elapsed,
|
|
906
|
+
oiState.maxOiLong,
|
|
907
|
+
oiState.maxOiShort,
|
|
706
908
|
)
|
|
707
909
|
return long ? deltaRate : -deltaRate
|
|
708
910
|
}
|
|
@@ -1082,7 +1284,7 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
1082
1284
|
price: number,
|
|
1083
1285
|
lpSupplyAmount: number,
|
|
1084
1286
|
timestamp: number,
|
|
1085
|
-
|
|
1287
|
+
oiState?: ISLPOiFundingState,
|
|
1086
1288
|
pairedOpeningSize?: number,
|
|
1087
1289
|
): number {
|
|
1088
1290
|
const accFundingRate = this.calcAccFundingFeeRate(
|
|
@@ -1092,7 +1294,7 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
1092
1294
|
lpSupplyAmount,
|
|
1093
1295
|
timestamp,
|
|
1094
1296
|
position.long,
|
|
1095
|
-
|
|
1297
|
+
oiState,
|
|
1096
1298
|
pairedOpeningSize,
|
|
1097
1299
|
)
|
|
1098
1300
|
return position.fundingFeeValue + (accFundingRate - position.lastFundingRate) * position.positionSize
|
|
@@ -1105,17 +1307,17 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
1105
1307
|
lpSupplyAmount: number,
|
|
1106
1308
|
timestamp: number,
|
|
1107
1309
|
isLong: boolean,
|
|
1108
|
-
|
|
1310
|
+
oiState?: ISLPOiFundingState,
|
|
1109
1311
|
pairedOpeningSize?: number,
|
|
1110
1312
|
): number {
|
|
1111
1313
|
if (symbol.lastUpdate > 0) {
|
|
1112
1314
|
const elapsed = timestamp - symbol.lastUpdate
|
|
1113
1315
|
if (elapsed > 0) {
|
|
1114
|
-
// Prefer OI-based delta when OI
|
|
1115
|
-
if (
|
|
1316
|
+
// Prefer OI-based delta when OI state and paired side are available
|
|
1317
|
+
if (oiState?.enabled && oiState.model && typeof pairedOpeningSize === 'number') {
|
|
1116
1318
|
const longSize = isLong ? symbol.openingSize : pairedOpeningSize
|
|
1117
1319
|
const shortSize = isLong ? pairedOpeningSize : symbol.openingSize
|
|
1118
|
-
const deltaRate = SLPDataAPI.calcOiFundingFeeRate(
|
|
1320
|
+
const deltaRate = SLPDataAPI.calcOiFundingFeeRate(oiState.model, longSize, shortSize, elapsed, oiState.maxOiLong, oiState.maxOiShort)
|
|
1119
1321
|
const appliedRate = isLong ? deltaRate : -deltaRate
|
|
1120
1322
|
return symbol.accFundingRate + appliedRate
|
|
1121
1323
|
}
|
|
@@ -1140,16 +1342,42 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
1140
1342
|
return pnlPerRate >= 0 ? -secondsRate : secondsRate
|
|
1141
1343
|
}
|
|
1142
1344
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1345
|
+
/**
|
|
1346
|
+
* OI funding rate matching Move compute_oi_funding_rate_capped.
|
|
1347
|
+
* When both maxOiLong and maxOiShort are set and > 0, uses normalized skew (oi/cap);
|
|
1348
|
+
* otherwise falls back to (long - short) / total.
|
|
1349
|
+
*/
|
|
1350
|
+
private static calcOiFundingFeeRate(
|
|
1351
|
+
model: ISLPOiFundingModel,
|
|
1352
|
+
oiLong: number,
|
|
1353
|
+
oiShort: number,
|
|
1354
|
+
elapsed: number,
|
|
1355
|
+
maxOiLong?: number,
|
|
1356
|
+
maxOiShort?: number,
|
|
1357
|
+
): number {
|
|
1358
|
+
let skew: number
|
|
1359
|
+
if (maxOiLong && maxOiShort && maxOiLong > 0 && maxOiShort > 0) {
|
|
1360
|
+
const normLong = Math.min(oiLong / maxOiLong, 1)
|
|
1361
|
+
const normShort = Math.min(oiShort / maxOiShort, 1)
|
|
1362
|
+
skew = normLong - normShort
|
|
1363
|
+
}
|
|
1364
|
+
else {
|
|
1365
|
+
const total = oiLong + oiShort
|
|
1366
|
+
if (total === 0)
|
|
1367
|
+
return 0
|
|
1368
|
+
skew = (oiLong - oiShort) / total
|
|
1369
|
+
}
|
|
1146
1370
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1371
|
+
if (skew === 0)
|
|
1372
|
+
return 0
|
|
1373
|
+
|
|
1374
|
+
const skewIsPositive = skew > 0
|
|
1375
|
+
const skewAbs = Math.abs(skew)
|
|
1376
|
+
const exponentInt = Math.floor(model.exponent)
|
|
1377
|
+
const skewPow = skewAbs ** exponentInt
|
|
1378
|
+
const dailyRate = Math.min(model.multiplier * skewPow, model.max)
|
|
1379
|
+
const secondsRate = (dailyRate * elapsed) / SECONDS_PER_EIGHT_HOUR
|
|
1380
|
+
return skewIsPositive ? secondsRate : -secondsRate
|
|
1153
1381
|
}
|
|
1154
1382
|
|
|
1155
1383
|
private static calculatePositionReserveFee(position: ISLPPositionInfo, vault: ISLPVaultInfo, model: ISLPReservingFeeModel, timestamp: number): number {
|
|
@@ -1482,6 +1710,8 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
1482
1710
|
exponent: parseValue(content.model.fields.exponent),
|
|
1483
1711
|
max: parseValue(content.model.fields.max),
|
|
1484
1712
|
},
|
|
1713
|
+
maxOiLong: content.max_oi_long !== null && content.max_oi_long !== undefined ? parseValue(content.max_oi_long) : undefined,
|
|
1714
|
+
maxOiShort: content.max_oi_short !== null && content.max_oi_short !== undefined ? parseValue(content.max_oi_short) : undefined,
|
|
1485
1715
|
}
|
|
1486
1716
|
}
|
|
1487
1717
|
|
|
@@ -1636,7 +1866,7 @@ export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
|
1636
1866
|
(await this.getOraclePrice(positionInfo.indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked(),
|
|
1637
1867
|
(await this.getMarketInfo()).lpSupplyWithDecimals,
|
|
1638
1868
|
Date.now() / 1000,
|
|
1639
|
-
oiState && oiState.enabled ? oiState
|
|
1869
|
+
oiState && oiState.enabled ? oiState : undefined,
|
|
1640
1870
|
pairedSymbol.openingSize,
|
|
1641
1871
|
)
|
|
1642
1872
|
}
|