@zofai/zo-sdk 0.1.92

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 (275) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/.gitattributes +4 -0
  3. package/.prettierrc.js +9 -0
  4. package/README.md +28 -0
  5. package/dist/abstract/BaseAPI.cjs +206 -0
  6. package/dist/abstract/BaseAPI.cjs.map +1 -0
  7. package/dist/abstract/BaseAPI.d.cts +172 -0
  8. package/dist/abstract/BaseAPI.d.cts.map +1 -0
  9. package/dist/abstract/BaseAPI.d.mts +172 -0
  10. package/dist/abstract/BaseAPI.d.mts.map +1 -0
  11. package/dist/abstract/BaseAPI.mjs +202 -0
  12. package/dist/abstract/BaseAPI.mjs.map +1 -0
  13. package/dist/abstract/BaseDataAPI.cjs +140 -0
  14. package/dist/abstract/BaseDataAPI.cjs.map +1 -0
  15. package/dist/abstract/BaseDataAPI.d.cts +89 -0
  16. package/dist/abstract/BaseDataAPI.d.cts.map +1 -0
  17. package/dist/abstract/BaseDataAPI.d.mts +89 -0
  18. package/dist/abstract/BaseDataAPI.d.mts.map +1 -0
  19. package/dist/abstract/BaseDataAPI.mjs +136 -0
  20. package/dist/abstract/BaseDataAPI.mjs.map +1 -0
  21. package/dist/abstract/index.cjs +12 -0
  22. package/dist/abstract/index.cjs.map +1 -0
  23. package/dist/abstract/index.d.cts +7 -0
  24. package/dist/abstract/index.d.cts.map +1 -0
  25. package/dist/abstract/index.d.mts +7 -0
  26. package/dist/abstract/index.d.mts.map +1 -0
  27. package/dist/abstract/index.mjs +7 -0
  28. package/dist/abstract/index.mjs.map +1 -0
  29. package/dist/api.cjs +779 -0
  30. package/dist/api.cjs.map +1 -0
  31. package/dist/api.d.cts +75 -0
  32. package/dist/api.d.cts.map +1 -0
  33. package/dist/api.d.mts +75 -0
  34. package/dist/api.d.mts.map +1 -0
  35. package/dist/api.mjs +775 -0
  36. package/dist/api.mjs.map +1 -0
  37. package/dist/bcs.cjs +42 -0
  38. package/dist/bcs.cjs.map +1 -0
  39. package/dist/bcs.d.cts +91 -0
  40. package/dist/bcs.d.cts.map +1 -0
  41. package/dist/bcs.d.mts +91 -0
  42. package/dist/bcs.d.mts.map +1 -0
  43. package/dist/bcs.mjs +39 -0
  44. package/dist/bcs.mjs.map +1 -0
  45. package/dist/consts/deployments-shared-mainnet.json +50 -0
  46. package/dist/consts/deployments-shared-testnet.json +45 -0
  47. package/dist/consts/deployments-slp-mainnet.json +600 -0
  48. package/dist/consts/deployments-slp-testnet.json +87 -0
  49. package/dist/consts/deployments-usdz-mainnet.json +494 -0
  50. package/dist/consts/deployments-usdz-testnet.json +98 -0
  51. package/dist/consts/deployments-zbtcvc-mainnet.json +180 -0
  52. package/dist/consts/deployments-zlp-mainnet.json +791 -0
  53. package/dist/consts/deployments-zlp-testnet.json +76 -0
  54. package/dist/consts/index.cjs +200 -0
  55. package/dist/consts/index.cjs.map +1 -0
  56. package/dist/consts/index.d.cts +157 -0
  57. package/dist/consts/index.d.cts.map +1 -0
  58. package/dist/consts/index.d.mts +157 -0
  59. package/dist/consts/index.d.mts.map +1 -0
  60. package/dist/consts/index.mjs +189 -0
  61. package/dist/consts/index.mjs.map +1 -0
  62. package/dist/consts/price_id_to_object_id.mainnet.json +56 -0
  63. package/dist/consts/price_id_to_object_id.testnet.json +17 -0
  64. package/dist/data.cjs +919 -0
  65. package/dist/data.cjs.map +1 -0
  66. package/dist/data.d.cts +235 -0
  67. package/dist/data.d.cts.map +1 -0
  68. package/dist/data.d.mts +235 -0
  69. package/dist/data.d.mts.map +1 -0
  70. package/dist/data.mjs +915 -0
  71. package/dist/data.mjs.map +1 -0
  72. package/dist/factory/SDKFactory.cjs +228 -0
  73. package/dist/factory/SDKFactory.cjs.map +1 -0
  74. package/dist/factory/SDKFactory.d.cts +84 -0
  75. package/dist/factory/SDKFactory.d.cts.map +1 -0
  76. package/dist/factory/SDKFactory.d.mts +84 -0
  77. package/dist/factory/SDKFactory.d.mts.map +1 -0
  78. package/dist/factory/SDKFactory.mjs +222 -0
  79. package/dist/factory/SDKFactory.mjs.map +1 -0
  80. package/dist/implementations/SLPAPI.cjs +1794 -0
  81. package/dist/implementations/SLPAPI.cjs.map +1 -0
  82. package/dist/implementations/SLPAPI.d.cts +183 -0
  83. package/dist/implementations/SLPAPI.d.cts.map +1 -0
  84. package/dist/implementations/SLPAPI.d.mts +183 -0
  85. package/dist/implementations/SLPAPI.d.mts.map +1 -0
  86. package/dist/implementations/SLPAPI.mjs +1790 -0
  87. package/dist/implementations/SLPAPI.mjs.map +1 -0
  88. package/dist/implementations/SLPDataAPI.cjs +1384 -0
  89. package/dist/implementations/SLPDataAPI.cjs.map +1 -0
  90. package/dist/implementations/SLPDataAPI.d.cts +158 -0
  91. package/dist/implementations/SLPDataAPI.d.cts.map +1 -0
  92. package/dist/implementations/SLPDataAPI.d.mts +158 -0
  93. package/dist/implementations/SLPDataAPI.d.mts.map +1 -0
  94. package/dist/implementations/SLPDataAPI.mjs +1380 -0
  95. package/dist/implementations/SLPDataAPI.mjs.map +1 -0
  96. package/dist/implementations/USDZAPI.cjs +1676 -0
  97. package/dist/implementations/USDZAPI.cjs.map +1 -0
  98. package/dist/implementations/USDZAPI.d.cts +180 -0
  99. package/dist/implementations/USDZAPI.d.cts.map +1 -0
  100. package/dist/implementations/USDZAPI.d.mts +180 -0
  101. package/dist/implementations/USDZAPI.d.mts.map +1 -0
  102. package/dist/implementations/USDZAPI.mjs +1672 -0
  103. package/dist/implementations/USDZAPI.mjs.map +1 -0
  104. package/dist/implementations/USDZDataAPI.cjs +1209 -0
  105. package/dist/implementations/USDZDataAPI.cjs.map +1 -0
  106. package/dist/implementations/USDZDataAPI.d.cts +191 -0
  107. package/dist/implementations/USDZDataAPI.d.cts.map +1 -0
  108. package/dist/implementations/USDZDataAPI.d.mts +191 -0
  109. package/dist/implementations/USDZDataAPI.d.mts.map +1 -0
  110. package/dist/implementations/USDZDataAPI.mjs +1205 -0
  111. package/dist/implementations/USDZDataAPI.mjs.map +1 -0
  112. package/dist/implementations/ZBTCVCAPI.cjs +906 -0
  113. package/dist/implementations/ZBTCVCAPI.cjs.map +1 -0
  114. package/dist/implementations/ZBTCVCAPI.d.cts +107 -0
  115. package/dist/implementations/ZBTCVCAPI.d.cts.map +1 -0
  116. package/dist/implementations/ZBTCVCAPI.d.mts +107 -0
  117. package/dist/implementations/ZBTCVCAPI.d.mts.map +1 -0
  118. package/dist/implementations/ZBTCVCAPI.mjs +902 -0
  119. package/dist/implementations/ZBTCVCAPI.mjs.map +1 -0
  120. package/dist/implementations/ZBTCVCDataAPI.cjs +829 -0
  121. package/dist/implementations/ZBTCVCDataAPI.cjs.map +1 -0
  122. package/dist/implementations/ZBTCVCDataAPI.d.cts +94 -0
  123. package/dist/implementations/ZBTCVCDataAPI.d.cts.map +1 -0
  124. package/dist/implementations/ZBTCVCDataAPI.d.mts +94 -0
  125. package/dist/implementations/ZBTCVCDataAPI.d.mts.map +1 -0
  126. package/dist/implementations/ZBTCVCDataAPI.mjs +825 -0
  127. package/dist/implementations/ZBTCVCDataAPI.mjs.map +1 -0
  128. package/dist/implementations/ZLPAPI.cjs +1948 -0
  129. package/dist/implementations/ZLPAPI.cjs.map +1 -0
  130. package/dist/implementations/ZLPAPI.d.cts +192 -0
  131. package/dist/implementations/ZLPAPI.d.cts.map +1 -0
  132. package/dist/implementations/ZLPAPI.d.mts +192 -0
  133. package/dist/implementations/ZLPAPI.d.mts.map +1 -0
  134. package/dist/implementations/ZLPAPI.mjs +1944 -0
  135. package/dist/implementations/ZLPAPI.mjs.map +1 -0
  136. package/dist/implementations/ZLPDataAPI.cjs +1267 -0
  137. package/dist/implementations/ZLPDataAPI.cjs.map +1 -0
  138. package/dist/implementations/ZLPDataAPI.d.cts +193 -0
  139. package/dist/implementations/ZLPDataAPI.d.cts.map +1 -0
  140. package/dist/implementations/ZLPDataAPI.d.mts +193 -0
  141. package/dist/implementations/ZLPDataAPI.d.mts.map +1 -0
  142. package/dist/implementations/ZLPDataAPI.mjs +1263 -0
  143. package/dist/implementations/ZLPDataAPI.mjs.map +1 -0
  144. package/dist/implementations/index.cjs +26 -0
  145. package/dist/implementations/index.cjs.map +1 -0
  146. package/dist/implementations/index.d.cts +13 -0
  147. package/dist/implementations/index.d.cts.map +1 -0
  148. package/dist/implementations/index.d.mts +13 -0
  149. package/dist/implementations/index.d.mts.map +1 -0
  150. package/dist/implementations/index.mjs +15 -0
  151. package/dist/implementations/index.mjs.map +1 -0
  152. package/dist/index.cjs +69 -0
  153. package/dist/index.cjs.map +1 -0
  154. package/dist/index.d.cts +51 -0
  155. package/dist/index.d.cts.map +1 -0
  156. package/dist/index.d.mts +51 -0
  157. package/dist/index.d.mts.map +1 -0
  158. package/dist/index.mjs +51 -0
  159. package/dist/index.mjs.map +1 -0
  160. package/dist/interfaces/base.cjs +7 -0
  161. package/dist/interfaces/base.cjs.map +1 -0
  162. package/dist/interfaces/base.d.cts +346 -0
  163. package/dist/interfaces/base.d.cts.map +1 -0
  164. package/dist/interfaces/base.d.mts +346 -0
  165. package/dist/interfaces/base.d.mts.map +1 -0
  166. package/dist/interfaces/base.mjs +6 -0
  167. package/dist/interfaces/base.mjs.map +1 -0
  168. package/dist/interfaces/index.cjs +31 -0
  169. package/dist/interfaces/index.cjs.map +1 -0
  170. package/dist/interfaces/index.d.cts +15 -0
  171. package/dist/interfaces/index.d.cts.map +1 -0
  172. package/dist/interfaces/index.d.mts +15 -0
  173. package/dist/interfaces/index.d.mts.map +1 -0
  174. package/dist/interfaces/index.mjs +15 -0
  175. package/dist/interfaces/index.mjs.map +1 -0
  176. package/dist/interfaces/slp.cjs +7 -0
  177. package/dist/interfaces/slp.cjs.map +1 -0
  178. package/dist/interfaces/slp.d.cts +179 -0
  179. package/dist/interfaces/slp.d.cts.map +1 -0
  180. package/dist/interfaces/slp.d.mts +179 -0
  181. package/dist/interfaces/slp.d.mts.map +1 -0
  182. package/dist/interfaces/slp.mjs +6 -0
  183. package/dist/interfaces/slp.mjs.map +1 -0
  184. package/dist/interfaces/usdz.cjs +7 -0
  185. package/dist/interfaces/usdz.cjs.map +1 -0
  186. package/dist/interfaces/usdz.d.cts +104 -0
  187. package/dist/interfaces/usdz.d.cts.map +1 -0
  188. package/dist/interfaces/usdz.d.mts +104 -0
  189. package/dist/interfaces/usdz.d.mts.map +1 -0
  190. package/dist/interfaces/usdz.mjs +6 -0
  191. package/dist/interfaces/usdz.mjs.map +1 -0
  192. package/dist/interfaces/zbtcvc.cjs +7 -0
  193. package/dist/interfaces/zbtcvc.cjs.map +1 -0
  194. package/dist/interfaces/zbtcvc.d.cts +64 -0
  195. package/dist/interfaces/zbtcvc.d.cts.map +1 -0
  196. package/dist/interfaces/zbtcvc.d.mts +64 -0
  197. package/dist/interfaces/zbtcvc.d.mts.map +1 -0
  198. package/dist/interfaces/zbtcvc.mjs +6 -0
  199. package/dist/interfaces/zbtcvc.mjs.map +1 -0
  200. package/dist/interfaces/zlp.cjs +7 -0
  201. package/dist/interfaces/zlp.cjs.map +1 -0
  202. package/dist/interfaces/zlp.d.cts +114 -0
  203. package/dist/interfaces/zlp.d.cts.map +1 -0
  204. package/dist/interfaces/zlp.d.mts +114 -0
  205. package/dist/interfaces/zlp.d.mts.map +1 -0
  206. package/dist/interfaces/zlp.mjs +6 -0
  207. package/dist/interfaces/zlp.mjs.map +1 -0
  208. package/dist/oracle.cjs +118 -0
  209. package/dist/oracle.cjs.map +1 -0
  210. package/dist/oracle.d.cts +25 -0
  211. package/dist/oracle.d.cts.map +1 -0
  212. package/dist/oracle.d.mts +25 -0
  213. package/dist/oracle.d.mts.map +1 -0
  214. package/dist/oracle.mjs +114 -0
  215. package/dist/oracle.mjs.map +1 -0
  216. package/dist/utils.cjs +129 -0
  217. package/dist/utils.cjs.map +1 -0
  218. package/dist/utils.d.cts +44 -0
  219. package/dist/utils.d.cts.map +1 -0
  220. package/dist/utils.d.mts +44 -0
  221. package/dist/utils.d.mts.map +1 -0
  222. package/dist/utils.mjs +115 -0
  223. package/dist/utils.mjs.map +1 -0
  224. package/docs/SUMMARY.md +10 -0
  225. package/docs/api-reference.md +32 -0
  226. package/docs/architecture.md +14 -0
  227. package/docs/common-operations.md +52 -0
  228. package/docs/error-handling.md +17 -0
  229. package/docs/getting-started.md +60 -0
  230. package/docs/introduction.md +15 -0
  231. package/docs/lp-specific-features.md +96 -0
  232. package/docs/type-safety.md +29 -0
  233. package/eslint.config.mjs +18 -0
  234. package/package.json +42 -0
  235. package/src/abstract/BaseAPI.ts +575 -0
  236. package/src/abstract/BaseDataAPI.ts +207 -0
  237. package/src/abstract/index.ts +7 -0
  238. package/src/api.ts +1100 -0
  239. package/src/bcs.ts +45 -0
  240. package/src/consts/deployments-shared-mainnet.json +50 -0
  241. package/src/consts/deployments-shared-testnet.json +45 -0
  242. package/src/consts/deployments-slp-mainnet.json +600 -0
  243. package/src/consts/deployments-slp-testnet.json +87 -0
  244. package/src/consts/deployments-usdz-mainnet.json +494 -0
  245. package/src/consts/deployments-usdz-testnet.json +98 -0
  246. package/src/consts/deployments-zbtcvc-mainnet.json +180 -0
  247. package/src/consts/deployments-zlp-mainnet.json +791 -0
  248. package/src/consts/deployments-zlp-testnet.json +76 -0
  249. package/src/consts/index.ts +345 -0
  250. package/src/consts/price_id_to_object_id.mainnet.json +56 -0
  251. package/src/consts/price_id_to_object_id.testnet.json +17 -0
  252. package/src/data.ts +1279 -0
  253. package/src/factory/SDKFactory.ts +340 -0
  254. package/src/implementations/SLPAPI.ts +2722 -0
  255. package/src/implementations/SLPDataAPI.ts +1839 -0
  256. package/src/implementations/USDZAPI.ts +2488 -0
  257. package/src/implementations/USDZDataAPI.ts +1548 -0
  258. package/src/implementations/ZBTCVCAPI.ts +1337 -0
  259. package/src/implementations/ZBTCVCDataAPI.ts +993 -0
  260. package/src/implementations/ZLPAPI.ts +2888 -0
  261. package/src/implementations/ZLPDataAPI.ts +1603 -0
  262. package/src/implementations/index.ts +16 -0
  263. package/src/index.ts +58 -0
  264. package/src/interfaces/base.ts +838 -0
  265. package/src/interfaces/index.ts +50 -0
  266. package/src/interfaces/slp.ts +268 -0
  267. package/src/interfaces/usdz.ts +181 -0
  268. package/src/interfaces/zbtcvc.ts +116 -0
  269. package/src/interfaces/zlp.ts +244 -0
  270. package/src/oracle.ts +153 -0
  271. package/src/utils.ts +168 -0
  272. package/tests/api.test.ts +219 -0
  273. package/tests/data.test.ts +156 -0
  274. package/tests/oracle.test.ts +33 -0
  275. package/tsconfig.json +22 -0
@@ -0,0 +1,1267 @@
1
+ "use strict";
2
+ /* eslint-disable no-await-in-loop */
3
+ /**
4
+ * ZLP DataAPI implementation
5
+ * Implements ZLP-specific data access methods
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.ZLPDataAPI = void 0;
9
+ const utils_1 = require("@mysten/sui/utils");
10
+ const abstract_1 = require("../abstract/index.cjs");
11
+ const consts_1 = require("../consts/index.cjs");
12
+ const utils_2 = require("../utils.cjs");
13
+ class ZLPDataAPI extends abstract_1.BaseDataAPI {
14
+ constructor(network, provider, apiEndpoint, connectionURL) {
15
+ super(network, provider, apiEndpoint, connectionURL, consts_1.LPToken.ZLP);
16
+ }
17
+ async getStaked(owner) {
18
+ let rawCredentialsData = [];
19
+ let queryNextPage = true;
20
+ let queryCursor;
21
+ const limit = 50;
22
+ while (queryNextPage) {
23
+ const { data, hasNextPage, nextCursor } = await this.provider.getOwnedObjects({
24
+ owner,
25
+ filter: {
26
+ MoveModule: {
27
+ package: this.sharedConfig.zoStaking.package,
28
+ module: 'pool',
29
+ },
30
+ },
31
+ options: {
32
+ showType: true,
33
+ showContent: true,
34
+ },
35
+ cursor: queryCursor,
36
+ limit,
37
+ });
38
+ queryNextPage = hasNextPage;
39
+ queryCursor = nextCursor;
40
+ if (!data)
41
+ break;
42
+ rawCredentialsData = [...rawCredentialsData, ...data];
43
+ }
44
+ const pool = await this.getStakePool();
45
+ // TODO: Update reward coin type once available
46
+ const credentials = rawCredentialsData
47
+ .filter((item) => item.data.type
48
+ === `${this.sharedConfig.zoStaking.package}::pool::Credential<${this.consts.zoCore.package}::zlp::ZLP, 0x2::sui::SUI>`)
49
+ .map((item) => ZLPDataAPI.parseCredential(item, pool));
50
+ return {
51
+ credentials,
52
+ amount: credentials.reduce((acc, cur) => acc + cur.amount, BigInt(0)),
53
+ claimable: credentials.reduce((acc, cur) => acc + cur.claimable, BigInt(0)),
54
+ };
55
+ }
56
+ async getStakePool() {
57
+ const poolId = this.sharedConfig.zoStaking.pools.zlp;
58
+ const raw = await this.provider.getObject({
59
+ id: poolId,
60
+ options: {
61
+ showContent: true,
62
+ },
63
+ });
64
+ return ZLPDataAPI.parseStakePool(raw);
65
+ }
66
+ /**
67
+ * Creates vaults valuation for ZLP
68
+ */
69
+ valuateVaults(tx) {
70
+ const vaultsValuation = tx.moveCall({
71
+ target: `${this.consts.zoCore.upgradedPackage}::market::create_vaults_valuation`,
72
+ typeArguments: [`${this.consts.zoCore.package}::zlp::ZLP`],
73
+ arguments: [
74
+ tx.object(utils_1.SUI_CLOCK_OBJECT_ID),
75
+ tx.object(this.consts.zoCore.market),
76
+ ],
77
+ });
78
+ for (const key of Object.keys(this.consts.zoCore.vaults)) {
79
+ const vault = this.consts.zoCore.vaults[key];
80
+ tx.moveCall({
81
+ target: `${this.consts.zoCore.upgradedPackage}::market::valuate_vault`,
82
+ typeArguments: [
83
+ `${this.consts.zoCore.package}::zlp::ZLP`,
84
+ this.consts.coins[key].module,
85
+ ],
86
+ arguments: [
87
+ tx.object(this.consts.zoCore.market),
88
+ tx.object(vault.reservingFeeModel),
89
+ tx.object(this.consts.pythFeeder.feeder[key]),
90
+ vaultsValuation,
91
+ ],
92
+ });
93
+ }
94
+ return vaultsValuation;
95
+ }
96
+ /**
97
+ * Creates symbols valuation for ZLP
98
+ */
99
+ valuateSymbols(tx) {
100
+ const symbolsValuation = tx.moveCall({
101
+ target: `${this.consts.zoCore.upgradedPackage}::market::create_symbols_valuation`,
102
+ typeArguments: [`${this.consts.zoCore.package}::zlp::ZLP`],
103
+ arguments: [
104
+ tx.object(utils_1.SUI_CLOCK_OBJECT_ID),
105
+ tx.object(this.consts.zoCore.market),
106
+ ],
107
+ });
108
+ for (const key of Object.keys(this.consts.zoCore.symbols)) {
109
+ const [direction, token] = (0, utils_2.parseSymbolKey)(key);
110
+ const symbol = this.consts.zoCore.symbols[key];
111
+ tx.moveCall({
112
+ target: `${this.consts.zoCore.upgradedPackage}::market::valuate_symbol`,
113
+ typeArguments: [
114
+ `${this.consts.zoCore.package}::zlp::ZLP`,
115
+ this.consts.coins[token].module,
116
+ `${this.consts.zoCore.package}::market::${direction.toUpperCase()}`,
117
+ ],
118
+ arguments: [
119
+ tx.object(this.consts.zoCore.market),
120
+ tx.object(symbol.fundingFeeModel),
121
+ tx.object(this.consts.pythFeeder.feeder[token]),
122
+ symbolsValuation,
123
+ ],
124
+ });
125
+ }
126
+ return symbolsValuation;
127
+ }
128
+ /**
129
+ * Creates both vaults and symbols valuation
130
+ */
131
+ valuate(tx) {
132
+ const vaultsValuation = this.valuateVaults(tx);
133
+ const symbolsValuation = this.valuateSymbols(tx);
134
+ return { vaultsValuation, symbolsValuation };
135
+ }
136
+ /**
137
+ * Valuates the ZLP market
138
+ */
139
+ async valuateMarket() {
140
+ const marketInfo = await this.getMarketInfo();
141
+ let zlpPrice = 0;
142
+ let value = 0;
143
+ const vaultPromises = Object.keys(this.consts.zoCore.vaults).map(async (vault) => {
144
+ const vaultInfo = await this.getVaultInfo(vault);
145
+ const reservingFeeDelta = ZLPDataAPI.calculateVaultReservingFee(vaultInfo, vaultInfo.reservingFeeModel, Date.now() / 1000);
146
+ return (reservingFeeDelta + vaultInfo.liquidity + vaultInfo.reservedAmount) * (await this.getOraclePrice(vault)).getPriceUnchecked().getPriceAsNumberUnchecked() / (10 ** this.consts.coins[vault].decimals);
147
+ });
148
+ const symbolPromises = Object.keys(this.consts.zoCore.symbols).map(async (symbol) => {
149
+ const [direction, tokenId] = (0, utils_2.parseSymbolKey)(symbol);
150
+ const symbolInfo = await this.getSymbolInfo(tokenId, direction === 'long');
151
+ const price = (await this.getOraclePrice(tokenId)).getPriceUnchecked().getPriceAsNumberUnchecked();
152
+ const deltaSize = ZLPDataAPI.calcDeltaSize(symbolInfo, price);
153
+ const oiState = await this.getSymbolOiFundingState(tokenId);
154
+ const pairedInfo = await this.getSymbolInfo(tokenId, direction !== 'long');
155
+ const fundingFeeDelta = ZLPDataAPI.calculateSymbolFundingFee(symbolInfo, symbolInfo.fundingFeeModel, price, marketInfo.lpSupplyWithDecimals, Date.now() / 1000, oiState && oiState.enabled ? oiState.model : undefined, pairedInfo.openingSize);
156
+ return fundingFeeDelta + deltaSize;
157
+ });
158
+ const [vaultValues, symbolValues] = await Promise.all([Promise.all(vaultPromises), Promise.all(symbolPromises)]);
159
+ value = vaultValues.reduce((acc, curr) => acc + curr, 0);
160
+ value += symbolValues.reduce((acc, curr) => acc + curr, 0);
161
+ zlpPrice = value / marketInfo.lpSupplyWithDecimals;
162
+ return {
163
+ marketCap: value,
164
+ price: zlpPrice,
165
+ supply: marketInfo.lpSupplyWithDecimals,
166
+ apr: Number(marketInfo.apr),
167
+ };
168
+ }
169
+ /**
170
+ * Gets ZLP market information
171
+ */
172
+ async getMarketInfo() {
173
+ this.validateCache();
174
+ if (this.marketInfoCache) {
175
+ return this.marketInfoCache;
176
+ }
177
+ const rawData = await this.provider.getObject({
178
+ id: this.consts.zoCore.market,
179
+ options: {
180
+ showContent: true,
181
+ },
182
+ });
183
+ return ZLPDataAPI.parseMarketInfo(rawData);
184
+ }
185
+ /**
186
+ * Gets ZLP vault information
187
+ */
188
+ async getVaultInfo(vaultToken) {
189
+ this.validateCache();
190
+ if (this.vaultInfoCache[vaultToken]) {
191
+ return this.vaultInfoCache[vaultToken];
192
+ }
193
+ const rawData = await this.provider.getDynamicFieldObject({
194
+ parentId: this.consts.zoCore.vaultsParent,
195
+ name: {
196
+ type: `${this.consts.zoCore.package}::market::VaultName<${this.consts.coins[vaultToken].module}>`,
197
+ value: { dummy_field: false },
198
+ },
199
+ });
200
+ return await this.parseVaultInfo(rawData);
201
+ }
202
+ /**
203
+ * Gets ZLP symbol information
204
+ */
205
+ async getSymbolInfo(indexToken, long) {
206
+ this.validateCache();
207
+ const symbol = (0, utils_2.joinSymbol)(long ? 'long' : 'short', indexToken);
208
+ if (this.symbolInfoCache[symbol]) {
209
+ return this.symbolInfoCache[symbol];
210
+ }
211
+ const rawData = await this.provider.getDynamicFieldObject({
212
+ parentId: this.consts.zoCore.symbolsParent,
213
+ name: {
214
+ type: `${this.consts.zoCore.package}::market::SymbolName<${this.consts.coins[indexToken].module}, ${this.consts.zoCore.package}::market::${long ? 'LONG' : 'SHORT'}>`,
215
+ value: { dummy_field: false },
216
+ },
217
+ });
218
+ return await this.parseSymbolInfo(rawData, long);
219
+ }
220
+ async getPositionConfig(indexToken, long) {
221
+ this.validateCache();
222
+ const symbol = (0, utils_2.joinSymbol)(long ? 'long' : 'short', indexToken);
223
+ if (this.positionConfigCache[symbol]) {
224
+ return this.positionConfigCache[symbol];
225
+ }
226
+ const rawData = await this.provider.getObject({
227
+ id: this.consts.zoCore.symbols[symbol].positionConfig,
228
+ options: {
229
+ showContent: true,
230
+ },
231
+ });
232
+ return ZLPDataAPI.parsePositionConfig(rawData);
233
+ }
234
+ /**
235
+ * Gets ZLP symbol configuration
236
+ */
237
+ async getSymbolConfig(indexToken, long) {
238
+ this.validateCache();
239
+ try {
240
+ const rawData = await this.provider.getDynamicFieldObject({
241
+ parentId: this.consts.zoCore.market,
242
+ name: {
243
+ type: `${this.consts.zoCore.package}::market::SymbolName<${this.consts.coins[indexToken].module}, ${this.consts.zoCore.package}::market::${long ? 'LONG' : 'SHORT'}>`,
244
+ value: { dummy_field: false },
245
+ },
246
+ });
247
+ return ZLPDataAPI.parseSymbolConfig(rawData);
248
+ }
249
+ catch (e) {
250
+ // If the dynamic field doesn't exist, return null
251
+ console.error('Error Fetching Symbol Config:', e);
252
+ return null;
253
+ }
254
+ }
255
+ /**
256
+ * Gets ZLP symbol OI funding model
257
+ */
258
+ async getSymbolOiFundingState(indexToken) {
259
+ this.validateCache();
260
+ try {
261
+ const rawData = await this.provider.getDynamicFieldObject({
262
+ parentId: this.consts.zoCore.market,
263
+ name: {
264
+ type: `0x6e77b426313c6a8b4a47081b141cdea1f8fa7e58e892af05f516849c05a96811::funding::FundingStateKey<${this.consts.coins[indexToken].module}>`,
265
+ value: { dummy_field: false },
266
+ },
267
+ });
268
+ return ZLPDataAPI.parseOiFundingState(rawData);
269
+ }
270
+ catch (e) {
271
+ // If the dynamic field doesn't exist, return null
272
+ console.error('Error Fetching Symbol OI Funding State:', e);
273
+ return null;
274
+ }
275
+ }
276
+ /**
277
+ * Gets price impact configuration for a symbol.
278
+ * Price impact config applies to both long and short directions for the same index token.
279
+ */
280
+ async getPriceImpactConfig(indexToken) {
281
+ this.validateCache();
282
+ try {
283
+ const rawData = await this.provider.getDynamicFieldObject({
284
+ parentId: this.consts.zoCore.market,
285
+ name: {
286
+ type: `0x05248f4f39558466b1f7e55bd09072bb8a6c31b6514d42ce0b50b7f36356d204::price_impact::PriceImpactConfigKey<${this.consts.coins[indexToken].module}>`,
287
+ value: { dummy_field: false },
288
+ },
289
+ });
290
+ return ZLPDataAPI.parsePriceImpactConfig(rawData);
291
+ }
292
+ catch (e) {
293
+ // If the dynamic field doesn't exist, return null (price impact not configured for this symbol)
294
+ console.error('Error Fetching Price Impact Config:', e);
295
+ return null;
296
+ }
297
+ }
298
+ async getPositionCapInfoList(owner) {
299
+ const positionCapInfoList = [];
300
+ let cursor;
301
+ let hasNextPage = true;
302
+ while (hasNextPage) {
303
+ const positionCaps = await this.provider.getOwnedObjects({
304
+ owner,
305
+ filter: {
306
+ MoveModule: {
307
+ package: this.consts.zoCore.package,
308
+ module: 'market',
309
+ },
310
+ },
311
+ options: {
312
+ showType: true,
313
+ },
314
+ cursor,
315
+ });
316
+ for (const positionCap of positionCaps.data) {
317
+ if (positionCap.data?.type?.includes('PositionCap')) {
318
+ positionCapInfoList.push({
319
+ positionCapId: positionCap.data.objectId,
320
+ symbol0: positionCap.data.type.split('<')[1].split(',')[0].trim(),
321
+ symbol1: positionCap.data.type.split('<')[1].split(',')[1].split(',')[0].trim(),
322
+ long: positionCap.data.type.includes('LONG'),
323
+ });
324
+ }
325
+ }
326
+ // If we don't want to fetch all pages or there are no more pages, break the loop
327
+ hasNextPage = positionCaps.hasNextPage;
328
+ cursor = positionCaps.nextCursor;
329
+ }
330
+ return positionCapInfoList;
331
+ }
332
+ async getPositionInfoList(positionCapInfoList, owner, batchSize = 10) {
333
+ const positionInfoList = [];
334
+ // Process in batches of 10
335
+ for (let i = 0; i < positionCapInfoList.length; i += batchSize) {
336
+ const batch = positionCapInfoList.slice(i, i + batchSize);
337
+ await Promise.all(batch.map(async (positionCapInfo) => {
338
+ try {
339
+ const positionRaw = await this.provider.getDynamicFieldObject({
340
+ parentId: this.consts.zoCore.positionsParent,
341
+ name: {
342
+ type: `${this.consts.zoCore.package}::market::PositionName<${positionCapInfo.symbol0}, ${positionCapInfo.symbol1}, ${this.consts.zoCore.package}::market::${positionCapInfo.long ? 'LONG' : 'SHORT'}>`,
343
+ value: {
344
+ owner,
345
+ id: positionCapInfo.positionCapId,
346
+ },
347
+ },
348
+ });
349
+ positionInfoList.push(await this.parsePositionInfo(positionRaw, positionCapInfo.positionCapId));
350
+ }
351
+ catch (error) {
352
+ // Position might have been deleted after force settlement
353
+ console.warn(`Failed to parse position info for position cap ID ${positionCapInfo.positionCapId}: ${error}`);
354
+ // Continue with next position without adding this one to the list
355
+ }
356
+ }));
357
+ }
358
+ return positionInfoList.sort((a, b) => a.openTimestamp > b.openTimestamp ? 1 : -1);
359
+ }
360
+ async getOpenPositions(batchSize = 50, symbol = 'sui') {
361
+ let positionDynamicFields = [];
362
+ let _continue = true;
363
+ let cursor;
364
+ while (_continue) {
365
+ // data here will be a list of dynamic fields containing name and value
366
+ const { data, nextCursor, hasNextPage } = await this.provider.getDynamicFields({
367
+ parentId: this.consts.zoCore.positionsParent,
368
+ cursor,
369
+ });
370
+ positionDynamicFields = positionDynamicFields.concat(data);
371
+ _continue = hasNextPage;
372
+ cursor = nextCursor;
373
+ }
374
+ // Filter by symbol if provided
375
+ if (!(symbol && this.consts.coins[symbol])) {
376
+ return [];
377
+ }
378
+ const coinModule = symbol === 'sui' ? '0x2::sui::SUI' : this.consts.coins[symbol].module;
379
+ positionDynamicFields = positionDynamicFields.filter((field) => {
380
+ // Extract the second coin module from PositionName<coin1, coin2, direction>
381
+ const typeStr = field.name?.type;
382
+ if (!typeStr)
383
+ return false;
384
+ const match = typeStr.match(/PositionName<([^,]+),([^,]+),([^>]+)>/);
385
+ if (!match)
386
+ return false;
387
+ const secondCoin = match[2].trim();
388
+ return secondCoin === coinModule;
389
+ });
390
+ // then we query by dynamic field names and order by time
391
+ const positionInfoList = [];
392
+ // Process in batches of 50 (or specified batchSize)
393
+ for (let i = 0; i < positionDynamicFields.length; i += batchSize) {
394
+ const batch = positionDynamicFields.slice(i, i + batchSize);
395
+ await Promise.all(batch.map(async (positionDynamicField) => {
396
+ const positionRaw = await this.provider.getDynamicFieldObject({
397
+ parentId: this.consts.zoCore.positionsParent,
398
+ name: positionDynamicField.name,
399
+ });
400
+ if (positionRaw?.data?.content) {
401
+ // @ts-expect-error: content fields type is not properly defined
402
+ if (positionRaw?.data?.content?.fields?.value?.fields?.closed) {
403
+ // skip closed positions
404
+ return;
405
+ }
406
+ const positionInfo = await this.parsePositionInfo(positionRaw, positionDynamicField.objectId);
407
+ if (positionInfo) {
408
+ positionInfoList.push(positionInfo);
409
+ }
410
+ }
411
+ }));
412
+ }
413
+ return positionInfoList
414
+ .filter(positionInfo => !positionInfo.closed)
415
+ .sort((a, b) => (a.openTimestamp > b.openTimestamp ? 1 : -1));
416
+ }
417
+ async getOrderCapInfoList(owner) {
418
+ const orderCapInfoList = [];
419
+ let cursor;
420
+ let hasNextPage = true;
421
+ while (hasNextPage) {
422
+ const orderCaps = await this.provider.getOwnedObjects({
423
+ owner,
424
+ filter: {
425
+ MoveModule: {
426
+ package: this.consts.zoCore.package,
427
+ module: 'market',
428
+ },
429
+ },
430
+ options: {
431
+ showType: true,
432
+ showContent: true,
433
+ },
434
+ cursor,
435
+ });
436
+ for (const orderCap of orderCaps.data) {
437
+ if (orderCap.data?.type?.includes('OrderCap')) {
438
+ orderCapInfoList.push({
439
+ orderCapId: orderCap.data.objectId,
440
+ symbol0: orderCap.data.type.split('<')[1].split(',')[0].trim(),
441
+ symbol1: orderCap.data.type.split('<')[1].split(',')[1].split(',')[0].trim(),
442
+ long: orderCap.data.type.includes('LONG'),
443
+ positionId: orderCap.data.content?.fields?.position_id,
444
+ });
445
+ }
446
+ }
447
+ hasNextPage = orderCaps.hasNextPage;
448
+ cursor = orderCaps.nextCursor;
449
+ }
450
+ return orderCapInfoList;
451
+ }
452
+ async getOrderInfoList(orderCapInfoList, owner, batchSize = 10) {
453
+ const orderInfoList = [];
454
+ // Process in batches of 10
455
+ for (let i = 0; i < orderCapInfoList.length; i += batchSize) {
456
+ const batch = orderCapInfoList.slice(i, i + batchSize);
457
+ await Promise.all(batch.map(async (orderCapInfo) => {
458
+ try {
459
+ const orderRaw = await this.provider.getDynamicFieldObject({
460
+ parentId: this.consts.zoCore.ordersParent,
461
+ name: {
462
+ // 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
463
+ type: `${this.consts.zoCore.package}::market::OrderName<${orderCapInfo.symbol0}, ${orderCapInfo.symbol1}, ${this.consts.zoCore.package}::market::${orderCapInfo.long ? 'LONG' : 'SHORT'}, ${orderCapInfo.symbol0}>`,
464
+ value: {
465
+ owner,
466
+ id: orderCapInfo.orderCapId,
467
+ position_id: {
468
+ vec: orderCapInfo.positionId ? [orderCapInfo.positionId] : [],
469
+ },
470
+ },
471
+ },
472
+ });
473
+ orderInfoList.push(this.parseOrderInfo(orderRaw, orderCapInfo.orderCapId));
474
+ }
475
+ catch (error) {
476
+ // Order might have been deleted
477
+ console.warn(`Failed to parse order info for order cap ID ${orderCapInfo.orderCapId}: ${error}`);
478
+ // Continue with next order without adding this one to the list
479
+ }
480
+ }));
481
+ }
482
+ return orderInfoList.sort((a, b) => a.createdAt > b.createdAt ? 1 : -1);
483
+ }
484
+ async hasReferral(referree) {
485
+ const raw = await this.getReferralData(referree);
486
+ return !raw.error;
487
+ }
488
+ async getReferralData(referree) {
489
+ const raw = await this.provider.getDynamicFieldObject({
490
+ parentId: this.consts.zoCore.referralsParent,
491
+ name: {
492
+ type: 'address',
493
+ value: referree,
494
+ },
495
+ });
496
+ return raw;
497
+ }
498
+ /**
499
+ * Gets rebase fee model for ZLP
500
+ */
501
+ async getRebaseFeeModel() {
502
+ this.validateCache();
503
+ if (this.rebaseFeeModelCache) {
504
+ return this.rebaseFeeModelCache;
505
+ }
506
+ const rawData = await this.provider.getObject({
507
+ id: this.consts.zoCore.rebaseFeeModel,
508
+ options: {
509
+ showContent: true,
510
+ },
511
+ });
512
+ const model = ZLPDataAPI.parseRebaseFeeModel(rawData);
513
+ const exponent = await ZLPDataAPI.getRebaseFeeExponent(this.provider, this.consts.zoCore.rebaseFeeModel, this.consts.zoCore.upgradedPackage);
514
+ return { ...model, exponent };
515
+ }
516
+ async fundingFeeRate(indexToken, long) {
517
+ const oiState = await this.getSymbolOiFundingState(indexToken);
518
+ if (!oiState || !oiState.enabled) {
519
+ const symbol = await this.getSymbolInfo(indexToken, long);
520
+ if (symbol.lastUpdate <= 0) {
521
+ return 0;
522
+ }
523
+ const price = (await this.getOraclePrice(indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked();
524
+ const lpSupplyAmount = (await this.getMarketInfo()).lpSupplyWithDecimals;
525
+ const model = symbol.fundingFeeModel;
526
+ const elapsed = consts_1.SECONDS_PER_EIGHT_HOUR;
527
+ const deltaSize = ZLPDataAPI.calcDeltaSize(symbol, price);
528
+ const pnlPerLp = (symbol.realisedPnl + symbol.unrealisedFundingFeeValue + deltaSize) / lpSupplyAmount;
529
+ return ZLPDataAPI.calcFundingFeeRate(model, pnlPerLp, elapsed);
530
+ }
531
+ const longSymbol = await this.getSymbolInfo(indexToken, true);
532
+ const shortSymbol = await this.getSymbolInfo(indexToken, false);
533
+ if (longSymbol.lastUpdate <= 0 && shortSymbol.lastUpdate <= 0) {
534
+ return 0;
535
+ }
536
+ const elapsed = consts_1.SECONDS_PER_EIGHT_HOUR;
537
+ const longSize = longSymbol.openingSize;
538
+ const shortSize = shortSymbol.openingSize;
539
+ const deltaRate = ZLPDataAPI.calcOiFundingFeeRate(oiState.model, longSize, shortSize, elapsed);
540
+ return long ? deltaRate : -deltaRate;
541
+ }
542
+ async rebaseFeeRate(collateralToken, increase, amount) {
543
+ let vaultValue = 0;
544
+ if (!increase && amount > 0) {
545
+ amount = -amount;
546
+ }
547
+ const value = amount * (await this.getOraclePrice(collateralToken)).getPriceUnchecked().getPriceAsNumberUnchecked() / (10 ** this.consts.coins[collateralToken].decimals);
548
+ const vaultPromises = Object.keys(this.consts.zoCore.vaults).map(async (vault) => {
549
+ const vaultInfo = await this.getVaultInfo(vault);
550
+ const reservingFeeDelta = ZLPDataAPI.calculateVaultReservingFee(vaultInfo, vaultInfo.reservingFeeModel, Date.now() / 1000);
551
+ const res = (reservingFeeDelta + vaultInfo.liquidity + vaultInfo.reservedAmount) * (await this.getOraclePrice(vault)).getPriceUnchecked().getPriceAsNumberUnchecked() / (10 ** this.consts.coins[vault].decimals);
552
+ if (collateralToken === vault) {
553
+ vaultValue = res;
554
+ }
555
+ return res;
556
+ });
557
+ const vaultValues = await Promise.all(vaultPromises);
558
+ const totalVaultValue = vaultValues.reduce((acc, curr) => acc + curr, 0);
559
+ const targetRatio = Number.parseInt(this.consts.zoCore.vaults[collateralToken].weight, 10) / Object.values(this.consts.zoCore.vaults)
560
+ .map(e => Number.parseInt(e.weight, 10))
561
+ .reduce((acc, curr) => acc + curr, 0);
562
+ return ZLPDataAPI.calcRebaseFeeRate(await this.getRebaseFeeModel(), increase, (vaultValue + value) / (totalVaultValue + value), targetRatio);
563
+ }
564
+ async reservingFeeRate(collateralToken, amount = 0) {
565
+ const vaultInfo = await this.getVaultInfo(collateralToken);
566
+ const vaultSupply = vaultInfo.liquidity + vaultInfo.reservedAmount + vaultInfo.unrealisedReservingFeeAmount + amount;
567
+ const utilization = vaultSupply ? ((vaultInfo.reservedAmount + amount) / vaultSupply) : 0;
568
+ return ZLPDataAPI.calcReservingFeeRate(vaultInfo.reservingFeeModel, utilization, consts_1.SECONDS_PER_EIGHT_HOUR);
569
+ }
570
+ async getHistory(trader, page, limit, orderType, symbol) {
571
+ const params = new URLSearchParams({
572
+ trader,
573
+ page: page.toString(),
574
+ limit: limit.toString(),
575
+ });
576
+ // Add filter parameters if provided
577
+ if (orderType && orderType !== 'all') {
578
+ params.append('orderType', orderType);
579
+ }
580
+ if (symbol && symbol !== 'all') {
581
+ params.append('symbol', symbol);
582
+ }
583
+ params.append('lpType', 'ZLP');
584
+ const url = `${this.apiEndpoint}/traderEvents?${params}`;
585
+ const res = await fetch(url, {
586
+ method: 'GET',
587
+ headers: {
588
+ 'Content-Type': 'application/json',
589
+ },
590
+ });
591
+ const response = await res.json();
592
+ return {
593
+ histories: response.data?.histories || [],
594
+ pagination: response.data?.pagination || {
595
+ total: 0,
596
+ page: 1,
597
+ limit: 20,
598
+ pages: 0,
599
+ },
600
+ };
601
+ }
602
+ // Private helper methods
603
+ static calcFundingFeeRate(model, pnlPerRate, elapsed) {
604
+ const dailyRate = Math.min(model.multiplier * Math.abs(pnlPerRate), model.max);
605
+ const secondsRate = dailyRate * elapsed / consts_1.SECONDS_PER_EIGHT_HOUR;
606
+ return pnlPerRate >= 0 ? -secondsRate : secondsRate;
607
+ }
608
+ static calcOiFundingFeeRate(model, longSize, shortSize, elapsed) {
609
+ const imbalance = Math.abs(longSize - shortSize);
610
+ // multiplier = 0.1%, exponent = 1
611
+ const dailyRate = Math.min(model.multiplier * (imbalance ** model.exponent) / (longSize + shortSize > 0 ? longSize + shortSize : 1), model.max);
612
+ const secondsRate = dailyRate * elapsed / consts_1.SECONDS_PER_EIGHT_HOUR;
613
+ return longSize >= shortSize ? secondsRate : -secondsRate;
614
+ }
615
+ static calcAccFundingFeeRate(symbol, model, price, lpSupplyAmount, timestamp, oiModel, pairedOpeningSize) {
616
+ if (symbol.lastUpdate > 0) {
617
+ const elapsed = timestamp - symbol.lastUpdate;
618
+ if (elapsed > 0) {
619
+ // Prefer OI-based delta when model and paired side are available
620
+ if (oiModel && typeof pairedOpeningSize === 'number') {
621
+ const longSize = symbol.long ? symbol.openingSize : pairedOpeningSize;
622
+ const shortSize = symbol.long ? pairedOpeningSize : symbol.openingSize;
623
+ const deltaRate = ZLPDataAPI.calcOiFundingFeeRate(oiModel, longSize, shortSize, elapsed);
624
+ const appliedRate = symbol.long ? deltaRate : -deltaRate;
625
+ return symbol.accFundingRate + appliedRate;
626
+ }
627
+ // Fallback to PnL-based funding delta
628
+ const deltaSize = ZLPDataAPI.calcDeltaSize(symbol, price);
629
+ const pnlPerLp = (symbol.realisedPnl + symbol.unrealisedFundingFeeValue + deltaSize) / lpSupplyAmount;
630
+ return symbol.accFundingRate + ZLPDataAPI.calcFundingFeeRate(model, pnlPerLp, elapsed);
631
+ }
632
+ }
633
+ return symbol.accFundingRate;
634
+ }
635
+ static calculateSymbolFundingFee(symbol, model, price, lpSupplyAmount, timestamp, oiModel, pairedOpeningSize) {
636
+ const accFundingRate = ZLPDataAPI.calcAccFundingFeeRate(symbol, model, price, lpSupplyAmount, timestamp, oiModel, pairedOpeningSize);
637
+ return symbol.unrealisedFundingFeeValue + (accFundingRate - symbol.accFundingRate) * symbol.openingSize;
638
+ }
639
+ static calculatePositionReserveFee(position, vault, model, timestamp) {
640
+ const accReservingRate = ZLPDataAPI.calcAccReservingFeeRate(vault, model, timestamp);
641
+ return position.reservingFeeAmount + (accReservingRate - position.lastReservingRate) * position.reservedAmount;
642
+ }
643
+ static calcAccReservingFeeRate(vault, model, timestamp) {
644
+ if (vault.lastUpdate > 0) {
645
+ const elapsed = timestamp - vault.lastUpdate;
646
+ if (elapsed > 0) {
647
+ const utilization = ZLPDataAPI.vaultUtilization(vault);
648
+ return vault.accReservingRate + ZLPDataAPI.calcReservingFeeRate(model, utilization, elapsed);
649
+ }
650
+ }
651
+ return vault.accReservingRate;
652
+ }
653
+ static calcRebaseFeeRate(model, increase, ratio, targetRatio) {
654
+ if ((increase && ratio <= targetRatio) || (!increase && ratio >= targetRatio)) {
655
+ return model.base;
656
+ }
657
+ const exponent = model.exponent ?? 1;
658
+ return model.base + model.multiplier * (Math.abs(ratio - targetRatio) ** exponent);
659
+ }
660
+ static vaultUtilization(vault) {
661
+ const supplyAmount = vault.liquidity + vault.reservedAmount + vault.unrealisedReservingFeeAmount;
662
+ if (supplyAmount === 0) {
663
+ return 0;
664
+ }
665
+ return vault.reservedAmount / supplyAmount;
666
+ }
667
+ static calcReservingFeeRate(model, utilization, elapsed) {
668
+ return model.multiplier * utilization * elapsed / consts_1.SECONDS_PER_EIGHT_HOUR;
669
+ }
670
+ static calcDeltaSize(symbol, price) {
671
+ const latestSize = symbol.openingAmount / symbol.priceConfig.precision * price;
672
+ return symbol.long ? symbol.openingSize - latestSize : latestSize - symbol.openingSize;
673
+ }
674
+ static parseMarketInfo(raw) {
675
+ const content = raw.data.content.fields;
676
+ return {
677
+ lpSupply: content.lp_supply.fields.value,
678
+ positionId: content.positions.fields.id.id,
679
+ vaultId: content.vaults.fields.id.id,
680
+ symbolId: content.symbols.fields.id.id,
681
+ referralId: content.referrals.fields.id.id,
682
+ orderId: content.orders.fields.id.id,
683
+ rebaseFeeModel: content.rebase_fee_model,
684
+ lpSupplyWithDecimals: content.lp_supply.fields.value / (10 ** consts_1.ZLP_TOKEN_DECIMALS),
685
+ };
686
+ }
687
+ async parseVaultInfo(raw) {
688
+ const vaultFields = raw.data.content.fields.value.fields;
689
+ const reservingFeeModelAddr = vaultFields.reserving_fee_model;
690
+ const reservingFeeModelRaw = await this.provider.getObject({
691
+ id: reservingFeeModelAddr,
692
+ options: {
693
+ showContent: true,
694
+ },
695
+ });
696
+ const reservingFeeModel = ZLPDataAPI.parseReservingFeeModel(reservingFeeModelRaw);
697
+ return {
698
+ liquidity: (0, utils_2.parseValue)(vaultFields.liquidity),
699
+ reservedAmount: (0, utils_2.parseValue)(vaultFields.reserved_amount),
700
+ unrealisedReservingFeeAmount: (0, utils_2.parseValue)(vaultFields.unrealised_reserving_fee_amount),
701
+ accReservingRate: (0, utils_2.parseValue)(vaultFields.acc_reserving_rate),
702
+ enabled: vaultFields.enabled,
703
+ weight: (0, utils_2.parseValue)(vaultFields.weight),
704
+ lastUpdate: (0, utils_2.parseValue)(vaultFields.last_update),
705
+ reservingFeeModel,
706
+ priceConfig: {
707
+ maxInterval: (0, utils_2.parseValue)(vaultFields.price_config.fields.max_interval),
708
+ maxConfidence: (0, utils_2.parseValue)(vaultFields.price_config.fields.max_confidence),
709
+ precision: (0, utils_2.parseValue)(vaultFields.price_config.fields.precision),
710
+ feeder: vaultFields.price_config.fields.feeder,
711
+ },
712
+ };
713
+ }
714
+ async parseSymbolInfo(raw, long) {
715
+ const { objectId } = raw.data;
716
+ const { fields } = raw.data.content.fields.value;
717
+ const fundingFeeModelAddr = fields.funding_fee_model;
718
+ const fundingFeeModelRaw = await this.provider.getObject({
719
+ id: fundingFeeModelAddr,
720
+ options: {
721
+ showContent: true,
722
+ },
723
+ });
724
+ const fundingFeeModel = ZLPDataAPI.parseFundingFeeModel(fundingFeeModelRaw);
725
+ return {
726
+ objectId,
727
+ openingSize: (0, utils_2.parseValue)(fields.opening_size),
728
+ openingAmount: (0, utils_2.parseValue)(fields.opening_amount),
729
+ accFundingRate: (0, utils_2.parseValue)(fields.acc_funding_rate),
730
+ realisedPnl: (0, utils_2.parseValue)(fields.realised_pnl),
731
+ unrealisedFundingFeeValue: (0, utils_2.parseValue)(fields.unrealised_funding_fee_value),
732
+ openEnabled: fields.open_enabled,
733
+ liquidateEnabled: fields.liquidate_enabled,
734
+ decreaseEnabled: fields.decrease_enabled,
735
+ lastUpdate: (0, utils_2.parseValue)(fields.last_update),
736
+ fundingFeeModel,
737
+ long,
738
+ priceConfig: {
739
+ maxInterval: (0, utils_2.parseValue)(fields.price_config.fields.max_interval),
740
+ maxConfidence: (0, utils_2.parseValue)(fields.price_config.fields.max_confidence),
741
+ precision: (0, utils_2.parseValue)(fields.price_config.fields.precision),
742
+ feeder: fields.price_config.fields.feeder,
743
+ },
744
+ };
745
+ }
746
+ static parsePositionConfig(raw) {
747
+ const positionConfigFields = raw.data.content.fields.inner.fields;
748
+ return {
749
+ decreaseFeeBps: (0, utils_2.parseValue)(positionConfigFields.decrease_fee_bps),
750
+ liquidationBonus: (0, utils_2.parseValue)(positionConfigFields.liquidation_bonus),
751
+ liquidationThreshold: (0, utils_2.parseValue)(positionConfigFields.liquidation_threshold),
752
+ maxLeverage: (0, utils_2.parseValue)(positionConfigFields.max_leverage),
753
+ minHoldingDuration: (0, utils_2.parseValue)(positionConfigFields.min_holding_duration),
754
+ openFeeBps: (0, utils_2.parseValue)(positionConfigFields.open_fee_bps),
755
+ maxReservedMultiplier: (0, utils_2.parseValue)(positionConfigFields.max_reserved_multiplier),
756
+ minCollateralValue: (0, utils_2.parseValue)(positionConfigFields.min_collateral_value),
757
+ };
758
+ }
759
+ static parseSymbolConfig(raw) {
760
+ const { fields } = raw.data.content;
761
+ return {
762
+ id: fields.id.id,
763
+ max_opening_size: (0, utils_2.parseValue)(fields.max_opening_size),
764
+ max_opening_size_enabled: fields.max_opening_size_enabled,
765
+ max_opening_size_per_position: (0, utils_2.parseValue)(fields.max_opening_size_per_position),
766
+ max_opening_size_per_position_enabled: fields.max_opening_size_per_position_enabled,
767
+ instant_exit_fee_config: {
768
+ instant_exit_tier_1_duration_threshold: (0, utils_2.parseValue)(fields.instant_exit_fee_config.fields.instant_exit_tier_1_duration_threshold),
769
+ instant_exit_tier_1_fee_bps: (0, utils_2.parseValue)(fields.instant_exit_fee_config.fields.instant_exit_tier_1_fee_bps.fields.value),
770
+ instant_exit_tier_1_fee_enabled: fields.instant_exit_fee_config.fields.instant_exit_tier_1_fee_enabled,
771
+ instant_exit_tier_2_duration_threshold: (0, utils_2.parseValue)(fields.instant_exit_fee_config.fields.instant_exit_tier_2_duration_threshold),
772
+ instant_exit_tier_2_fee_bps: (0, utils_2.parseValue)(fields.instant_exit_fee_config.fields.instant_exit_tier_2_fee_bps.fields.value),
773
+ instant_exit_tier_2_fee_enabled: fields.instant_exit_fee_config.fields.instant_exit_tier_2_fee_enabled,
774
+ instant_exit_tier_3_duration_threshold: (0, utils_2.parseValue)(fields.instant_exit_fee_config.fields.instant_exit_tier_3_duration_threshold),
775
+ instant_exit_tier_3_fee_bps: (0, utils_2.parseValue)(fields.instant_exit_fee_config.fields.instant_exit_tier_3_fee_bps.fields.value),
776
+ instant_exit_tier_3_fee_enabled: fields.instant_exit_fee_config.fields.instant_exit_tier_3_fee_enabled,
777
+ },
778
+ };
779
+ }
780
+ async parsePositionInfo(raw, id_) {
781
+ const { content } = raw.data;
782
+ const { fields } = content;
783
+ const positionFields = fields.value.fields;
784
+ const dataType = fields.name.type;
785
+ const positionInfo = {
786
+ id: id_,
787
+ long: dataType.includes('::market::LONG'),
788
+ owner: fields.name.fields.owner,
789
+ version: Number.parseInt(raw.data.version, 10),
790
+ collateralToken: (0, utils_2.suiSymbolToSymbol)(dataType.split('<')[1].split(',')[0].trim(), this.consts),
791
+ indexToken: (0, utils_2.suiSymbolToSymbol)(dataType.split(',')[1].trim(), this.consts),
792
+ collateralAmount: (0, utils_2.parseValue)(positionFields.collateral),
793
+ positionAmount: (0, utils_2.parseValue)(positionFields.position_amount),
794
+ reservedAmount: (0, utils_2.parseValue)(positionFields.reserved),
795
+ positionSize: (0, utils_2.parseValue)(positionFields.position_size),
796
+ lastFundingRate: (0, utils_2.parseValue)(positionFields.last_funding_rate),
797
+ lastReservingRate: (0, utils_2.parseValue)(positionFields.last_reserving_rate),
798
+ reservingFeeAmount: (0, utils_2.parseValue)(positionFields.reserving_fee_amount),
799
+ fundingFeeValue: (0, utils_2.parseValue)(positionFields.funding_fee_value),
800
+ closed: positionFields.closed,
801
+ openTimestamp: (0, utils_2.parseValue)(positionFields.open_timestamp),
802
+ };
803
+ positionInfo.reservingFeeAmount = ZLPDataAPI.calculatePositionReserveFee(positionInfo, await this.getVaultInfo(positionInfo.collateralToken), (await this.getVaultInfo(positionInfo.collateralToken)).reservingFeeModel, Date.now() / 1000);
804
+ // OI context for funding: fetch state and paired side size when enabled
805
+ const oiState = await this.getSymbolOiFundingState(positionInfo.indexToken);
806
+ const pairedSymbol = await this.getSymbolInfo(positionInfo.indexToken, !positionInfo.long);
807
+ positionInfo.fundingFeeValue = ZLPDataAPI.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, oiState && oiState.enabled ? oiState.model : undefined, pairedSymbol.openingSize);
808
+ return positionInfo;
809
+ }
810
+ static calculatePositionFundingFee(position, symbol, model, price, lpSupplyAmount, timestamp, oiModel, pairedOpeningSize) {
811
+ const accFundingRate = ZLPDataAPI.calcAccFundingFeeRate(symbol, model, price, lpSupplyAmount, timestamp, oiModel, pairedOpeningSize);
812
+ return position.fundingFeeValue + (accFundingRate - position.lastFundingRate) * position.positionSize;
813
+ }
814
+ static parseRebaseFeeModel(raw) {
815
+ const { fields } = raw.data.content;
816
+ return {
817
+ base: (0, utils_2.parseValue)(fields.base),
818
+ multiplier: (0, utils_2.parseValue)(fields.multiplier),
819
+ };
820
+ }
821
+ /**
822
+ * Fetches rebase fee exponent from chain (dynamic field RebaseFeeExponentKey).
823
+ * Returns 1 (linear) for backward compatibility when not set.
824
+ */
825
+ static async getRebaseFeeExponent(provider, rebaseFeeModelId, packageId) {
826
+ const WAD = 1e18;
827
+ try {
828
+ const raw = await provider.getDynamicFieldObject({
829
+ parentId: rebaseFeeModelId,
830
+ name: {
831
+ type: `${packageId}::model::RebaseFeeExponentKey`,
832
+ value: {},
833
+ },
834
+ });
835
+ if (raw.data?.content && 'fields' in raw.data.content) {
836
+ const value = raw.data.content.fields?.value;
837
+ if (value !== undefined) {
838
+ return Number(BigInt(value)) / WAD;
839
+ }
840
+ }
841
+ }
842
+ catch {
843
+ // Dynamic field doesn't exist - use default WAD (linear)
844
+ }
845
+ return 1;
846
+ }
847
+ static parseReservingFeeModel(raw) {
848
+ const content = raw.data.content.fields;
849
+ return {
850
+ multiplier: (0, utils_2.parseValue)(content.multiplier),
851
+ };
852
+ }
853
+ static parseFundingFeeModel(raw) {
854
+ const content = raw.data.content.fields;
855
+ return {
856
+ multiplier: (0, utils_2.parseValue)(content.multiplier),
857
+ max: (0, utils_2.parseValue)(content.max),
858
+ };
859
+ }
860
+ static parseOiFundingState(raw) {
861
+ const content = raw.data.content.fields;
862
+ return {
863
+ id: content.id.id,
864
+ enabled: content.enabled,
865
+ last_update: (0, utils_2.parseValue)(content.last_update),
866
+ model: {
867
+ id: content.model.fields.id.id,
868
+ multiplier: (0, utils_2.parseValue)(content.model.fields.multiplier),
869
+ exponent: (0, utils_2.parseValue)(content.model.fields.exponent),
870
+ max: (0, utils_2.parseValue)(content.model.fields.max),
871
+ },
872
+ };
873
+ }
874
+ static parsePriceImpactConfig(raw) {
875
+ const content = raw.data.content.fields;
876
+ return {
877
+ id: content.id.id,
878
+ enabled: content.enabled,
879
+ baseSpreadRate: (0, utils_2.parseValue)(content.base_spread_rate),
880
+ maxDynamicSpreadRate: (0, utils_2.parseValue)(content.max_dynamic_spread_rate),
881
+ maxTotalSpreadRate: (0, utils_2.parseValue)(content.max_total_spread_rate),
882
+ impactExponent: (0, utils_2.parseValue)(content.impact_exponent),
883
+ maxOiLong: (0, utils_2.parseValue)(content.max_oi_long),
884
+ maxOiShort: (0, utils_2.parseValue)(content.max_oi_short),
885
+ referenceSize: (0, utils_2.parseValue)(content.reference_size),
886
+ };
887
+ }
888
+ /**
889
+ * Computes the spread rate based on OI skew between long and short sides using point evaluation.
890
+ * This is the offchain equivalent of price_impact::compute_spread_rate in the Move contract.
891
+ *
892
+ * NOTE: This method uses point evaluation at the final state. For large trades,
893
+ * use computeAverageSpreadRate() instead, which integrates over the trade path
894
+ * for more accurate pricing.
895
+ *
896
+ * @param config - Price impact configuration for the symbol
897
+ * @param currentOiThisSide - Current OI on the side being traded (in value terms)
898
+ * @param newPositionSize - Size of the new position in value terms
899
+ * @param currentOiOpposite - Current OI on the opposite side (in value terms)
900
+ * @param maxOiThisSide - Maximum OI for this side (from config or SymbolConfig)
901
+ * @param maxOiOpposite - Maximum OI for opposite side (from config or SymbolConfig)
902
+ * @returns Total spread rate to apply (as a decimal, e.g., 0.001 = 0.1%)
903
+ */
904
+ static computeSpreadRate(config, currentOiThisSide, newPositionSize, currentOiOpposite, maxOiThisSide, maxOiOpposite) {
905
+ // If not enabled, return zero spread
906
+ if (!config.enabled) {
907
+ return 0;
908
+ }
909
+ // If maxOiThisSide is zero, return base spread only (cannot compute utilization)
910
+ if (maxOiThisSide === 0) {
911
+ return config.baseSpreadRate;
912
+ }
913
+ // Calculate this side utilization: (current_oi + new_position) / max_oi
914
+ const totalOiThis = currentOiThisSide + newPositionSize;
915
+ const thisUtil = Math.min(totalOiThis / maxOiThisSide, 1);
916
+ // Calculate opposite side utilization: current_oi / max_oi
917
+ let oppositeUtil = 0;
918
+ if (maxOiOpposite > 0) {
919
+ oppositeUtil = Math.min(currentOiOpposite / maxOiOpposite, 1);
920
+ }
921
+ // Calculate skew_ratio = this_util - opposite_util, clamped to [0, 1]
922
+ // Positive when this side is more utilized (crowded), which increases spread
923
+ let skewRatio = thisUtil - oppositeUtil;
924
+ if (skewRatio < 0) {
925
+ skewRatio = 0; // No penalty when opposite side is more crowded
926
+ }
927
+ else if (skewRatio > 1) {
928
+ skewRatio = 1; // Cap at 1.0
929
+ }
930
+ // Calculate dynamic_spread = max_dynamic * (skew_ratio ^ exponent)
931
+ // For fractional exponents, use linear interpolation
932
+ const exponent = config.impactExponent;
933
+ const exponentInt = Math.floor(exponent);
934
+ const exponentFrac = exponent - exponentInt;
935
+ // Compute base^exponent_int
936
+ let ratioPowered = skewRatio ** exponentInt;
937
+ // For fractional exponent, interpolate: result = base^n + frac * (base^(n+1) - base^n)
938
+ if (exponentFrac > 0) {
939
+ const ratioPoweredNext = skewRatio ** (exponentInt + 1);
940
+ const diff = ratioPoweredNext - ratioPowered;
941
+ if (diff >= 0) {
942
+ ratioPowered += exponentFrac * diff;
943
+ }
944
+ }
945
+ // dynamic_spread = max_dynamic_spread * final_ratio
946
+ const dynamicSpread = config.maxDynamicSpreadRate * ratioPowered;
947
+ // total_spread = base_spread + dynamic_spread
948
+ let totalSpread = config.baseSpreadRate + dynamicSpread;
949
+ // Cap at max_total_spread
950
+ if (totalSpread > config.maxTotalSpreadRate) {
951
+ totalSpread = config.maxTotalSpreadRate;
952
+ }
953
+ return totalSpread;
954
+ }
955
+ /**
956
+ * Computes the size factor multiplier based on position size.
957
+ * Formula: size_factor = 1 + min(1, position_size / reference_size)
958
+ *
959
+ * @param positionSize - Size of the new position
960
+ * @param referenceSize - Reference size for scaling (0 = disabled)
961
+ * @returns Size factor multiplier (1.0 to 2.0)
962
+ */
963
+ static computeSizeFactor(positionSize, referenceSize) {
964
+ // If reference_size is zero, size scaling is disabled
965
+ if (referenceSize === 0) {
966
+ return 1;
967
+ }
968
+ // Compute position_size / reference_size, capped at 1
969
+ const ratio = Math.min(positionSize / referenceSize, 1);
970
+ // Return 1 + capped_ratio
971
+ return 1 + ratio;
972
+ }
973
+ /**
974
+ * Computes the average value of max_dynamic * s^n over the interval [s1, s2].
975
+ * Uses the integral formula: average = max_dynamic * (s2^(n+1) - s1^(n+1)) / ((n+1) * (s2 - s1))
976
+ *
977
+ * @param maxDynamic - Maximum dynamic spread rate
978
+ * @param s1 - Start skew ratio
979
+ * @param s2 - End skew ratio
980
+ * @param exponent - Impact exponent
981
+ * @returns Average dynamic spread rate
982
+ */
983
+ static computeIntegralAverage(maxDynamic, s1, s2, exponent) {
984
+ // If s1 == s2, return point evaluation
985
+ if (s1 === s2) {
986
+ return maxDynamic * (s1 ** exponent);
987
+ }
988
+ // Compute n + 1
989
+ const nPlus1 = exponent + 1;
990
+ // Compute s2^(n+1) and s1^(n+1)
991
+ const s2Pow = s2 ** nPlus1;
992
+ const s1Pow = s1 ** nPlus1;
993
+ // Compute numerator: s2^(n+1) - s1^(n+1)
994
+ const numerator = Math.abs(s2Pow - s1Pow);
995
+ // Compute denominator: (n+1) * (s2 - s1)
996
+ const sDiff = Math.abs(s2 - s1);
997
+ const denominator = nPlus1 * sDiff;
998
+ // Avoid division by zero
999
+ if (denominator === 0) {
1000
+ return 0;
1001
+ }
1002
+ // Compute average ratio and multiply by max_dynamic
1003
+ return maxDynamic * (numerator / denominator);
1004
+ }
1005
+ /**
1006
+ * Computes the average spread rate over the trade path with size scaling.
1007
+ * This is the offchain equivalent of price_impact::compute_average_spread_rate in the Move contract.
1008
+ *
1009
+ * This function integrates the spread over the utilization change rather than
1010
+ * using the final-state spread, and applies a size-based multiplier to the
1011
+ * dynamic spread component.
1012
+ *
1013
+ * @param config - Price impact configuration for the symbol
1014
+ * @param currentOiThisSide - Current OI on the side being traded (in value terms)
1015
+ * @param newPositionSize - Size of the new position in value terms
1016
+ * @param currentOiOpposite - Current OI on the opposite side (in value terms)
1017
+ * @param maxOiThisSide - Maximum OI for this side (from config or SymbolConfig)
1018
+ * @param maxOiOpposite - Maximum OI for opposite side (from config or SymbolConfig)
1019
+ * @returns Total spread rate to apply (as a decimal, e.g., 0.001 = 0.1%)
1020
+ */
1021
+ static computeAverageSpreadRate(config, currentOiThisSide, newPositionSize, currentOiOpposite, maxOiThisSide, maxOiOpposite) {
1022
+ // If not enabled, return zero spread
1023
+ if (!config.enabled) {
1024
+ return 0;
1025
+ }
1026
+ // If maxOiThisSide is zero, return base spread only (cannot compute utilization)
1027
+ if (maxOiThisSide === 0) {
1028
+ return config.baseSpreadRate;
1029
+ }
1030
+ // Calculate opposite side utilization (constant throughout the trade)
1031
+ let oppositeUtil = 0;
1032
+ if (maxOiOpposite > 0) {
1033
+ oppositeUtil = Math.min(currentOiOpposite / maxOiOpposite, 1);
1034
+ }
1035
+ // Calculate start utilization: current_oi / max_oi
1036
+ const startUtil = Math.min(currentOiThisSide / maxOiThisSide, 1);
1037
+ // Calculate end utilization: (current_oi + new_position) / max_oi
1038
+ const totalOiThis = currentOiThisSide + newPositionSize;
1039
+ const endUtil = Math.min(totalOiThis / maxOiThisSide, 1);
1040
+ // Calculate start skew: max(0, min(1, start_util - opposite_util))
1041
+ let startSkew = startUtil - oppositeUtil;
1042
+ if (startSkew < 0) {
1043
+ startSkew = 0;
1044
+ }
1045
+ else if (startSkew > 1) {
1046
+ startSkew = 1;
1047
+ }
1048
+ // Calculate end skew: max(0, min(1, end_util - opposite_util))
1049
+ let endSkew = endUtil - oppositeUtil;
1050
+ if (endSkew < 0) {
1051
+ endSkew = 0;
1052
+ }
1053
+ else if (endSkew > 1) {
1054
+ endSkew = 1;
1055
+ }
1056
+ // Compute average dynamic spread via integral
1057
+ const avgDynamicRate = ZLPDataAPI.computeIntegralAverage(config.maxDynamicSpreadRate, startSkew, endSkew, config.impactExponent);
1058
+ // Apply size scaling factor to dynamic spread
1059
+ const sizeFactor = ZLPDataAPI.computeSizeFactor(newPositionSize, config.referenceSize);
1060
+ const scaledDynamicRate = avgDynamicRate * sizeFactor;
1061
+ // total_spread = base_spread + scaled_dynamic
1062
+ let totalSpread = config.baseSpreadRate + scaledDynamicRate;
1063
+ // Cap at max_total_spread
1064
+ if (totalSpread > config.maxTotalSpreadRate) {
1065
+ totalSpread = config.maxTotalSpreadRate;
1066
+ }
1067
+ return totalSpread;
1068
+ }
1069
+ /**
1070
+ * Applies price impact to a price.
1071
+ * This is the offchain equivalent of price_impact::apply_price_impact in the Move contract.
1072
+ *
1073
+ * Direction logic:
1074
+ * - Open Long: price INCREASES (pay more)
1075
+ * - Open Short: price DECREASES (receive less)
1076
+ * - Close Long: price DECREASES (receive less)
1077
+ * - Close Short: price INCREASES (pay more)
1078
+ *
1079
+ * @param originalPrice - The original price
1080
+ * @param spreadRate - The spread rate to apply (as a decimal, e.g., 0.001 = 0.1%)
1081
+ * @param isLong - Whether this is a long position
1082
+ * @param isOpening - Whether this is opening (true) or closing (false)
1083
+ * @returns Adjusted price with spread applied
1084
+ */
1085
+ static applyPriceImpact(originalPrice, spreadRate, isLong, isOpening) {
1086
+ // If spread is zero, return original price
1087
+ if (spreadRate === 0) {
1088
+ return originalPrice;
1089
+ }
1090
+ // Determine if we should increase or decrease price
1091
+ // Open Long or Close Short -> increase price
1092
+ // Open Short or Close Long -> decrease price
1093
+ const increasePrice = (isLong && isOpening) || (!isLong && !isOpening);
1094
+ // Calculate spread amount: price * spread_rate
1095
+ const spreadAmount = originalPrice * spreadRate;
1096
+ // Apply spread
1097
+ if (increasePrice) {
1098
+ return originalPrice + spreadAmount;
1099
+ }
1100
+ // Ensure we don't go negative
1101
+ const adjusted = originalPrice - spreadAmount;
1102
+ return Math.max(adjusted, 0);
1103
+ }
1104
+ /**
1105
+ * Convenience method to compute the estimated execution price with price impact.
1106
+ * Fetches the required data and computes the adjusted price.
1107
+ *
1108
+ * @param indexToken - The index token symbol (e.g., 'sui', 'btc')
1109
+ * @param isLong - Whether this is a long position
1110
+ * @param isOpening - Whether this is opening or closing
1111
+ * @param positionSizeValue - Size of the position in value terms (USD)
1112
+ * @returns Object containing spread rate and adjusted price, or null if price impact not configured
1113
+ */
1114
+ async estimatePriceImpact(indexToken, isLong, isOpening, positionSizeValue) {
1115
+ const config = await this.getPriceImpactConfig(indexToken);
1116
+ if (!config) {
1117
+ return null;
1118
+ }
1119
+ // Get current OI for both sides
1120
+ const longSymbol = await this.getSymbolInfo(indexToken, true);
1121
+ const shortSymbol = await this.getSymbolInfo(indexToken, false);
1122
+ // Get oracle price
1123
+ const oraclePrice = (await this.getOraclePrice(indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked();
1124
+ // Determine which side is "this" side and which is "opposite"
1125
+ const currentOiThisSide = isLong ? longSymbol.openingSize : shortSymbol.openingSize;
1126
+ const currentOiOpposite = isLong ? shortSymbol.openingSize : longSymbol.openingSize;
1127
+ const maxOiThisSide = isLong ? config.maxOiLong : config.maxOiShort;
1128
+ const maxOiOpposite = isLong ? config.maxOiShort : config.maxOiLong;
1129
+ // Compute average spread rate (uses integration for better accuracy on large trades)
1130
+ const spreadRate = ZLPDataAPI.computeAverageSpreadRate(config, currentOiThisSide, positionSizeValue, currentOiOpposite, maxOiThisSide, maxOiOpposite);
1131
+ // Apply price impact
1132
+ const adjustedPrice = ZLPDataAPI.applyPriceImpact(oraclePrice, spreadRate, isLong, isOpening);
1133
+ return {
1134
+ spreadRate,
1135
+ originalPrice: oraclePrice,
1136
+ adjustedPrice,
1137
+ };
1138
+ }
1139
+ parseOrderInfo(raw, capId) {
1140
+ const { content } = raw.data;
1141
+ const { fields } = content.fields.value;
1142
+ // Extract tokens from dataType
1143
+ const dataType = content?.type;
1144
+ let orderType;
1145
+ if (content.fields.value?.type.includes('OpenPositionOrder')) {
1146
+ orderType = 'OPEN_POSITION';
1147
+ }
1148
+ else if (content.fields.value?.type.includes('OpenMarketOrder')) {
1149
+ orderType = 'OPEN_MARKET';
1150
+ }
1151
+ else if (content.fields.value?.type.includes('DecreasePositionOrder')) {
1152
+ orderType = 'DECREASE_POSITION';
1153
+ }
1154
+ else {
1155
+ orderType = 'DECREASE_MARKET';
1156
+ }
1157
+ const ret = {
1158
+ id: content.fields.id.id,
1159
+ capId,
1160
+ executed: fields.executed,
1161
+ owner: content.fields.name.fields.owner,
1162
+ collateralToken: (0, utils_2.suiSymbolToSymbol)(dataType.split('<')[2].split(',')[0].trim(), this.consts),
1163
+ indexToken: (0, utils_2.suiSymbolToSymbol)(dataType.split(',')[1].trim(), this.consts),
1164
+ feeToken: (0, utils_2.suiSymbolToSymbol)(dataType.split(',')[3].split('>')[0].trim(), this.consts),
1165
+ // Use index_price for open orders, limited_index_price for decrease orders
1166
+ indexPrice: orderType === 'OPEN_MARKET'
1167
+ ? (0, utils_2.parseValue)(fields.index_price)
1168
+ : (0, utils_2.parseValue)(fields.limited_index_price?.fields?.price || fields.limited_index_price),
1169
+ collateralPriceThreshold: fields.collateral_price_threshold ? (0, utils_2.parseValue)(fields.collateral_price_threshold) : 0,
1170
+ // New index price threshold
1171
+ indexPriceThreshold: fields.index_price_threshold ? (0, utils_2.parseValue)(fields.index_price_threshold) : undefined,
1172
+ feeAmount: BigInt(fields.fee),
1173
+ long: dataType.includes('::market::LONG'),
1174
+ orderType,
1175
+ createdAt: (0, utils_2.parseValue)(fields.created_at),
1176
+ v11Order: !fields.limited_index_price?.fields?.price,
1177
+ // New: referrer and scard fields
1178
+ referrer: fields.referrer,
1179
+ scardId: (fields.scard_id?.fields?.some?.fields?.id
1180
+ || fields.scard_id?.fields?.value?.fields?.id
1181
+ || fields.scard_id?.fields?.id
1182
+ || fields.scard_id?.id),
1183
+ scardRebateRate: (() => {
1184
+ const inner = fields.scard_rebate_rate?.fields?.some ?? fields.scard_rebate_rate?.fields?.value;
1185
+ return inner ? (0, utils_2.parseValue)(inner) : undefined;
1186
+ })(),
1187
+ };
1188
+ if (orderType === 'OPEN_POSITION' || orderType === 'OPEN_MARKET') {
1189
+ ret.openOrder = {
1190
+ reserveAmount: BigInt(fields.reserve_amount),
1191
+ collateralAmount: BigInt(fields.collateral),
1192
+ openAmount: BigInt(fields.open_amount),
1193
+ // New: position_config from OpenMarketOrder
1194
+ positionConfig: fields.position_config?.fields
1195
+ ? {
1196
+ decreaseFeeBps: (0, utils_2.parseValue)(fields.position_config.fields.decrease_fee_bps),
1197
+ liquidationBonus: (0, utils_2.parseValue)(fields.position_config.fields.liquidation_bonus),
1198
+ liquidationThreshold: (0, utils_2.parseValue)(fields.position_config.fields.liquidation_threshold),
1199
+ maxLeverage: (0, utils_2.parseValue)(fields.position_config.fields.max_leverage),
1200
+ minHoldingDuration: (0, utils_2.parseValue)(fields.position_config.fields.min_holding_duration),
1201
+ openFeeBps: (0, utils_2.parseValue)(fields.position_config.fields.open_fee_bps),
1202
+ maxReservedMultiplier: (0, utils_2.parseValue)(fields.position_config.fields.max_reserved_multiplier),
1203
+ minCollateralValue: (0, utils_2.parseValue)(fields.position_config.fields.min_collateral_value),
1204
+ }
1205
+ : undefined,
1206
+ };
1207
+ }
1208
+ else if (orderType === 'DECREASE_POSITION' || orderType === 'DECREASE_MARKET') {
1209
+ ret.decreaseOrder = {
1210
+ decreaseAmount: BigInt(fields.decrease_amount),
1211
+ takeProfit: fields.take_profit,
1212
+ };
1213
+ }
1214
+ return ret;
1215
+ }
1216
+ static calculateVaultReservingFee(vaultInfo, reservingFeeModel, currentTime) {
1217
+ const timeDelta = currentTime - vaultInfo.lastUpdate;
1218
+ const periods = Math.floor(timeDelta / consts_1.SECONDS_PER_EIGHT_HOUR);
1219
+ return vaultInfo.unrealisedReservingFeeAmount
1220
+ + (vaultInfo.reservedAmount * reservingFeeModel.multiplier * periods) / 1e18;
1221
+ }
1222
+ static parseCredential(raw, pool) {
1223
+ const stakedAmount = BigInt(raw.data.content.fields.stake);
1224
+ const accRewardPerShare = BigInt(raw.data.content.fields.acc_reward_per_share);
1225
+ return {
1226
+ id: raw.data.objectId,
1227
+ lockUntil: (0, utils_2.parseValue)(raw.data.content.fields.lock_until),
1228
+ amount: stakedAmount,
1229
+ accRewardPerShare,
1230
+ claimable: ((pool.accRewardPerShare - accRewardPerShare) * stakedAmount)
1231
+ / BigInt(1e18),
1232
+ };
1233
+ }
1234
+ static parseStakePool(raw) {
1235
+ const content = raw.data.content.fields;
1236
+ const pool = {
1237
+ id: content.id.id,
1238
+ enabled: content.enabled,
1239
+ lastUpdatedTime: (0, utils_2.parseValue)(content.last_updated_time),
1240
+ stakedAmount: BigInt(content.staked_amount),
1241
+ reward: BigInt(content.reward_vault),
1242
+ startTime: (0, utils_2.parseValue)(content.start_time),
1243
+ endTime: (0, utils_2.parseValue)(content.end_time),
1244
+ rewardRate: BigInt(content.reward_rate),
1245
+ accRewardPerShare: BigInt(content.acc_reward_per_share),
1246
+ lockDuration: (0, utils_2.parseValue)(content.lock_duration),
1247
+ };
1248
+ ZLPDataAPI.refreshPool(pool, Math.floor(Date.now() / 1000));
1249
+ return pool;
1250
+ }
1251
+ static refreshPool(pool, timestamp) {
1252
+ if (timestamp <= pool.lastUpdatedTime || timestamp < pool.startTime) {
1253
+ return;
1254
+ }
1255
+ const applicableEndTime = Math.max(pool.endTime, pool.lastUpdatedTime);
1256
+ const calculationEndTime = Math.min(timestamp, applicableEndTime);
1257
+ if (calculationEndTime > pool.lastUpdatedTime && pool.stakedAmount > BigInt(0) && pool.rewardRate > BigInt(0)) {
1258
+ const timeDiff = BigInt(calculationEndTime - pool.lastUpdatedTime);
1259
+ const rewardAmount = timeDiff * pool.rewardRate;
1260
+ const rewardPerShare = (rewardAmount * BigInt(1e18)) / pool.stakedAmount;
1261
+ pool.accRewardPerShare += rewardPerShare;
1262
+ }
1263
+ pool.lastUpdatedTime = calculationEndTime;
1264
+ }
1265
+ }
1266
+ exports.ZLPDataAPI = ZLPDataAPI;
1267
+ //# sourceMappingURL=ZLPDataAPI.cjs.map