zo-sdk 0.0.50 → 0.1.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.
Files changed (206) hide show
  1. package/README.md +272 -20
  2. package/dist/abstract/BaseAPI.cjs +117 -0
  3. package/dist/abstract/BaseAPI.cjs.map +1 -0
  4. package/dist/abstract/BaseAPI.d.cts +131 -0
  5. package/dist/abstract/BaseAPI.d.cts.map +1 -0
  6. package/dist/abstract/BaseAPI.d.mts +131 -0
  7. package/dist/abstract/BaseAPI.d.mts.map +1 -0
  8. package/dist/abstract/BaseAPI.mjs +113 -0
  9. package/dist/abstract/BaseAPI.mjs.map +1 -0
  10. package/dist/abstract/BaseDataAPI.cjs +139 -0
  11. package/dist/abstract/BaseDataAPI.cjs.map +1 -0
  12. package/dist/abstract/BaseDataAPI.d.cts +89 -0
  13. package/dist/abstract/BaseDataAPI.d.cts.map +1 -0
  14. package/dist/abstract/BaseDataAPI.d.mts +89 -0
  15. package/dist/abstract/BaseDataAPI.d.mts.map +1 -0
  16. package/dist/abstract/BaseDataAPI.mjs +135 -0
  17. package/dist/abstract/BaseDataAPI.mjs.map +1 -0
  18. package/dist/abstract/index.cjs +12 -0
  19. package/dist/abstract/index.cjs.map +1 -0
  20. package/dist/abstract/index.d.cts +7 -0
  21. package/dist/abstract/index.d.cts.map +1 -0
  22. package/dist/abstract/index.d.mts +7 -0
  23. package/dist/abstract/index.d.mts.map +1 -0
  24. package/dist/abstract/index.mjs +7 -0
  25. package/dist/abstract/index.mjs.map +1 -0
  26. package/dist/bcs.cjs +42 -0
  27. package/dist/bcs.cjs.map +1 -0
  28. package/dist/bcs.d.cts +91 -0
  29. package/dist/bcs.d.cts.map +1 -0
  30. package/dist/bcs.d.mts +91 -0
  31. package/dist/bcs.d.mts.map +1 -0
  32. package/dist/bcs.mjs +39 -0
  33. package/dist/bcs.mjs.map +1 -0
  34. package/dist/consts/deployments-slp-mainnet.json +710 -0
  35. package/dist/consts/deployments-slp-testnet.json +109 -0
  36. package/dist/consts/deployments-usdz-mainnet.json +180 -0
  37. package/dist/consts/deployments-usdz-testnet.json +98 -0
  38. package/dist/consts/{deployments-mainnet.json → deployments-zlp-mainnet.json} +278 -42
  39. package/dist/consts/index.cjs +40 -8
  40. package/dist/consts/index.cjs.map +1 -1
  41. package/dist/consts/index.d.cts +81 -11
  42. package/dist/consts/index.d.cts.map +1 -1
  43. package/dist/consts/index.d.mts +81 -11
  44. package/dist/consts/index.d.mts.map +1 -1
  45. package/dist/consts/index.mjs +39 -7
  46. package/dist/consts/index.mjs.map +1 -1
  47. package/dist/consts/price_id_to_object_id.mainnet.json +9 -1
  48. package/dist/data.cjs +2 -2
  49. package/dist/data.cjs.map +1 -1
  50. package/dist/data.mjs +2 -2
  51. package/dist/data.mjs.map +1 -1
  52. package/dist/factory/SDKFactory.cjs +185 -0
  53. package/dist/factory/SDKFactory.cjs.map +1 -0
  54. package/dist/factory/SDKFactory.d.cts +74 -0
  55. package/dist/factory/SDKFactory.d.cts.map +1 -0
  56. package/dist/factory/SDKFactory.d.mts +74 -0
  57. package/dist/factory/SDKFactory.d.mts.map +1 -0
  58. package/dist/factory/SDKFactory.mjs +179 -0
  59. package/dist/factory/SDKFactory.mjs.map +1 -0
  60. package/dist/implementations/SLPAPI.cjs +829 -0
  61. package/dist/implementations/SLPAPI.cjs.map +1 -0
  62. package/dist/implementations/SLPAPI.d.cts +120 -0
  63. package/dist/implementations/SLPAPI.d.cts.map +1 -0
  64. package/dist/implementations/SLPAPI.d.mts +120 -0
  65. package/dist/implementations/SLPAPI.d.mts.map +1 -0
  66. package/dist/implementations/SLPAPI.mjs +825 -0
  67. package/dist/implementations/SLPAPI.mjs.map +1 -0
  68. package/dist/implementations/SLPDataAPI.cjs +916 -0
  69. package/dist/implementations/SLPDataAPI.cjs.map +1 -0
  70. package/dist/implementations/SLPDataAPI.d.cts +102 -0
  71. package/dist/implementations/SLPDataAPI.d.cts.map +1 -0
  72. package/dist/implementations/SLPDataAPI.d.mts +102 -0
  73. package/dist/implementations/SLPDataAPI.d.mts.map +1 -0
  74. package/dist/implementations/SLPDataAPI.mjs +912 -0
  75. package/dist/implementations/SLPDataAPI.mjs.map +1 -0
  76. package/dist/implementations/USDZAPI.cjs +522 -0
  77. package/dist/implementations/USDZAPI.cjs.map +1 -0
  78. package/dist/implementations/USDZAPI.d.cts +118 -0
  79. package/dist/implementations/USDZAPI.d.cts.map +1 -0
  80. package/dist/implementations/USDZAPI.d.mts +118 -0
  81. package/dist/implementations/USDZAPI.d.mts.map +1 -0
  82. package/dist/implementations/USDZAPI.mjs +518 -0
  83. package/dist/implementations/USDZAPI.mjs.map +1 -0
  84. package/dist/implementations/USDZDataAPI.cjs +697 -0
  85. package/dist/implementations/USDZDataAPI.cjs.map +1 -0
  86. package/dist/implementations/USDZDataAPI.d.cts +86 -0
  87. package/dist/implementations/USDZDataAPI.d.cts.map +1 -0
  88. package/dist/implementations/USDZDataAPI.d.mts +86 -0
  89. package/dist/implementations/USDZDataAPI.d.mts.map +1 -0
  90. package/dist/implementations/USDZDataAPI.mjs +693 -0
  91. package/dist/implementations/USDZDataAPI.mjs.map +1 -0
  92. package/dist/implementations/ZLPAPI.cjs +809 -0
  93. package/dist/implementations/ZLPAPI.cjs.map +1 -0
  94. package/dist/implementations/ZLPAPI.d.cts +121 -0
  95. package/dist/implementations/ZLPAPI.d.cts.map +1 -0
  96. package/dist/implementations/ZLPAPI.d.mts +121 -0
  97. package/dist/implementations/ZLPAPI.d.mts.map +1 -0
  98. package/dist/implementations/ZLPAPI.mjs +805 -0
  99. package/dist/implementations/ZLPAPI.mjs.map +1 -0
  100. package/dist/implementations/ZLPDataAPI.cjs +724 -0
  101. package/dist/implementations/ZLPDataAPI.cjs.map +1 -0
  102. package/dist/implementations/ZLPDataAPI.d.cts +83 -0
  103. package/dist/implementations/ZLPDataAPI.d.cts.map +1 -0
  104. package/dist/implementations/ZLPDataAPI.d.mts +83 -0
  105. package/dist/implementations/ZLPDataAPI.d.mts.map +1 -0
  106. package/dist/implementations/ZLPDataAPI.mjs +720 -0
  107. package/dist/implementations/ZLPDataAPI.mjs.map +1 -0
  108. package/dist/implementations/index.cjs +22 -0
  109. package/dist/implementations/index.cjs.map +1 -0
  110. package/dist/implementations/index.d.cts +11 -0
  111. package/dist/implementations/index.d.cts.map +1 -0
  112. package/dist/implementations/index.d.mts +11 -0
  113. package/dist/implementations/index.d.mts.map +1 -0
  114. package/dist/implementations/index.mjs +13 -0
  115. package/dist/implementations/index.mjs.map +1 -0
  116. package/dist/index.cjs +47 -0
  117. package/dist/index.cjs.map +1 -1
  118. package/dist/index.d.cts +45 -0
  119. package/dist/index.d.cts.map +1 -1
  120. package/dist/index.d.mts +45 -0
  121. package/dist/index.d.mts.map +1 -1
  122. package/dist/index.mjs +45 -0
  123. package/dist/index.mjs.map +1 -1
  124. package/dist/interfaces/base.cjs +8 -0
  125. package/dist/interfaces/base.cjs.map +1 -0
  126. package/dist/interfaces/base.d.cts +293 -0
  127. package/dist/interfaces/base.d.cts.map +1 -0
  128. package/dist/interfaces/base.d.mts +293 -0
  129. package/dist/interfaces/base.d.mts.map +1 -0
  130. package/dist/interfaces/base.mjs +6 -0
  131. package/dist/interfaces/base.mjs.map +1 -0
  132. package/dist/interfaces/index.cjs +29 -0
  133. package/dist/interfaces/index.cjs.map +1 -0
  134. package/dist/interfaces/index.d.cts +13 -0
  135. package/dist/interfaces/index.d.cts.map +1 -0
  136. package/dist/interfaces/index.d.mts +13 -0
  137. package/dist/interfaces/index.d.mts.map +1 -0
  138. package/dist/interfaces/index.mjs +13 -0
  139. package/dist/interfaces/index.mjs.map +1 -0
  140. package/dist/interfaces/slp.cjs +9 -0
  141. package/dist/interfaces/slp.cjs.map +1 -0
  142. package/dist/interfaces/slp.d.cts +115 -0
  143. package/dist/interfaces/slp.d.cts.map +1 -0
  144. package/dist/interfaces/slp.d.mts +115 -0
  145. package/dist/interfaces/slp.d.mts.map +1 -0
  146. package/dist/interfaces/slp.mjs +7 -0
  147. package/dist/interfaces/slp.mjs.map +1 -0
  148. package/dist/interfaces/usdz.cjs +7 -0
  149. package/dist/interfaces/usdz.cjs.map +1 -0
  150. package/dist/interfaces/usdz.d.cts +40 -0
  151. package/dist/interfaces/usdz.d.cts.map +1 -0
  152. package/dist/interfaces/usdz.d.mts +40 -0
  153. package/dist/interfaces/usdz.d.mts.map +1 -0
  154. package/dist/interfaces/usdz.mjs +6 -0
  155. package/dist/interfaces/usdz.mjs.map +1 -0
  156. package/dist/interfaces/zlp.cjs +7 -0
  157. package/dist/interfaces/zlp.cjs.map +1 -0
  158. package/dist/interfaces/zlp.d.cts +45 -0
  159. package/dist/interfaces/zlp.d.cts.map +1 -0
  160. package/dist/interfaces/zlp.d.mts +45 -0
  161. package/dist/interfaces/zlp.d.mts.map +1 -0
  162. package/dist/interfaces/zlp.mjs +6 -0
  163. package/dist/interfaces/zlp.mjs.map +1 -0
  164. package/dist/oracle.cjs +7 -35
  165. package/dist/oracle.cjs.map +1 -1
  166. package/dist/oracle.d.cts +3 -4
  167. package/dist/oracle.d.cts.map +1 -1
  168. package/dist/oracle.d.mts +3 -4
  169. package/dist/oracle.d.mts.map +1 -1
  170. package/dist/oracle.mjs +8 -32
  171. package/dist/oracle.mjs.map +1 -1
  172. package/package.json +1 -1
  173. package/src/abstract/BaseAPI.ts +429 -0
  174. package/src/abstract/BaseDataAPI.ts +204 -0
  175. package/src/abstract/index.ts +7 -0
  176. package/src/bcs.ts +45 -0
  177. package/src/consts/deployments-slp-mainnet.json +710 -0
  178. package/src/consts/deployments-slp-testnet.json +109 -0
  179. package/src/consts/deployments-usdz-mainnet.json +180 -0
  180. package/src/consts/deployments-usdz-testnet.json +98 -0
  181. package/src/consts/{deployments-mainnet.json → deployments-zlp-mainnet.json} +279 -43
  182. package/src/consts/index.ts +134 -39
  183. package/src/consts/price_id_to_object_id.mainnet.json +10 -2
  184. package/src/data.ts +2 -2
  185. package/src/factory/SDKFactory.ts +282 -0
  186. package/src/implementations/SLPAPI.ts +1207 -0
  187. package/src/implementations/SLPDataAPI.ts +1188 -0
  188. package/src/implementations/USDZAPI.ts +715 -0
  189. package/src/implementations/USDZDataAPI.ts +826 -0
  190. package/src/implementations/ZLPAPI.ts +1130 -0
  191. package/src/implementations/ZLPDataAPI.ts +856 -0
  192. package/src/implementations/index.ts +14 -0
  193. package/src/index.ts +53 -0
  194. package/src/interfaces/base.ts +556 -0
  195. package/src/interfaces/index.ts +45 -0
  196. package/src/interfaces/slp.ts +156 -0
  197. package/src/interfaces/usdz.ts +71 -0
  198. package/src/interfaces/zlp.ts +96 -0
  199. package/src/oracle.ts +12 -42
  200. package/tsconfig.json +4 -2
  201. package/dist/consts/staking/deployments-mainnet.json +0 -12
  202. package/dist/consts/staking/deployments-testnet.json +0 -11
  203. package/src/consts/staking/deployments-mainnet.json +0 -12
  204. package/src/consts/staking/deployments-testnet.json +0 -11
  205. /package/dist/consts/{deployments-testnet.json → deployments-zlp-testnet.json} +0 -0
  206. /package/src/consts/{deployments-testnet.json → deployments-zlp-testnet.json} +0 -0
@@ -0,0 +1,856 @@
1
+ /**
2
+ * ZLP DataAPI implementation
3
+ * Implements ZLP-specific data access methods
4
+ */
5
+
6
+ import type { DynamicFieldInfo, SuiClient } from '@mysten/sui/client'
7
+ import type { Transaction } from '@mysten/sui/transactions'
8
+ import { SUI_CLOCK_OBJECT_ID } from '@mysten/sui/utils'
9
+ import type { Network } from '../consts'
10
+ import { LPToken, ZLP_TOKEN_DECIMALS, SECONDS_PER_EIGHT_HOUR } from '../consts'
11
+ import type {
12
+ IZLPDataAPI,
13
+ IZLPMarketValuationInfo,
14
+ IZLPMarketInfo,
15
+ IZLPVaultInfo,
16
+ IZLPSymbolInfo,
17
+ IZLPPositionInfo,
18
+ IZLPOrderInfo,
19
+ IZLPFundingFeeModel,
20
+ IZLPPositionConfig,
21
+ IZLPPositionCapInfo,
22
+ IZLPReservingFeeModel,
23
+ IZLPOrderCapInfo,
24
+ IZLPRebaseFeeModel,
25
+ IBaseHistoryResponse,
26
+ IBaseStaked,
27
+ IBaseStakePool
28
+ } from '../interfaces'
29
+ import { BaseDataAPI } from '../abstract'
30
+ import { parseSymbolKey, parseValue, suiSymbolToSymbol, joinSymbol } from '../utils'
31
+
32
+ export class ZLPDataAPI extends BaseDataAPI implements IZLPDataAPI {
33
+ constructor(
34
+ network: Network,
35
+ provider: SuiClient,
36
+ apiEndpoint: string,
37
+ connectionURL: string
38
+ ) {
39
+ super(network, provider, apiEndpoint, connectionURL, LPToken.ZLP)
40
+ }
41
+
42
+ public getStaked(owner: string): Promise<IBaseStaked> {
43
+ throw new Error('Method not implemented.')
44
+ }
45
+
46
+ public getStakePool(): Promise<IBaseStakePool> {
47
+ throw new Error('Method not implemented.')
48
+ }
49
+
50
+ /**
51
+ * Creates vaults valuation for ZLP
52
+ */
53
+ public valuateVaults(tx: Transaction) {
54
+ const vaultsValuation = tx.moveCall({
55
+ target: `${this.consts.zoCore.package}::market::create_vaults_valuation`,
56
+ typeArguments: [`${this.consts.zoCore.package}::zlp::ZLP`],
57
+ arguments: [
58
+ tx.object(SUI_CLOCK_OBJECT_ID),
59
+ tx.object(this.consts.zoCore.market),
60
+ ],
61
+ })
62
+
63
+ for (const key of Object.keys(this.consts.zoCore.vaults)) {
64
+ const vault = this.consts.zoCore.vaults[key]
65
+ tx.moveCall({
66
+ target: `${this.consts.zoCore.package}::market::valuate_vault`,
67
+ typeArguments: [
68
+ `${this.consts.zoCore.package}::zlp::ZLP`,
69
+ this.consts.coins[key].module,
70
+ ],
71
+ arguments: [
72
+ tx.object(this.consts.zoCore.market),
73
+ tx.object(vault.reservingFeeModel),
74
+ tx.object(this.consts.pythFeeder.feeder[key]),
75
+ vaultsValuation,
76
+ ],
77
+ })
78
+ }
79
+ return vaultsValuation
80
+ }
81
+
82
+ /**
83
+ * Creates symbols valuation for ZLP
84
+ */
85
+ public valuateSymbols(tx: Transaction) {
86
+ const symbolsValuation = tx.moveCall({
87
+ target: `${this.consts.zoCore.package}::market::create_symbols_valuation`,
88
+ typeArguments: [`${this.consts.zoCore.package}::zlp::ZLP`],
89
+ arguments: [
90
+ tx.object(SUI_CLOCK_OBJECT_ID),
91
+ tx.object(this.consts.zoCore.market),
92
+ ],
93
+ })
94
+
95
+ for (const key of Object.keys(this.consts.zoCore.symbols)) {
96
+ const [direction, token] = parseSymbolKey(key)
97
+ const symbol = this.consts.zoCore.symbols[key]
98
+ tx.moveCall({
99
+ target: `${this.consts.zoCore.package}::market::valuate_symbol`,
100
+ typeArguments: [
101
+ `${this.consts.zoCore.package}::zlp::ZLP`,
102
+ this.consts.coins[token].module,
103
+ `${this.consts.zoCore.package}::market::${direction.toUpperCase()}`,
104
+ ],
105
+ arguments: [
106
+ tx.object(this.consts.zoCore.market),
107
+ tx.object(symbol.fundingFeeModel),
108
+ tx.object(this.consts.pythFeeder.feeder[token]),
109
+ symbolsValuation,
110
+ ],
111
+ })
112
+ }
113
+ return symbolsValuation
114
+ }
115
+
116
+ /**
117
+ * Creates both vaults and symbols valuation
118
+ */
119
+ public valuate(tx: Transaction) {
120
+ const vaultsValuation = this.valuateVaults(tx)
121
+ const symbolsValuation = this.valuateSymbols(tx)
122
+ return { vaultsValuation, symbolsValuation }
123
+ }
124
+
125
+ /**
126
+ * Valuates the ZLP market
127
+ */
128
+ public async valuateMarket(): Promise<IZLPMarketValuationInfo> {
129
+ const marketInfo = await this.getMarketInfo()
130
+ const days = 7
131
+ const fee = await this.getPastFee(days)
132
+ let zlpPrice = 0
133
+ let value = 0
134
+
135
+ const vaultPromises = Object.keys(this.consts.zoCore.vaults).map(async (vault) => {
136
+ const vaultInfo = await this.getVaultInfo(vault)
137
+ const reservingFeeDelta = this.calculateVaultReservingFee(vaultInfo, vaultInfo.reservingFeeModel, Date.now() / 1000)
138
+ return (reservingFeeDelta + vaultInfo.liquidity + vaultInfo.reservedAmount) * (await this.getOraclePrice(vault)).getPriceUnchecked().getPriceAsNumberUnchecked() / (10 ** this.consts.coins[vault].decimals)
139
+ })
140
+
141
+ const symbolPromises = Object.keys(this.consts.zoCore.symbols).map(async (symbol) => {
142
+ const [direction, tokenId] = parseSymbolKey(symbol)
143
+ const symbolInfo = await this.getSymbolInfo(tokenId, direction === 'long')
144
+ const deltaSize = this.calcDeltaSize(symbolInfo, (await this.getOraclePrice(tokenId)).getPriceUnchecked().getPriceAsNumberUnchecked())
145
+ const fundingFeeDelta = this.calculateSymbolFundingFee(symbolInfo, symbolInfo.fundingFeeModel, (await this.getOraclePrice(tokenId)).getPriceUnchecked().getPriceAsNumberUnchecked(), marketInfo.lpSupplyWithDecimals, Date.now() / 1000)
146
+ return fundingFeeDelta + deltaSize
147
+ })
148
+
149
+ const [vaultValues, symbolValues] = await Promise.all([Promise.all(vaultPromises), Promise.all(symbolPromises)])
150
+
151
+ value = vaultValues.reduce((acc, curr) => acc + curr, 0)
152
+ value += symbolValues.reduce((acc, curr) => acc + curr, 0)
153
+
154
+ zlpPrice = value / marketInfo.lpSupplyWithDecimals
155
+
156
+ return {
157
+ marketCap: value,
158
+ price: zlpPrice,
159
+ supply: marketInfo.lpSupplyWithDecimals,
160
+ apr: (fee / value) * 365 / days,
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Gets ZLP market information
166
+ */
167
+ public async getMarketInfo(): Promise<IZLPMarketInfo> {
168
+ this.validateCache()
169
+ if (this.marketInfoCache) {
170
+ return this.marketInfoCache
171
+ }
172
+ const rawData = await this.provider.getObject({
173
+ id: this.consts.zoCore.market,
174
+ options: {
175
+ showContent: true,
176
+ },
177
+ })
178
+ return this.parseMarketInfo(rawData)
179
+ }
180
+
181
+ /**
182
+ * Gets ZLP vault information
183
+ */
184
+ public async getVaultInfo(vaultToken: string): Promise<IZLPVaultInfo> {
185
+ this.validateCache()
186
+ if (this.vaultInfoCache[vaultToken]) {
187
+ return this.vaultInfoCache[vaultToken]
188
+ }
189
+
190
+ const rawData = await this.provider.getDynamicFieldObject({
191
+ parentId: this.consts.zoCore.vaultsParent,
192
+ name: {
193
+ type: `${this.consts.zoCore.package}::market::VaultName<${this.consts.coins[vaultToken].module}>`,
194
+ value: { dummy_field: false },
195
+ },
196
+ })
197
+ return await this.parseVaultInfo(rawData)
198
+ }
199
+
200
+ /**
201
+ * Gets ZLP symbol information
202
+ */
203
+ public async getSymbolInfo(indexToken: string, long: boolean): Promise<IZLPSymbolInfo> {
204
+ this.validateCache()
205
+ const symbol = joinSymbol(long ? 'long' : 'short', indexToken)
206
+ if (this.symbolInfoCache[symbol]) {
207
+ return this.symbolInfoCache[symbol]
208
+ }
209
+ const rawData = await this.provider.getDynamicFieldObject({
210
+ parentId: this.consts.zoCore.symbolsParent,
211
+ name: {
212
+ type: `${this.consts.zoCore.package}::market::SymbolName<${this.consts.coins[indexToken].module}, ${this.consts.zoCore.package}::market::${long ? 'LONG' : 'SHORT'}>`,
213
+ value: { dummy_field: false },
214
+ },
215
+ })
216
+ return await this.parseSymbolInfo(rawData, long)
217
+ }
218
+
219
+ public async getPositionConfig(indexToken: string, long: boolean): Promise<IZLPPositionConfig> {
220
+ this.validateCache()
221
+ const symbol = joinSymbol(long ? 'long' : 'short', indexToken)
222
+ if (this.positionConfigCache[symbol]) {
223
+ return this.positionConfigCache[symbol]
224
+ }
225
+ const rawData = await this.provider.getObject({
226
+ id: this.consts.zoCore.symbols[symbol].positionConfig,
227
+ options: {
228
+ showContent: true,
229
+ },
230
+ })
231
+ return this.parsePositionConfig(rawData)
232
+ }
233
+
234
+ public async getPositionCapInfoList(owner: string): Promise<IZLPPositionCapInfo[]> {
235
+ const positionCapInfoList: IZLPPositionCapInfo[] = []
236
+ let cursor: string | undefined | null = undefined
237
+ let hasNextPage = true
238
+
239
+ while (hasNextPage) {
240
+ const positionCaps = await this.provider.getOwnedObjects({
241
+ owner,
242
+ filter: {
243
+ MoveModule: {
244
+ package: this.consts.zoCore.package,
245
+ module: 'market',
246
+ },
247
+ },
248
+ options: {
249
+ showType: true,
250
+ },
251
+ cursor,
252
+ })
253
+
254
+ for (const positionCap of positionCaps.data) {
255
+ if (positionCap.data?.type?.includes('PositionCap')) {
256
+ positionCapInfoList.push({
257
+ positionCapId: positionCap.data.objectId,
258
+ symbol0: positionCap.data.type.split('<')[1].split(',')[0].trim(),
259
+ symbol1: positionCap.data.type.split('<')[1].split(',')[1].split(',')[0].trim(),
260
+ long: positionCap.data.type.includes('LONG'),
261
+ })
262
+ }
263
+ }
264
+
265
+ // If we don't want to fetch all pages or there are no more pages, break the loop
266
+ hasNextPage = positionCaps.hasNextPage
267
+ cursor = positionCaps.nextCursor
268
+ }
269
+
270
+ return positionCapInfoList
271
+ }
272
+
273
+ public async getPositionInfoList(positionCapInfoList: IZLPPositionCapInfo[], owner: string, batchSize: number = 10) {
274
+ const positionInfoList: IZLPPositionInfo[] = []
275
+
276
+ // Process in batches of 10
277
+ for (let i = 0; i < positionCapInfoList.length; i += batchSize) {
278
+ const batch = positionCapInfoList.slice(i, i + batchSize)
279
+
280
+ await Promise.all(batch.map(async (positionCapInfo) => {
281
+ try {
282
+ const positionRaw = await this.provider.getDynamicFieldObject({
283
+ parentId: this.consts.zoCore.positionsParent,
284
+ name: {
285
+ type: `${this.consts.zoCore.package}::market::PositionName<${positionCapInfo.symbol0}, ${positionCapInfo.symbol1}, ${this.consts.zoCore.package}::market::${positionCapInfo.long ? 'LONG' : 'SHORT'}>`,
286
+ value: {
287
+ owner,
288
+ id: positionCapInfo.positionCapId,
289
+ },
290
+ },
291
+ })
292
+ positionInfoList.push(await this.parsePositionInfo(positionRaw, positionCapInfo.positionCapId))
293
+ } catch (error) {
294
+ // Position might have been deleted after force settlement
295
+ console.warn(`Failed to parse position info for position cap ID ${positionCapInfo.positionCapId}: ${error}`)
296
+ // Continue with next position without adding this one to the list
297
+ }
298
+ }))
299
+ }
300
+
301
+ return positionInfoList.sort((a, b) => a.openTimestamp > b.openTimestamp ? 1 : -1)
302
+ }
303
+
304
+ public async getOpenPositions(batchSize: number = 50, symbol: string = 'sui'): Promise<IZLPPositionInfo[]> {
305
+ let positionDynamicFields: DynamicFieldInfo[] = []
306
+ let _continue = true
307
+ let cursor = undefined
308
+ while (_continue) {
309
+ // data here will be a list of dynamic fields containing name and value
310
+ const { data, nextCursor, hasNextPage } =
311
+ await this.provider.getDynamicFields({
312
+ parentId: this.consts.zoCore.positionsParent,
313
+ cursor,
314
+ })
315
+
316
+ positionDynamicFields = positionDynamicFields.concat(data)
317
+ _continue = hasNextPage
318
+ cursor = nextCursor
319
+ }
320
+
321
+ // Filter by symbol if provided
322
+ if (symbol && this.consts.coins[symbol]) {
323
+ const coinModule = symbol === 'sui' ? '0x2::sui::SUI' : this.consts.coins[symbol].module
324
+ positionDynamicFields = positionDynamicFields.filter(field => {
325
+ // Extract the second coin module from PositionName<coin1, coin2, direction>
326
+ const typeStr = field.name?.type
327
+ if (!typeStr) return false
328
+
329
+ const match = typeStr.match(/PositionName<([^,]+),\s*([^,]+),\s*([^>]+)>/)
330
+ if (!match) return false
331
+
332
+ const secondCoin = match[2].trim()
333
+ return secondCoin === coinModule
334
+ })
335
+ } else {
336
+ return []
337
+ }
338
+
339
+ // then we query by dynamic field names and order by time
340
+ const positionInfoList: IZLPPositionInfo[] = []
341
+
342
+ // Process in batches of 50 (or specified batchSize)
343
+ for (let i = 0; i < positionDynamicFields.length; i += batchSize) {
344
+ const batch = positionDynamicFields.slice(i, i + batchSize)
345
+
346
+ await Promise.all(
347
+ batch.map(async positionDynamicField => {
348
+ const positionRaw = await this.provider.getDynamicFieldObject({
349
+ parentId: this.consts.zoCore.positionsParent,
350
+ name: positionDynamicField.name,
351
+ })
352
+
353
+ if (positionRaw?.data?.content) {
354
+ // @ts-ignore
355
+ if (positionRaw?.data?.content?.fields?.value?.fields?.closed) {
356
+ // skip closed positions
357
+ return
358
+ }
359
+ const positionInfo = await this.parsePositionInfo(
360
+ positionRaw,
361
+ positionDynamicField.objectId,
362
+ )
363
+ if (positionInfo) {
364
+ positionInfoList.push(positionInfo)
365
+ }
366
+ }
367
+ }),
368
+ )
369
+ }
370
+
371
+ return positionInfoList
372
+ .filter(positionInfo => !positionInfo.closed)
373
+ .sort((a, b) => (a.openTimestamp > b.openTimestamp ? 1 : -1))
374
+ }
375
+
376
+ public async getOrderCapInfoList(owner: string): Promise<IZLPOrderCapInfo[]> {
377
+ const orderCapInfoList: IZLPOrderCapInfo[] = []
378
+ let cursor: string | undefined | null = undefined
379
+ let hasNextPage = true
380
+
381
+ while (hasNextPage) {
382
+ const orderCaps = await this.provider.getOwnedObjects({
383
+ owner,
384
+ filter: {
385
+ MoveModule: {
386
+ package: this.consts.zoCore.package,
387
+ module: 'market',
388
+ },
389
+ },
390
+ options: {
391
+ showType: true,
392
+ showContent: true,
393
+ },
394
+ cursor,
395
+ })
396
+
397
+ for (const orderCap of orderCaps.data) {
398
+ if (orderCap.data?.type?.includes('OrderCap')) {
399
+ orderCapInfoList.push({
400
+ orderCapId: orderCap.data.objectId,
401
+ symbol0: orderCap.data.type.split('<')[1].split(',')[0].trim(),
402
+ symbol1: orderCap.data.type.split('<')[1].split(',')[1].split(',')[0].trim(),
403
+ long: orderCap.data.type.includes('LONG'),
404
+ positionId: (orderCap.data.content as any)?.fields?.position_id,
405
+ })
406
+ }
407
+ }
408
+
409
+ hasNextPage = orderCaps.hasNextPage
410
+ cursor = orderCaps.nextCursor
411
+ }
412
+
413
+ return orderCapInfoList
414
+ }
415
+
416
+ public async getOrderInfoList(orderCapInfoList: IZLPOrderCapInfo[], owner: string, batchSize: number = 10) {
417
+ const orderInfoList: IZLPOrderInfo[] = []
418
+
419
+ // Process in batches of 10
420
+ for (let i = 0; i < orderCapInfoList.length; i += batchSize) {
421
+ const batch = orderCapInfoList.slice(i, i + batchSize)
422
+
423
+ await Promise.all(batch.map(async (orderCapInfo) => {
424
+ try {
425
+ const orderRaw = await this.provider.getDynamicFieldObject({
426
+ parentId: this.consts.zoCore.ordersParent,
427
+ name: {
428
+ // We have enforced collateral coin type to match with fee coin type so we can use symbol0 in the first slot and the last slot of the OrderName
429
+ type: `${this.consts.zoCore.package}::market::OrderName<${orderCapInfo.symbol0}, ${orderCapInfo.symbol1}, ${this.consts.zoCore.package}::market::${orderCapInfo.long ? 'LONG' : 'SHORT'}, ${orderCapInfo.symbol0}>`,
430
+ value: {
431
+ owner,
432
+ id: orderCapInfo.orderCapId,
433
+ position_id: {
434
+ vec: orderCapInfo.positionId ? [orderCapInfo.positionId] : [],
435
+ },
436
+ },
437
+ },
438
+ })
439
+ orderInfoList.push(this.parseOrderInfo(orderRaw, orderCapInfo.orderCapId))
440
+ } catch (error) {
441
+ // Order might have been deleted
442
+ console.warn(`Failed to parse order info for order cap ID ${orderCapInfo.orderCapId}: ${error}`)
443
+ // Continue with next order without adding this one to the list
444
+ }
445
+ }))
446
+ }
447
+
448
+ return orderInfoList.sort((a, b) => a.createdAt > b.createdAt ? 1 : -1)
449
+ }
450
+
451
+ public async hasReferral(referree: string): Promise<boolean> {
452
+ const raw = await this.getReferralData(referree)
453
+ return !raw.error
454
+ }
455
+
456
+ public async getReferralData(referree: string): Promise<any> {
457
+ const raw = await this.provider.getDynamicFieldObject({
458
+ parentId: this.consts.zoCore.referralsParent,
459
+ name: {
460
+ type: 'address',
461
+ value: referree,
462
+ },
463
+ })
464
+ return raw
465
+ }
466
+
467
+ /**
468
+ * Gets rebase fee model for ZLP
469
+ */
470
+ public async getRebaseFeeModel(): Promise<IZLPRebaseFeeModel> {
471
+ this.validateCache()
472
+ if (this.rebaseFeeModelCache) {
473
+ return this.rebaseFeeModelCache
474
+ }
475
+ const rawData = await this.provider.getObject({
476
+ id: this.consts.zoCore.rebaseFeeModel,
477
+ options: {
478
+ showContent: true,
479
+ },
480
+ })
481
+ return this.parseRebaseFeeModel(rawData)
482
+ }
483
+
484
+ public async fundingFeeRate(indexToken: string, long: boolean,): Promise<number> {
485
+ const symbol = await this.getSymbolInfo(indexToken, long)
486
+ if (symbol.lastUpdate <= 0) {
487
+ return 0
488
+ }
489
+ const price = (await this.getOraclePrice(indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked()
490
+ const lpSupplyAmount = (await this.getMarketInfo()).lpSupplyWithDecimals
491
+ const model = symbol.fundingFeeModel
492
+ const elapsed = SECONDS_PER_EIGHT_HOUR
493
+
494
+ const deltaSize = this.calcDeltaSize(symbol, price)
495
+ const pnlPerLp = (symbol.realisedPnl + symbol.unrealisedFundingFeeValue + deltaSize) / lpSupplyAmount
496
+ return this.calcFundingFeeRate(model, pnlPerLp, elapsed)
497
+ }
498
+
499
+ public async rebaseFeeRate(collateralToken: string, increase: boolean, amount: number): Promise<number> {
500
+ let vaultValue = 0
501
+ if (!increase && amount > 0) {
502
+ amount = -amount
503
+ }
504
+ const value = amount * (await this.getOraclePrice(collateralToken)).getPriceUnchecked().getPriceAsNumberUnchecked() / (10 ** this.consts.coins[collateralToken].decimals)
505
+ const vaultPromises = Object.keys(this.consts.zoCore.vaults).map(async (vault) => {
506
+ const vaultInfo = await this.getVaultInfo(vault)
507
+ const reservingFeeDelta = this.calculateVaultReservingFee(vaultInfo, vaultInfo.reservingFeeModel, Date.now() / 1000)
508
+ const res = (reservingFeeDelta + vaultInfo.liquidity + vaultInfo.reservedAmount) * (await this.getOraclePrice(vault)).getPriceUnchecked().getPriceAsNumberUnchecked() / (10 ** this.consts.coins[vault].decimals)
509
+ if (collateralToken === vault) {
510
+ vaultValue = res
511
+ }
512
+ return res
513
+ })
514
+
515
+ const vaultValues = await Promise.all(vaultPromises)
516
+ const totalVaultValue = vaultValues.reduce((acc, curr) => acc + curr, 0)
517
+ const targetRatio = Number.parseInt(
518
+ this.consts.zoCore.vaults[collateralToken].weight,
519
+ 10,
520
+ ) / Object.values(this.consts.zoCore.vaults)
521
+ .map(e => Number.parseInt(e.weight, 10))
522
+ .reduce((acc, curr) => acc + curr, 0)
523
+
524
+ return this.calcRebaseFeeRate(
525
+ await this.getRebaseFeeModel(),
526
+ increase,
527
+ (vaultValue + value) / (totalVaultValue + value),
528
+ targetRatio,
529
+ )
530
+ }
531
+
532
+ public async reservingFeeRate(collateralToken: string, amount = 0): Promise<number> {
533
+ const vaultInfo = await this.getVaultInfo(collateralToken)
534
+ const vaultSupply = vaultInfo.liquidity + vaultInfo.reservedAmount + vaultInfo.unrealisedReservingFeeAmount + amount
535
+ const utilization = vaultSupply ? ((vaultInfo.reservedAmount + amount) / vaultSupply) : 0
536
+ return this.calcReservingFeeRate(vaultInfo.reservingFeeModel, utilization, SECONDS_PER_EIGHT_HOUR)
537
+ }
538
+
539
+ public async getHistory(trader: string, page: number, limit: number, orderType?: string, symbol?: string): Promise<IBaseHistoryResponse> {
540
+ const params = new URLSearchParams({
541
+ trader,
542
+ page: page.toString(),
543
+ limit: limit.toString(),
544
+ })
545
+
546
+ // Add filter parameters if provided
547
+ if (orderType && orderType !== 'all') {
548
+ params.append('orderType', orderType)
549
+ }
550
+ if (symbol && symbol !== 'all') {
551
+ params.append('symbol', symbol)
552
+ }
553
+
554
+ const url = `${this.apiEndpoint}/traderEvents?${params}`
555
+ const res = await fetch(url, {
556
+ method: 'GET',
557
+ headers: {
558
+ 'Content-Type': 'application/json',
559
+ },
560
+ })
561
+ const response = await res.json() as any
562
+
563
+ return {
564
+ histories: response.data?.histories || [],
565
+ pagination: response.data?.pagination || {
566
+ total: 0,
567
+ page: 1,
568
+ limit: 20,
569
+ pages: 0,
570
+ },
571
+ }
572
+ }
573
+
574
+ // Private helper methods
575
+ private calcFundingFeeRate(model: IZLPFundingFeeModel, pnlPerRate: number, elapsed: number): number {
576
+ const dailyRate = Math.min(model.multiplier * Math.abs(pnlPerRate), model.max)
577
+ const secondsRate = dailyRate * elapsed / SECONDS_PER_EIGHT_HOUR
578
+ return pnlPerRate >= 0 ? -secondsRate : secondsRate
579
+ }
580
+
581
+ private calcAccFundingFeeRate(symbol: IZLPSymbolInfo, model: IZLPFundingFeeModel, price: number, lpSupplyAmount: number, timestamp: number): number {
582
+ if (symbol.lastUpdate > 0) {
583
+ const elapsed = timestamp - symbol.lastUpdate
584
+ if (elapsed > 0) {
585
+ const deltaSize = this.calcDeltaSize(symbol, price)
586
+ const pnlPerLp = (symbol.realisedPnl + symbol.unrealisedFundingFeeValue + deltaSize) / lpSupplyAmount
587
+ return symbol.accFundingRate + this.calcFundingFeeRate(model, pnlPerLp, elapsed)
588
+ }
589
+ }
590
+ return symbol.accFundingRate
591
+ }
592
+
593
+ private calculateSymbolFundingFee(symbol: IZLPSymbolInfo, model: IZLPFundingFeeModel, price: number, lpSupplyAmount: number, timestamp: number): number {
594
+ const accFundingRate = this.calcAccFundingFeeRate(symbol, model, price, lpSupplyAmount, timestamp)
595
+ return symbol.unrealisedFundingFeeValue + (accFundingRate - symbol.accFundingRate) * symbol.openingSize
596
+ }
597
+
598
+ private calculatePositionReserveFee(position: IZLPPositionInfo, vault: IZLPVaultInfo, model: IZLPReservingFeeModel, timestamp: number): number {
599
+ const accReservingRate = this.calcAccReservingFeeRate(vault, model, timestamp)
600
+ return position.reservingFeeAmount + (accReservingRate - position.lastReservingRate) * position.collateralAmount
601
+ }
602
+
603
+ private calcAccReservingFeeRate(vault: IZLPVaultInfo, model: IZLPReservingFeeModel, timestamp: number): number {
604
+ if (vault.lastUpdate > 0) {
605
+ const elapsed = timestamp - vault.lastUpdate
606
+ if (elapsed > 0) {
607
+ const utilization = this.vaultUtilization(vault)
608
+ return vault.accReservingRate + this.calcReservingFeeRate(model, utilization, elapsed)
609
+ }
610
+ }
611
+ return vault.accReservingRate
612
+ }
613
+
614
+ private calcRebaseFeeRate(model: IZLPRebaseFeeModel, increase: boolean, ratio: number, targetRatio: number): number {
615
+ if ((increase && ratio <= targetRatio) || (!increase && ratio >= targetRatio)) {
616
+ return model.base
617
+ }
618
+ return model.base + model.multiplier * Math.abs(ratio - targetRatio)
619
+ }
620
+
621
+ private vaultUtilization(vault: IZLPVaultInfo): number {
622
+ const supplyAmount = vault.liquidity + vault.reservedAmount + vault.unrealisedReservingFeeAmount
623
+ if (supplyAmount === 0) {
624
+ return 0
625
+ }
626
+ return vault.reservedAmount / supplyAmount
627
+ }
628
+
629
+ private calcReservingFeeRate(model: IZLPReservingFeeModel, utilization: number, elapsed: number): number {
630
+ return model.multiplier * utilization * elapsed / SECONDS_PER_EIGHT_HOUR
631
+ }
632
+
633
+ private calcDeltaSize(symbol: IZLPSymbolInfo, price: number): number {
634
+ const latestSize = symbol.openingAmount / symbol.priceConfig.precision * price
635
+ return symbol.long ? symbol.openingSize - latestSize : latestSize - symbol.openingSize
636
+ }
637
+
638
+ private parseMarketInfo(raw: any): IZLPMarketInfo {
639
+ const content = raw.data.content.fields
640
+
641
+ return {
642
+ lpSupply: content.lp_supply.fields.value,
643
+ positionId: content.positions.fields.id.id,
644
+ vaultId: content.vaults.fields.id.id,
645
+ symbolId: content.symbols.fields.id.id,
646
+ referralId: content.referrals.fields.id.id,
647
+ orderId: content.orders.fields.id.id,
648
+ rebaseFeeModel: content.rebase_fee_model,
649
+ lpSupplyWithDecimals: content.lp_supply.fields.value / (10 ** ZLP_TOKEN_DECIMALS),
650
+ }
651
+ }
652
+
653
+ private async parseVaultInfo(raw: any): Promise<IZLPVaultInfo> {
654
+ const vaultFields = raw.data.content.fields.value.fields
655
+ const reservingFeeModelAddr = vaultFields.reserving_fee_model
656
+ const reservingFeeModelRaw = await this.provider.getObject({
657
+ id: reservingFeeModelAddr,
658
+ options: {
659
+ showContent: true,
660
+ },
661
+ })
662
+ const reservingFeeModel = this.parseReservingFeeModel(reservingFeeModelRaw)
663
+
664
+ return {
665
+ liquidity: parseValue(vaultFields.liquidity),
666
+ reservedAmount: parseValue(vaultFields.reserved_amount),
667
+ unrealisedReservingFeeAmount: parseValue(
668
+ vaultFields.unrealised_reserving_fee_amount,
669
+ ),
670
+ accReservingRate: parseValue(vaultFields.acc_reserving_rate),
671
+ enabled: vaultFields.enabled,
672
+ weight: parseValue(vaultFields.weight),
673
+ lastUpdate: parseValue(vaultFields.last_update),
674
+ reservingFeeModel,
675
+ priceConfig: {
676
+ maxInterval: parseValue(vaultFields.price_config.fields.max_interval),
677
+ maxConfidence: parseValue(vaultFields.price_config.fields.max_confidence),
678
+ precision: parseValue(vaultFields.price_config.fields.precision),
679
+ feeder: vaultFields.price_config.fields.feeder,
680
+ },
681
+ }
682
+ }
683
+
684
+ private async parseSymbolInfo(raw: any, long: boolean): Promise<IZLPSymbolInfo> {
685
+ const { fields } = raw.data.content.fields.value
686
+ const fundingFeeModelAddr = fields.funding_fee_model
687
+ const fundingFeeModelRaw = await this.provider.getObject({
688
+ id: fundingFeeModelAddr,
689
+ options: {
690
+ showContent: true,
691
+ },
692
+ })
693
+ const fundingFeeModel = this.parseFundingFeeModel(fundingFeeModelRaw)
694
+
695
+ return {
696
+ openingSize: parseValue(fields.opening_size),
697
+ openingAmount: parseValue(fields.opening_amount),
698
+ accFundingRate: parseValue(fields.acc_funding_rate),
699
+ realisedPnl: parseValue(fields.realised_pnl),
700
+ unrealisedFundingFeeValue: parseValue(fields.unrealised_funding_fee_value),
701
+ openEnabled: fields.open_enabled,
702
+ liquidateEnabled: fields.liquidate_enabled,
703
+ decreaseEnabled: fields.decrease_enabled,
704
+ lastUpdate: parseValue(fields.last_update),
705
+ fundingFeeModel,
706
+ long,
707
+ priceConfig: {
708
+ maxInterval: parseValue(fields.price_config.fields.max_interval),
709
+ maxConfidence: parseValue(fields.price_config.fields.max_confidence),
710
+ precision: parseValue(fields.price_config.fields.precision),
711
+ feeder: fields.price_config.fields.feeder,
712
+ },
713
+ }
714
+ }
715
+
716
+ private parsePositionConfig(raw: any): IZLPPositionConfig {
717
+ const positionConfigFields = raw.data.content.fields.inner.fields
718
+
719
+ return {
720
+ decreaseFeeBps: parseValue(positionConfigFields.decrease_fee_bps),
721
+ liquidationBonus: parseValue(positionConfigFields.liquidation_bonus),
722
+ liquidationThreshold: parseValue(positionConfigFields.liquidation_threshold),
723
+ maxLeverage: parseValue(positionConfigFields.max_leverage),
724
+ minHoldingDuration: parseValue(positionConfigFields.min_holding_duration),
725
+ openFeeBps: parseValue(positionConfigFields.open_fee_bps),
726
+ maxReservedMultiplier: parseValue(positionConfigFields.max_reserved_multiplier),
727
+ minCollateralValue: parseValue(positionConfigFields.min_collateral_value),
728
+ }
729
+ }
730
+
731
+ private async parsePositionInfo(raw: any, id_: string): Promise<IZLPPositionInfo> {
732
+ const { content } = raw.data
733
+ const { fields } = content
734
+ const positionFields = fields.value.fields
735
+ const dataType = fields.name.type
736
+
737
+ const positionInfo = {
738
+ id: id_,
739
+ long: dataType.includes('::market::LONG'),
740
+ owner: fields.name.fields.owner,
741
+ version: Number.parseInt(raw.data.version, 10),
742
+ collateralToken: suiSymbolToSymbol(dataType.split('<')[1].split(',')[0].trim(), this.consts),
743
+ indexToken: suiSymbolToSymbol(dataType.split(',')[1].trim(), this.consts),
744
+ collateralAmount: parseValue(positionFields.collateral),
745
+ positionAmount: parseValue(positionFields.position_amount),
746
+ reservedAmount: parseValue(positionFields.reserved),
747
+ positionSize: parseValue(positionFields.position_size),
748
+ lastFundingRate: parseValue(positionFields.last_funding_rate),
749
+ lastReservingRate: parseValue(positionFields.last_reserving_rate),
750
+ reservingFeeAmount: parseValue(positionFields.reserving_fee_amount),
751
+ fundingFeeValue: parseValue(positionFields.funding_fee_value),
752
+ closed: positionFields.closed,
753
+ openTimestamp: parseValue(positionFields.open_timestamp),
754
+ }
755
+
756
+ positionInfo.reservingFeeAmount = this.calculatePositionReserveFee(positionInfo, await this.getVaultInfo(positionInfo.collateralToken), (await this.getVaultInfo(positionInfo.collateralToken)).reservingFeeModel, Date.now() / 1000)
757
+ positionInfo.fundingFeeValue = this.calculatePositionFundingFee(positionInfo, await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long), (await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long)).fundingFeeModel, (await this.getOraclePrice(positionInfo.indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked(), (await this.getMarketInfo()).lpSupplyWithDecimals, Date.now() / 1000)
758
+
759
+ return positionInfo
760
+ }
761
+
762
+ private calculatePositionFundingFee(position: IZLPPositionInfo, symbol: IZLPSymbolInfo, model: IZLPFundingFeeModel, price: number, lpSupplyAmount: number, timestamp: number): number {
763
+ const accFundingRate = this.calcAccFundingFeeRate(symbol, model, price, lpSupplyAmount, timestamp)
764
+ return position.fundingFeeValue + (accFundingRate - position.lastFundingRate) * position.positionSize
765
+ }
766
+
767
+ private parseRebaseFeeModel(raw: any): { base: number; multiplier: number } {
768
+ const { fields } = raw.data.content
769
+
770
+ return {
771
+ base: parseValue(fields.base),
772
+ multiplier: parseValue(fields.multiplier),
773
+ }
774
+ }
775
+
776
+ private parseReservingFeeModel(raw: any): { multiplier: number } {
777
+ const content = raw.data.content.fields
778
+ return {
779
+ multiplier: parseValue(content.multiplier),
780
+ }
781
+ }
782
+
783
+ private parseFundingFeeModel(raw: any): { multiplier: number; max: number } {
784
+ const content = raw.data.content.fields
785
+ return {
786
+ multiplier: parseValue(content.multiplier),
787
+ max: parseValue(content.max),
788
+ }
789
+ }
790
+
791
+ private parseOrderInfo(raw: any, capId: string): IZLPOrderInfo {
792
+ const { content } = raw.data
793
+ const { fields } = content.fields.value
794
+
795
+ // Extract tokens from dataType
796
+ const dataType = content.type
797
+
798
+ const orderType = content.fields.value.type.includes('OpenPositionOrder') ? 'OPEN_POSITION' : 'DECREASE_POSITION'
799
+
800
+ const ret: IZLPOrderInfo = {
801
+ id: content.fields.id.id,
802
+ capId,
803
+ executed: fields.executed,
804
+ owner: content.fields.name.fields.owner,
805
+ collateralToken: suiSymbolToSymbol(dataType.split('<')[2].split(',')[0].trim(), this.consts),
806
+ indexToken: suiSymbolToSymbol(dataType.split(',')[1].trim(), this.consts),
807
+ feeToken: suiSymbolToSymbol(dataType.split(',')[3].split('>')[0].trim(), this.consts),
808
+ indexPrice: parseValue(fields.limited_index_price.fields.price || fields.limited_index_price),
809
+ collateralPriceThreshold: parseValue(fields.collateral_price_threshold),
810
+ feeAmount: BigInt(fields.fee),
811
+ long: dataType.includes('::market::LONG'),
812
+ orderType,
813
+ createdAt: parseValue(fields.created_at),
814
+ v11Order: !fields.limited_index_price.fields.price,
815
+ }
816
+
817
+ if (orderType === 'OPEN_POSITION') {
818
+ ret.openOrder = {
819
+ reserveAmount: BigInt(fields.reserve_amount),
820
+ collateralAmount: BigInt(fields.collateral),
821
+ openAmount: BigInt(fields.open_amount),
822
+ }
823
+ }
824
+ else {
825
+ ret.decreaseOrder = {
826
+ decreaseAmount: BigInt(fields.decrease_amount),
827
+ takeProfit: fields.take_profit,
828
+ }
829
+ }
830
+
831
+ return ret
832
+ }
833
+
834
+ private async getPastFee(days = 7): Promise<number> {
835
+ // TODO: Check with Sentio data
836
+ const url = `${this.apiEndpoint}/histories/proxy/fee?days=${days}`
837
+ const res = await fetch(url, {
838
+ method: 'GET',
839
+ headers: {
840
+ 'Content-Type': 'application/json',
841
+ },
842
+ })
843
+ return Number.parseFloat(await res.text() || '0')
844
+ }
845
+
846
+ private calculateVaultReservingFee(
847
+ vaultInfo: IZLPVaultInfo,
848
+ reservingFeeModel: { multiplier: number },
849
+ currentTime: number
850
+ ): number {
851
+ const timeDelta = currentTime - vaultInfo.lastUpdate
852
+ const periods = Math.floor(timeDelta / SECONDS_PER_EIGHT_HOUR)
853
+ return vaultInfo.unrealisedReservingFeeAmount +
854
+ (vaultInfo.reservedAmount * reservingFeeModel.multiplier * periods) / 1e18
855
+ }
856
+ }