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