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