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