zo-sdk 0.0.50 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -20
- package/dist/abstract/BaseAPI.cjs +117 -0
- package/dist/abstract/BaseAPI.cjs.map +1 -0
- package/dist/abstract/BaseAPI.d.cts +131 -0
- package/dist/abstract/BaseAPI.d.cts.map +1 -0
- package/dist/abstract/BaseAPI.d.mts +131 -0
- package/dist/abstract/BaseAPI.d.mts.map +1 -0
- package/dist/abstract/BaseAPI.mjs +113 -0
- package/dist/abstract/BaseAPI.mjs.map +1 -0
- package/dist/abstract/BaseDataAPI.cjs +139 -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 +135 -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/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-slp-mainnet.json +710 -0
- package/dist/consts/deployments-slp-testnet.json +109 -0
- package/dist/consts/deployments-usdz-mainnet.json +180 -0
- package/dist/consts/deployments-usdz-testnet.json +98 -0
- package/dist/consts/{deployments-mainnet.json → deployments-zlp-mainnet.json} +278 -42
- package/dist/consts/index.cjs +40 -8
- package/dist/consts/index.cjs.map +1 -1
- package/dist/consts/index.d.cts +81 -11
- package/dist/consts/index.d.cts.map +1 -1
- package/dist/consts/index.d.mts +81 -11
- package/dist/consts/index.d.mts.map +1 -1
- package/dist/consts/index.mjs +39 -7
- package/dist/consts/index.mjs.map +1 -1
- package/dist/consts/price_id_to_object_id.mainnet.json +9 -1
- package/dist/data.cjs +2 -2
- package/dist/data.cjs.map +1 -1
- package/dist/data.mjs +2 -2
- package/dist/data.mjs.map +1 -1
- package/dist/factory/SDKFactory.cjs +185 -0
- package/dist/factory/SDKFactory.cjs.map +1 -0
- package/dist/factory/SDKFactory.d.cts +74 -0
- package/dist/factory/SDKFactory.d.cts.map +1 -0
- package/dist/factory/SDKFactory.d.mts +74 -0
- package/dist/factory/SDKFactory.d.mts.map +1 -0
- package/dist/factory/SDKFactory.mjs +179 -0
- package/dist/factory/SDKFactory.mjs.map +1 -0
- package/dist/implementations/SLPAPI.cjs +829 -0
- package/dist/implementations/SLPAPI.cjs.map +1 -0
- package/dist/implementations/SLPAPI.d.cts +120 -0
- package/dist/implementations/SLPAPI.d.cts.map +1 -0
- package/dist/implementations/SLPAPI.d.mts +120 -0
- package/dist/implementations/SLPAPI.d.mts.map +1 -0
- package/dist/implementations/SLPAPI.mjs +825 -0
- package/dist/implementations/SLPAPI.mjs.map +1 -0
- package/dist/implementations/SLPDataAPI.cjs +916 -0
- package/dist/implementations/SLPDataAPI.cjs.map +1 -0
- package/dist/implementations/SLPDataAPI.d.cts +102 -0
- package/dist/implementations/SLPDataAPI.d.cts.map +1 -0
- package/dist/implementations/SLPDataAPI.d.mts +102 -0
- package/dist/implementations/SLPDataAPI.d.mts.map +1 -0
- package/dist/implementations/SLPDataAPI.mjs +912 -0
- package/dist/implementations/SLPDataAPI.mjs.map +1 -0
- package/dist/implementations/USDZAPI.cjs +522 -0
- package/dist/implementations/USDZAPI.cjs.map +1 -0
- package/dist/implementations/USDZAPI.d.cts +118 -0
- package/dist/implementations/USDZAPI.d.cts.map +1 -0
- package/dist/implementations/USDZAPI.d.mts +118 -0
- package/dist/implementations/USDZAPI.d.mts.map +1 -0
- package/dist/implementations/USDZAPI.mjs +518 -0
- package/dist/implementations/USDZAPI.mjs.map +1 -0
- package/dist/implementations/USDZDataAPI.cjs +697 -0
- package/dist/implementations/USDZDataAPI.cjs.map +1 -0
- package/dist/implementations/USDZDataAPI.d.cts +86 -0
- package/dist/implementations/USDZDataAPI.d.cts.map +1 -0
- package/dist/implementations/USDZDataAPI.d.mts +86 -0
- package/dist/implementations/USDZDataAPI.d.mts.map +1 -0
- package/dist/implementations/USDZDataAPI.mjs +693 -0
- package/dist/implementations/USDZDataAPI.mjs.map +1 -0
- package/dist/implementations/ZLPAPI.cjs +809 -0
- package/dist/implementations/ZLPAPI.cjs.map +1 -0
- package/dist/implementations/ZLPAPI.d.cts +121 -0
- package/dist/implementations/ZLPAPI.d.cts.map +1 -0
- package/dist/implementations/ZLPAPI.d.mts +121 -0
- package/dist/implementations/ZLPAPI.d.mts.map +1 -0
- package/dist/implementations/ZLPAPI.mjs +805 -0
- package/dist/implementations/ZLPAPI.mjs.map +1 -0
- package/dist/implementations/ZLPDataAPI.cjs +724 -0
- package/dist/implementations/ZLPDataAPI.cjs.map +1 -0
- package/dist/implementations/ZLPDataAPI.d.cts +83 -0
- package/dist/implementations/ZLPDataAPI.d.cts.map +1 -0
- package/dist/implementations/ZLPDataAPI.d.mts +83 -0
- package/dist/implementations/ZLPDataAPI.d.mts.map +1 -0
- package/dist/implementations/ZLPDataAPI.mjs +720 -0
- package/dist/implementations/ZLPDataAPI.mjs.map +1 -0
- package/dist/implementations/index.cjs +22 -0
- package/dist/implementations/index.cjs.map +1 -0
- package/dist/implementations/index.d.cts +11 -0
- package/dist/implementations/index.d.cts.map +1 -0
- package/dist/implementations/index.d.mts +11 -0
- package/dist/implementations/index.d.mts.map +1 -0
- package/dist/implementations/index.mjs +13 -0
- package/dist/implementations/index.mjs.map +1 -0
- package/dist/index.cjs +47 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +45 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +45 -0
- package/dist/index.mjs.map +1 -1
- package/dist/interfaces/base.cjs +8 -0
- package/dist/interfaces/base.cjs.map +1 -0
- package/dist/interfaces/base.d.cts +293 -0
- package/dist/interfaces/base.d.cts.map +1 -0
- package/dist/interfaces/base.d.mts +293 -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 +29 -0
- package/dist/interfaces/index.cjs.map +1 -0
- package/dist/interfaces/index.d.cts +13 -0
- package/dist/interfaces/index.d.cts.map +1 -0
- package/dist/interfaces/index.d.mts +13 -0
- package/dist/interfaces/index.d.mts.map +1 -0
- package/dist/interfaces/index.mjs +13 -0
- package/dist/interfaces/index.mjs.map +1 -0
- package/dist/interfaces/slp.cjs +9 -0
- package/dist/interfaces/slp.cjs.map +1 -0
- package/dist/interfaces/slp.d.cts +115 -0
- package/dist/interfaces/slp.d.cts.map +1 -0
- package/dist/interfaces/slp.d.mts +115 -0
- package/dist/interfaces/slp.d.mts.map +1 -0
- package/dist/interfaces/slp.mjs +7 -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 +40 -0
- package/dist/interfaces/usdz.d.cts.map +1 -0
- package/dist/interfaces/usdz.d.mts +40 -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/zlp.cjs +7 -0
- package/dist/interfaces/zlp.cjs.map +1 -0
- package/dist/interfaces/zlp.d.cts +45 -0
- package/dist/interfaces/zlp.d.cts.map +1 -0
- package/dist/interfaces/zlp.d.mts +45 -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 +7 -35
- package/dist/oracle.cjs.map +1 -1
- package/dist/oracle.d.cts +3 -4
- package/dist/oracle.d.cts.map +1 -1
- package/dist/oracle.d.mts +3 -4
- package/dist/oracle.d.mts.map +1 -1
- package/dist/oracle.mjs +8 -32
- package/dist/oracle.mjs.map +1 -1
- package/package.json +1 -1
- package/src/abstract/BaseAPI.ts +429 -0
- package/src/abstract/BaseDataAPI.ts +204 -0
- package/src/abstract/index.ts +7 -0
- package/src/bcs.ts +45 -0
- package/src/consts/deployments-slp-mainnet.json +710 -0
- package/src/consts/deployments-slp-testnet.json +109 -0
- package/src/consts/deployments-usdz-mainnet.json +180 -0
- package/src/consts/deployments-usdz-testnet.json +98 -0
- package/src/consts/{deployments-mainnet.json → deployments-zlp-mainnet.json} +279 -43
- package/src/consts/index.ts +134 -39
- package/src/consts/price_id_to_object_id.mainnet.json +10 -2
- package/src/data.ts +2 -2
- package/src/factory/SDKFactory.ts +282 -0
- package/src/implementations/SLPAPI.ts +1207 -0
- package/src/implementations/SLPDataAPI.ts +1188 -0
- package/src/implementations/USDZAPI.ts +715 -0
- package/src/implementations/USDZDataAPI.ts +826 -0
- package/src/implementations/ZLPAPI.ts +1130 -0
- package/src/implementations/ZLPDataAPI.ts +856 -0
- package/src/implementations/index.ts +14 -0
- package/src/index.ts +53 -0
- package/src/interfaces/base.ts +556 -0
- package/src/interfaces/index.ts +45 -0
- package/src/interfaces/slp.ts +156 -0
- package/src/interfaces/usdz.ts +71 -0
- package/src/interfaces/zlp.ts +96 -0
- package/src/oracle.ts +12 -42
- package/tsconfig.json +4 -2
- package/dist/consts/staking/deployments-mainnet.json +0 -12
- package/dist/consts/staking/deployments-testnet.json +0 -11
- package/src/consts/staking/deployments-mainnet.json +0 -12
- package/src/consts/staking/deployments-testnet.json +0 -11
- /package/dist/consts/{deployments-testnet.json → deployments-zlp-testnet.json} +0 -0
- /package/src/consts/{deployments-testnet.json → deployments-zlp-testnet.json} +0 -0
|
@@ -0,0 +1,1188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SLP DataAPI implementation
|
|
3
|
+
* Implements SLP-specific data access methods for Sudo SDK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DynamicFieldInfo, SuiClient } from '@mysten/sui/client'
|
|
7
|
+
import { Transaction } from '@mysten/sui/transactions'
|
|
8
|
+
import { SUI_CLOCK_OBJECT_ID } from '@mysten/sui/utils'
|
|
9
|
+
import type { Network } from '../consts'
|
|
10
|
+
import { LPToken, SLP_TOKEN_DECIMALS } from '../consts'
|
|
11
|
+
import type {
|
|
12
|
+
ISLPDataAPI,
|
|
13
|
+
ISLPMarketValuationInfo,
|
|
14
|
+
ISLPMarketInfo,
|
|
15
|
+
ISLPVaultInfo,
|
|
16
|
+
ISLPSymbolInfo,
|
|
17
|
+
ISLPPositionInfo,
|
|
18
|
+
ISLPOrderInfo,
|
|
19
|
+
ISLPStakePool,
|
|
20
|
+
ISLPCredential,
|
|
21
|
+
IBaseHistoryResponse,
|
|
22
|
+
ISLPPositionCapInfo,
|
|
23
|
+
ISLPOrderCapInfo,
|
|
24
|
+
ISLPStaked,
|
|
25
|
+
ISLPPositionConfig,
|
|
26
|
+
ISLPFundingFeeModel,
|
|
27
|
+
ISLPReservingFeeModel,
|
|
28
|
+
ISLPRebaseFeeModel
|
|
29
|
+
} from '../interfaces'
|
|
30
|
+
import { BaseDataAPI } from '../abstract'
|
|
31
|
+
import { decimalToObject, joinSymbol, parseSymbolKey, parseValue, suiSymbolToSymbol } from '../utils'
|
|
32
|
+
import { Rate, SRate, SymbolsValuation, VaultsValuation } from '../bcs'
|
|
33
|
+
|
|
34
|
+
export interface GetCumulativeAprResponse {
|
|
35
|
+
generatedAt?: string
|
|
36
|
+
apr?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let aprResponse: GetCumulativeAprResponse = {}
|
|
40
|
+
|
|
41
|
+
const SECONDS_PER_EIGHT_HOUR = 8 * 60 * 60; // 28800 seconds
|
|
42
|
+
|
|
43
|
+
export class SLPDataAPI extends BaseDataAPI implements ISLPDataAPI {
|
|
44
|
+
constructor(
|
|
45
|
+
network: Network,
|
|
46
|
+
provider: SuiClient,
|
|
47
|
+
apiEndpoint: string,
|
|
48
|
+
connectionURL: string
|
|
49
|
+
) {
|
|
50
|
+
super(network, provider, apiEndpoint, connectionURL, LPToken.SLP)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async getRebaseFeeModel(): Promise<ISLPRebaseFeeModel> {
|
|
54
|
+
this.validateCache()
|
|
55
|
+
if (this.rebaseFeeModelCache) {
|
|
56
|
+
return this.rebaseFeeModelCache
|
|
57
|
+
}
|
|
58
|
+
const rawData = await this.provider.getObject({
|
|
59
|
+
id: this.consts.zoCore.rebaseFeeModel,
|
|
60
|
+
options: {
|
|
61
|
+
showContent: true,
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
return this.parseRebaseFeeModel(rawData)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates vaults valuation for SLP using Sudo SDK approach
|
|
69
|
+
*/
|
|
70
|
+
public valuateVaults(tx: Transaction) {
|
|
71
|
+
if (!this.consts.sudoCore) {
|
|
72
|
+
throw new Error('Sudo Core configuration not found. Make sure you are using LPToken.SLP')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const vaultsValuation = tx.moveCall({
|
|
76
|
+
target: `${this.consts.sudoCore.upgradedPackage}::market::create_vaults_valuation`,
|
|
77
|
+
typeArguments: [`${this.consts.sudoCore.package}::slp::SLP`],
|
|
78
|
+
arguments: [
|
|
79
|
+
tx.object(SUI_CLOCK_OBJECT_ID),
|
|
80
|
+
tx.object(this.consts.sudoCore.market),
|
|
81
|
+
],
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
for (const key of Object.keys(this.consts.sudoCore.vaults)) {
|
|
85
|
+
const vault = this.consts.sudoCore.vaults[key]
|
|
86
|
+
|
|
87
|
+
tx.moveCall({
|
|
88
|
+
target: `${this.consts.sudoCore.upgradedPackage}::market::valuate_vault_v1_1`,
|
|
89
|
+
typeArguments: [
|
|
90
|
+
`${this.consts.sudoCore.package}::slp::SLP`,
|
|
91
|
+
this.consts.coins[key].module,
|
|
92
|
+
],
|
|
93
|
+
arguments: [
|
|
94
|
+
tx.object(this.consts.sudoCore.market),
|
|
95
|
+
tx.object(vault.reservingFeeModel),
|
|
96
|
+
tx.object(this.consts.pythFeeder.feeder[key]),
|
|
97
|
+
vaultsValuation,
|
|
98
|
+
],
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
return vaultsValuation
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates symbols valuation for SLP using Sudo SDK approach
|
|
106
|
+
*/
|
|
107
|
+
public valuateSymbols(tx: Transaction) {
|
|
108
|
+
if (!this.consts.sudoCore) {
|
|
109
|
+
throw new Error('Sudo Core configuration not found. Make sure you are using LPToken.SLP')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const symbolsValuation = tx.moveCall({
|
|
113
|
+
target: `${this.consts.sudoCore.upgradedPackage}::market::create_symbols_valuation`,
|
|
114
|
+
typeArguments: [`${this.consts.sudoCore.package}::slp::SLP`],
|
|
115
|
+
arguments: [
|
|
116
|
+
tx.object(SUI_CLOCK_OBJECT_ID),
|
|
117
|
+
tx.object(this.consts.sudoCore.market),
|
|
118
|
+
],
|
|
119
|
+
})
|
|
120
|
+
for (const key of Object.keys(this.consts.sudoCore.symbols)) {
|
|
121
|
+
const [direction, token] = parseSymbolKey(key)
|
|
122
|
+
const symbol = this.consts.sudoCore.symbols[key]
|
|
123
|
+
tx.moveCall({
|
|
124
|
+
target: `${this.consts.sudoCore.upgradedPackage}::market::valuate_symbol_v1_1`,
|
|
125
|
+
typeArguments: [
|
|
126
|
+
`${this.consts.sudoCore.package}::slp::SLP`,
|
|
127
|
+
this.consts.coins[token].module,
|
|
128
|
+
`${this.consts.sudoCore.package}::market::${direction.toUpperCase()}`,
|
|
129
|
+
],
|
|
130
|
+
arguments: [
|
|
131
|
+
tx.object(this.consts.sudoCore.market),
|
|
132
|
+
tx.object(symbol.fundingFeeModel),
|
|
133
|
+
tx.object(this.consts.pythFeeder.feeder[token]),
|
|
134
|
+
symbolsValuation,
|
|
135
|
+
],
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
return symbolsValuation
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Creates both vaults and symbols valuation for SLP
|
|
143
|
+
*/
|
|
144
|
+
public valuate(tx: Transaction) {
|
|
145
|
+
const vaultsValuation = this.valuateVaults(tx)
|
|
146
|
+
const symbolsValuation = this.valuateSymbols(tx)
|
|
147
|
+
return { vaultsValuation, symbolsValuation }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Valuates the SLP market using Sudo SDK's approach
|
|
152
|
+
*/
|
|
153
|
+
public async valuateMarket(): Promise<ISLPMarketValuationInfo> {
|
|
154
|
+
const marketInfo = await this.getMarketInfo()
|
|
155
|
+
let slpPrice = 0
|
|
156
|
+
let value = 0
|
|
157
|
+
value = await this.simValuate(this.consts.sudoCore.adminCap)
|
|
158
|
+
slpPrice = value / marketInfo.lpSupplyWithDecimals
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
marketCap: value,
|
|
162
|
+
price: slpPrice,
|
|
163
|
+
supply: marketInfo.lpSupplyWithDecimals,
|
|
164
|
+
apr: Number(marketInfo.apr),
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Valuates market using simulation (Sudo SDK specific)
|
|
170
|
+
*/
|
|
171
|
+
public async simValuate(sender: string): Promise<number> {
|
|
172
|
+
const tx = await this.initOracleTxb(
|
|
173
|
+
Object.keys(this.consts.pythFeeder.feeder),
|
|
174
|
+
)
|
|
175
|
+
this.valuate(tx)
|
|
176
|
+
const res = await this.provider.devInspectTransactionBlock({
|
|
177
|
+
transactionBlock: tx,
|
|
178
|
+
sender,
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const symbolsValuationOffset =
|
|
182
|
+
Object.keys(this.consts.sudoCore.symbols).length + 1
|
|
183
|
+
|
|
184
|
+
const vaultsValuation = VaultsValuation.parse(
|
|
185
|
+
new Uint8Array(
|
|
186
|
+
(
|
|
187
|
+
(res.results as any)[
|
|
188
|
+
(res.results?.length || 0) - symbolsValuationOffset - 1
|
|
189
|
+
].mutableReferenceOutputs as any
|
|
190
|
+
)[1][1],
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
const symbolsValuation = SymbolsValuation.parse(
|
|
195
|
+
new Uint8Array(
|
|
196
|
+
(
|
|
197
|
+
(res.results as any)[(res.results?.length || 0) - 1]
|
|
198
|
+
.mutableReferenceOutputs as any
|
|
199
|
+
)[1][1],
|
|
200
|
+
),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
const result =
|
|
204
|
+
Number(
|
|
205
|
+
BigInt(vaultsValuation.value) +
|
|
206
|
+
BigInt(symbolsValuation.value.value) *
|
|
207
|
+
BigInt(symbolsValuation.value.is_positive ? 1 : -1),
|
|
208
|
+
) / 1e18
|
|
209
|
+
return result
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Valuates only vaults (Sudo SDK specific)
|
|
214
|
+
*/
|
|
215
|
+
public async simValuateVaults(sender: string): Promise<number> {
|
|
216
|
+
const tx = await this.initOracleTxb(
|
|
217
|
+
Object.keys(this.consts.sudoCore.vaults),
|
|
218
|
+
)
|
|
219
|
+
this.valuateVaults(tx)
|
|
220
|
+
|
|
221
|
+
const res = await this.provider.devInspectTransactionBlock({
|
|
222
|
+
transactionBlock: tx,
|
|
223
|
+
sender,
|
|
224
|
+
})
|
|
225
|
+
const vaultsValuation = VaultsValuation.parse(
|
|
226
|
+
new Uint8Array(
|
|
227
|
+
(
|
|
228
|
+
(res.results as any)[(res.results?.length || 0) - 1]
|
|
229
|
+
.mutableReferenceOutputs as any
|
|
230
|
+
)[1][1],
|
|
231
|
+
),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
const result = Number(BigInt(vaultsValuation.value)) / 1e18
|
|
236
|
+
return result
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Valuates market with vaults only
|
|
241
|
+
*/
|
|
242
|
+
public async valuateMarketWithVaultsOnly(): Promise<ISLPMarketValuationInfo> {
|
|
243
|
+
if (!this.consts.sudoCore) {
|
|
244
|
+
throw new Error('Sudo Core configuration not found')
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const marketInfo = await this.getMarketInfo()
|
|
248
|
+
let value = await this.simValuateVaults(this.consts.sudoCore.adminCap)
|
|
249
|
+
let slpPrice = value / marketInfo.lpSupplyWithDecimals
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
marketCap: value,
|
|
253
|
+
price: slpPrice,
|
|
254
|
+
supply: marketInfo.lpSupplyWithDecimals,
|
|
255
|
+
apr: Number(marketInfo.apr),
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Gets SLP market information
|
|
261
|
+
*/
|
|
262
|
+
public async getMarketInfo(): Promise<ISLPMarketInfo> {
|
|
263
|
+
this.validateCache()
|
|
264
|
+
if (this.marketInfoCache) {
|
|
265
|
+
return this.marketInfoCache
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const rawData = await this.provider.getObject({
|
|
269
|
+
id: this.consts.sudoCore.market,
|
|
270
|
+
options: {
|
|
271
|
+
showContent: true,
|
|
272
|
+
},
|
|
273
|
+
})
|
|
274
|
+
const apr = await this.getCumulativeApr()
|
|
275
|
+
return {
|
|
276
|
+
...this.parseMarketInfo(rawData),
|
|
277
|
+
apr,
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Gets SLP vault information
|
|
283
|
+
*/
|
|
284
|
+
public async getVaultInfo(vaultToken: string): Promise<ISLPVaultInfo> {
|
|
285
|
+
this.validateCache()
|
|
286
|
+
if (this.vaultInfoCache[vaultToken]) {
|
|
287
|
+
return this.vaultInfoCache[vaultToken]
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const rawData = await this.provider.getDynamicFieldObject({
|
|
291
|
+
parentId: this.consts.sudoCore.vaultsParent,
|
|
292
|
+
name: {
|
|
293
|
+
type: `${this.consts.sudoCore.package}::market::VaultName<${this.consts.coins[vaultToken].module}>`,
|
|
294
|
+
value: { dummy_field: false },
|
|
295
|
+
},
|
|
296
|
+
})
|
|
297
|
+
return await this.parseVaultInfo(rawData)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Gets SLP symbol information
|
|
302
|
+
*/
|
|
303
|
+
public async getSymbolInfo(indexToken: string, long: boolean): Promise<ISLPSymbolInfo> {
|
|
304
|
+
this.validateCache();
|
|
305
|
+
const symbol = joinSymbol(long ? 'long' : 'short', indexToken);
|
|
306
|
+
if (this.symbolInfoCache[symbol]) {
|
|
307
|
+
return this.symbolInfoCache[symbol];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const rawData = await this.provider.getDynamicFieldObject({
|
|
311
|
+
parentId: this.consts.sudoCore.symbolsParent,
|
|
312
|
+
name: {
|
|
313
|
+
type: `${this.consts.sudoCore.package}::market::SymbolName<${this.consts.coins[indexToken].module
|
|
314
|
+
}, ${this.consts.sudoCore.package}::market::${long ? 'LONG' : 'SHORT'
|
|
315
|
+
}>`,
|
|
316
|
+
value: { dummy_field: false },
|
|
317
|
+
},
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
return await this.parseSymbolInfo(rawData, long)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
public async getPositionInfoList(
|
|
324
|
+
positionCapInfoList: ISLPPositionCapInfo[],
|
|
325
|
+
owner: string
|
|
326
|
+
): Promise<ISLPPositionInfo[]> {
|
|
327
|
+
const positionInfoList: ISLPPositionInfo[] = []
|
|
328
|
+
await Promise.all(
|
|
329
|
+
positionCapInfoList.map(async positionCapInfo => {
|
|
330
|
+
const positionRaw = await this.provider.getDynamicFieldObject({
|
|
331
|
+
parentId: this.consts.sudoCore.positionsParent,
|
|
332
|
+
name: {
|
|
333
|
+
type: `${this.consts.sudoCore.package}::market::PositionName<${positionCapInfo.symbol0
|
|
334
|
+
}, ${positionCapInfo.symbol1}, ${this.consts.sudoCore.package
|
|
335
|
+
}::market::${positionCapInfo.long ? 'LONG' : 'SHORT'}>`,
|
|
336
|
+
value: {
|
|
337
|
+
owner,
|
|
338
|
+
id: positionCapInfo.positionCapId,
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
if (positionRaw?.data?.content) {
|
|
344
|
+
positionInfoList.push(
|
|
345
|
+
await this.parsePositionInfo(
|
|
346
|
+
positionRaw,
|
|
347
|
+
positionCapInfo.positionCapId,
|
|
348
|
+
),
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
}),
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
return positionInfoList.sort((a, b) =>
|
|
355
|
+
a.openTimestamp > b.openTimestamp ? 1 : -1,
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Gets user history for SLP
|
|
362
|
+
*/
|
|
363
|
+
public async getHistory(
|
|
364
|
+
trader: string,
|
|
365
|
+
page: number,
|
|
366
|
+
limit: number,
|
|
367
|
+
orderType?: string,
|
|
368
|
+
symbol?: string
|
|
369
|
+
): Promise<IBaseHistoryResponse> {
|
|
370
|
+
const params = new URLSearchParams({
|
|
371
|
+
trader,
|
|
372
|
+
page: page.toString(),
|
|
373
|
+
limit: limit.toString(),
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
if (orderType) {
|
|
377
|
+
params.append('orderType', orderType)
|
|
378
|
+
}
|
|
379
|
+
if (symbol) {
|
|
380
|
+
params.append('symbol', symbol)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const url = `${this.apiEndpoint}/traderEvents?${params}`
|
|
384
|
+
const res = await fetch(url, {
|
|
385
|
+
method: 'GET',
|
|
386
|
+
headers: {
|
|
387
|
+
'Content-Type': 'application/json',
|
|
388
|
+
},
|
|
389
|
+
})
|
|
390
|
+
const response = await res.json() as any
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
histories: response.data?.histories || [],
|
|
394
|
+
pagination: response.data?.pagination || {
|
|
395
|
+
total: 0,
|
|
396
|
+
page: 1,
|
|
397
|
+
limit: 20,
|
|
398
|
+
pages: 0,
|
|
399
|
+
},
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
public async getStaked(owner: string): Promise<ISLPStaked> {
|
|
404
|
+
let rawCredentialsData: any[] = []
|
|
405
|
+
let queryNextPage = true
|
|
406
|
+
let queryCursor = undefined
|
|
407
|
+
const limit = 50
|
|
408
|
+
|
|
409
|
+
while (queryNextPage) {
|
|
410
|
+
const { data, hasNextPage, nextCursor } = await this.provider.getOwnedObjects({
|
|
411
|
+
owner,
|
|
412
|
+
filter: {
|
|
413
|
+
MoveModule: {
|
|
414
|
+
package: this.consts.sudoStaking.package,
|
|
415
|
+
module: 'pool',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
options: {
|
|
419
|
+
showType: true,
|
|
420
|
+
showContent: true,
|
|
421
|
+
},
|
|
422
|
+
cursor: queryCursor,
|
|
423
|
+
limit,
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
queryNextPage = hasNextPage
|
|
427
|
+
queryCursor = nextCursor!
|
|
428
|
+
if (!data) break
|
|
429
|
+
rawCredentialsData = [...rawCredentialsData, ...data]
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const pool = await this.getStakePool()
|
|
433
|
+
const credentials = rawCredentialsData.map((item: any) =>
|
|
434
|
+
this.parseCredential(item, pool),
|
|
435
|
+
)
|
|
436
|
+
return {
|
|
437
|
+
credentials,
|
|
438
|
+
amount: credentials.reduce(
|
|
439
|
+
(acc: bigint, cur: ISLPCredential) => acc + cur.amount,
|
|
440
|
+
BigInt(0),
|
|
441
|
+
),
|
|
442
|
+
claimable: credentials.reduce(
|
|
443
|
+
(acc: bigint, cur: ISLPCredential) => acc + cur.claimable,
|
|
444
|
+
BigInt(0),
|
|
445
|
+
),
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
public async getStakePool(): Promise<ISLPStakePool> {
|
|
450
|
+
const raw = await this.provider.getObject({
|
|
451
|
+
id: this.consts.sudoStaking.pool,
|
|
452
|
+
options: {
|
|
453
|
+
showContent: true,
|
|
454
|
+
},
|
|
455
|
+
})
|
|
456
|
+
return this.parseStakePool(raw)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
public async fundingFeeRate(indexToken: string, long: boolean, sender: string): Promise<number> {
|
|
460
|
+
const tx = await this.initOracleTxb([indexToken])
|
|
461
|
+
const symbol_ = joinSymbol(long ? 'long' : 'short', indexToken)
|
|
462
|
+
const currentTimestamp = parseInt((+new Date() / 1000).toFixed(0))
|
|
463
|
+
const symbol = tx.moveCall({
|
|
464
|
+
target: `${this.consts.sudoCore.package}::market::symbol`,
|
|
465
|
+
typeArguments: [
|
|
466
|
+
`${this.consts.sudoCore.package}::slp::SLP`,
|
|
467
|
+
this.consts.coins[indexToken].module,
|
|
468
|
+
`${this.consts.sudoCore.package}::market::${long ? 'LONG' : 'SHORT'}`,
|
|
469
|
+
],
|
|
470
|
+
arguments: [tx.object(this.consts.sudoCore.market)],
|
|
471
|
+
})
|
|
472
|
+
const aggPriceConfig = tx.moveCall({
|
|
473
|
+
target: `${this.consts.sudoCore.package}::pool::symbol_price_config`,
|
|
474
|
+
typeArguments: [],
|
|
475
|
+
arguments: [symbol],
|
|
476
|
+
})
|
|
477
|
+
const aggPrice = tx.moveCall({
|
|
478
|
+
target: `${this.consts.sudoCore.package}::agg_price::parse_pyth_feeder_v1_1`,
|
|
479
|
+
typeArguments: [],
|
|
480
|
+
arguments: [
|
|
481
|
+
aggPriceConfig,
|
|
482
|
+
tx.object(this.consts.pythFeeder.feeder[indexToken]),
|
|
483
|
+
tx.pure.u64(currentTimestamp),
|
|
484
|
+
],
|
|
485
|
+
})
|
|
486
|
+
const deltaSize = tx.moveCall({
|
|
487
|
+
target: `${this.consts.sudoCore.package}::pool::symbol_delta_size`,
|
|
488
|
+
typeArguments: [],
|
|
489
|
+
arguments: [symbol, aggPrice, tx.pure.bool(long)],
|
|
490
|
+
})
|
|
491
|
+
const LpSupplyAmount = tx.moveCall({
|
|
492
|
+
target: `${this.consts.sudoCore.package}::market::lp_supply_amount`,
|
|
493
|
+
typeArguments: [`${this.consts.sudoCore.package}::slp::SLP`],
|
|
494
|
+
arguments: [tx.object(this.consts.sudoCore.market)],
|
|
495
|
+
})
|
|
496
|
+
const PnlPerLp = tx.moveCall({
|
|
497
|
+
target: `${this.consts.sudoCore.package}::pool::symbol_pnl_per_lp`,
|
|
498
|
+
typeArguments: [],
|
|
499
|
+
arguments: [symbol, deltaSize, LpSupplyAmount],
|
|
500
|
+
})
|
|
501
|
+
tx.moveCall({
|
|
502
|
+
target: `${this.consts.sudoCore.package}::model::compute_funding_fee_rate`,
|
|
503
|
+
arguments: [
|
|
504
|
+
tx.object(this.consts.sudoCore.symbols[symbol_].fundingFeeModel),
|
|
505
|
+
PnlPerLp,
|
|
506
|
+
tx.pure.u64(8 * 3600),
|
|
507
|
+
],
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
const res: any = await this.provider.devInspectTransactionBlock({
|
|
511
|
+
transactionBlock: tx,
|
|
512
|
+
sender,
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
const de = SRate.parse(
|
|
516
|
+
new Uint8Array(res.results[res.results.length - 1].returnValues[0][0]),
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
return (Number(BigInt(de.value)) / 1e18) * (de.is_positive ? 1 : -1)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
public async rebaseFeeRate(collateralToken: string, increase: boolean, amount: number, sender: string): Promise<number> {
|
|
523
|
+
const tx1 = await this.initOracleTxb(
|
|
524
|
+
Object.keys(this.consts.pythFeeder.feeder),
|
|
525
|
+
)
|
|
526
|
+
this.valuateVaults(tx1)
|
|
527
|
+
const res1 = await this.provider.devInspectTransactionBlock({
|
|
528
|
+
transactionBlock: tx1,
|
|
529
|
+
sender,
|
|
530
|
+
})
|
|
531
|
+
const vaultsValuation = VaultsValuation.parse(
|
|
532
|
+
new Uint8Array(
|
|
533
|
+
(
|
|
534
|
+
(res1.results as any)[(res1.results?.length || 0) - 1]
|
|
535
|
+
.mutableReferenceOutputs as any
|
|
536
|
+
)[1][1],
|
|
537
|
+
),
|
|
538
|
+
)
|
|
539
|
+
const singleVaultValue =
|
|
540
|
+
BigInt(
|
|
541
|
+
// @ts-ignore
|
|
542
|
+
vaultsValuation.handled.find((item: any) =>
|
|
543
|
+
(item.key || '').includes(this.consts.coins[collateralToken].module.slice(2)) || false,
|
|
544
|
+
)?.value.value,
|
|
545
|
+
) + BigInt(Math.floor(amount))
|
|
546
|
+
const allVaultValue = BigInt(vaultsValuation.value) + BigInt(Math.floor(amount))
|
|
547
|
+
const singleVaultWeight = BigInt(
|
|
548
|
+
this.consts.sudoCore.vaults[collateralToken].weight,
|
|
549
|
+
)
|
|
550
|
+
const allVaultWeight = BigInt(vaultsValuation.total_weight)
|
|
551
|
+
const tx2 = new Transaction()
|
|
552
|
+
tx2.moveCall({
|
|
553
|
+
target: `${this.consts.sudoCore.package}::pool::compute_rebase_fee_rate`,
|
|
554
|
+
arguments: [
|
|
555
|
+
tx2.object(this.consts.sudoCore.rebaseFeeModel),
|
|
556
|
+
tx2.pure.bool(increase),
|
|
557
|
+
tx2.pure.u256(singleVaultValue),
|
|
558
|
+
tx2.pure.u256(allVaultValue),
|
|
559
|
+
tx2.pure.u256(singleVaultWeight),
|
|
560
|
+
tx2.pure.u256(allVaultWeight),
|
|
561
|
+
],
|
|
562
|
+
})
|
|
563
|
+
const res2: any = await this.provider.devInspectTransactionBlock({
|
|
564
|
+
transactionBlock: tx2,
|
|
565
|
+
sender,
|
|
566
|
+
})
|
|
567
|
+
const de = Rate.parse(
|
|
568
|
+
new Uint8Array(res2.results[res2.results.length - 1].returnValues[0][0]),
|
|
569
|
+
)
|
|
570
|
+
return Number(BigInt(de)) / 1e18
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
public async reservingFeeRate(collateralToken: string, amount: number, sender: string): Promise<number> {
|
|
574
|
+
const vaultInfo = await this.getVaultInfo(collateralToken)
|
|
575
|
+
const vaultSupply =
|
|
576
|
+
vaultInfo.liquidity +
|
|
577
|
+
vaultInfo.reservedAmount +
|
|
578
|
+
vaultInfo.unrealisedReservingFeeAmount +
|
|
579
|
+
amount
|
|
580
|
+
const utilization = vaultSupply
|
|
581
|
+
? parseInt(
|
|
582
|
+
(((vaultInfo.reservedAmount + amount) / vaultSupply) * 1e18).toFixed(
|
|
583
|
+
0,
|
|
584
|
+
),
|
|
585
|
+
)
|
|
586
|
+
: 0
|
|
587
|
+
const tx = new Transaction()
|
|
588
|
+
tx.moveCall({
|
|
589
|
+
target: `${this.consts.sudoCore.upgradedPackage}::model::compute_reserving_fee_rate`,
|
|
590
|
+
arguments: [
|
|
591
|
+
tx.object(
|
|
592
|
+
this.consts.sudoCore.vaults[collateralToken].reservingFeeModel,
|
|
593
|
+
),
|
|
594
|
+
tx.pure.u128(utilization),
|
|
595
|
+
tx.pure.u64(8 * 3600),
|
|
596
|
+
],
|
|
597
|
+
})
|
|
598
|
+
const res: any = await this.provider.devInspectTransactionBlock({
|
|
599
|
+
transactionBlock: tx,
|
|
600
|
+
sender,
|
|
601
|
+
})
|
|
602
|
+
const de = Rate.parse(
|
|
603
|
+
new Uint8Array(res.results[res.results.length - 1].returnValues[0][0]),
|
|
604
|
+
)
|
|
605
|
+
return Number(BigInt(de)) / 1e18
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
public async getPositionConfig(indexToken: string, long: boolean): Promise<ISLPPositionConfig> {
|
|
609
|
+
this.validateCache()
|
|
610
|
+
const symbol = joinSymbol(long ? 'long' : 'short', indexToken)
|
|
611
|
+
if (this.positionConfigCache[symbol]) {
|
|
612
|
+
return this.positionConfigCache[symbol]
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const rawData = await this.provider.getObject({
|
|
616
|
+
id: this.consts.sudoCore.symbols[symbol].positionConfig,
|
|
617
|
+
options: {
|
|
618
|
+
showContent: true,
|
|
619
|
+
},
|
|
620
|
+
})
|
|
621
|
+
return this.parsePositionConfig(rawData)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
public async getOpenPositions(batchSize: number = 50, symbol: string = 'sui'): Promise<ISLPPositionInfo[]> {
|
|
625
|
+
let positionDynamicFields: DynamicFieldInfo[] = []
|
|
626
|
+
let _continue = true
|
|
627
|
+
let cursor = undefined
|
|
628
|
+
while (_continue) {
|
|
629
|
+
// data here will be a list of dynamic fields containing name and value
|
|
630
|
+
const { data, nextCursor, hasNextPage } =
|
|
631
|
+
await this.provider.getDynamicFields({
|
|
632
|
+
parentId: this.consts.sudoCore.positionsParent,
|
|
633
|
+
cursor,
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
positionDynamicFields = positionDynamicFields.concat(data)
|
|
637
|
+
_continue = hasNextPage
|
|
638
|
+
cursor = nextCursor
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// Filter by symbol if provided
|
|
642
|
+
if (symbol && this.consts.coins[symbol]) {
|
|
643
|
+
const coinModule = symbol === 'sui' ? '0x2::sui::SUI' : this.consts.coins[symbol].module
|
|
644
|
+
positionDynamicFields = positionDynamicFields.filter(field => {
|
|
645
|
+
// Extract the second coin module from PositionName<coin1, coin2, direction>
|
|
646
|
+
const typeStr = field.name?.type
|
|
647
|
+
if (!typeStr) return false
|
|
648
|
+
|
|
649
|
+
const match = typeStr.match(/PositionName<([^,]+),\s*([^,]+),\s*([^>]+)>/)
|
|
650
|
+
if (!match) return false
|
|
651
|
+
|
|
652
|
+
const secondCoin = match[2].trim()
|
|
653
|
+
return secondCoin === coinModule
|
|
654
|
+
})
|
|
655
|
+
} else {
|
|
656
|
+
return []
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// then we query by dynamic field names and order by time
|
|
660
|
+
const positionInfoList: ISLPPositionInfo[] = []
|
|
661
|
+
|
|
662
|
+
for (let i = 0; i < positionDynamicFields.length; i += batchSize) {
|
|
663
|
+
const batch = positionDynamicFields.slice(i, i + batchSize)
|
|
664
|
+
|
|
665
|
+
await Promise.all(
|
|
666
|
+
batch.map(async positionDynamicField => {
|
|
667
|
+
const positionRaw = await this.provider.getDynamicFieldObject({
|
|
668
|
+
parentId: this.consts.sudoCore.positionsParent,
|
|
669
|
+
name: positionDynamicField.name,
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
if (positionRaw?.data?.content) {
|
|
673
|
+
// @ts-ignore
|
|
674
|
+
if (positionRaw?.data?.content?.fields?.value?.fields?.closed) {
|
|
675
|
+
// skip closed positions
|
|
676
|
+
return
|
|
677
|
+
}
|
|
678
|
+
const positionInfo = await this.parsePositionInfo(
|
|
679
|
+
positionRaw,
|
|
680
|
+
positionDynamicField.objectId,
|
|
681
|
+
)
|
|
682
|
+
if (positionInfo) {
|
|
683
|
+
positionInfoList.push(positionInfo)
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}),
|
|
687
|
+
)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return positionInfoList
|
|
691
|
+
.filter(positionInfo => !positionInfo.closed)
|
|
692
|
+
.sort((a, b) => (a.openTimestamp > b.openTimestamp ? 1 : -1))
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
public async getPositionCapInfoList(owner: string): Promise<ISLPPositionCapInfo[]> {
|
|
696
|
+
let cursor: string | undefined | null = undefined
|
|
697
|
+
let hasNextPage = true
|
|
698
|
+
const positionCapInfoList = []
|
|
699
|
+
|
|
700
|
+
while (hasNextPage) {
|
|
701
|
+
const positionCaps = await this.provider.getOwnedObjects({
|
|
702
|
+
owner,
|
|
703
|
+
filter: {
|
|
704
|
+
StructType: `${this.consts.sudoCore.package}::market::PositionCap`,
|
|
705
|
+
},
|
|
706
|
+
options: {
|
|
707
|
+
showType: true,
|
|
708
|
+
},
|
|
709
|
+
cursor,
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
for (const positionCap of positionCaps.data) {
|
|
713
|
+
if (positionCap.data?.type?.includes('PositionCap')) {
|
|
714
|
+
positionCapInfoList.push({
|
|
715
|
+
positionCapId: positionCap.data.objectId,
|
|
716
|
+
symbol0: positionCap.data.type.split('<')[1].split(',')[0].trim(),
|
|
717
|
+
symbol1: positionCap.data.type
|
|
718
|
+
.split('<')[1]
|
|
719
|
+
.split(',')[1]
|
|
720
|
+
.split(',')[0]
|
|
721
|
+
.trim(),
|
|
722
|
+
long: positionCap.data.type.includes('LONG'),
|
|
723
|
+
})
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
hasNextPage = positionCaps.hasNextPage
|
|
728
|
+
cursor = positionCaps.nextCursor
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return positionCapInfoList
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
public async getOrderCapInfoList(owner: string): Promise<ISLPOrderCapInfo[]> {
|
|
735
|
+
let cursor: string | undefined | null = undefined
|
|
736
|
+
let hasNextPage = true
|
|
737
|
+
const orderCapInfoList = []
|
|
738
|
+
|
|
739
|
+
while (hasNextPage) {
|
|
740
|
+
const orderCaps = await this.provider.getOwnedObjects({
|
|
741
|
+
owner,
|
|
742
|
+
filter: {
|
|
743
|
+
StructType: `${this.consts.sudoCore.package}::market::OrderCap`,
|
|
744
|
+
},
|
|
745
|
+
options: {
|
|
746
|
+
showType: true,
|
|
747
|
+
showContent: true,
|
|
748
|
+
},
|
|
749
|
+
cursor,
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
for (const orderCap of orderCaps.data) {
|
|
753
|
+
if (orderCap.data?.type?.includes('OrderCap')) {
|
|
754
|
+
orderCapInfoList.push({
|
|
755
|
+
orderCapId: orderCap.data.objectId,
|
|
756
|
+
symbol0: orderCap.data.type.split('<')[1].split(',')[0].trim(),
|
|
757
|
+
symbol1: orderCap.data.type
|
|
758
|
+
.split('<')[1]
|
|
759
|
+
.split(',')[1]
|
|
760
|
+
.split(',')[0]
|
|
761
|
+
.trim(),
|
|
762
|
+
long: orderCap.data.type.includes('LONG'),
|
|
763
|
+
positionId: (orderCap.data.content as any)?.fields?.position_id,
|
|
764
|
+
})
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
hasNextPage = orderCaps.hasNextPage
|
|
769
|
+
cursor = orderCaps.nextCursor
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return orderCapInfoList
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
public async hasReferral(referree: string): Promise<boolean> {
|
|
776
|
+
const raw = await this.getReferralData(referree)
|
|
777
|
+
return !raw.error
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
public async getReferralData(referree: string): Promise<any> {
|
|
781
|
+
const raw = await this.provider.getDynamicFieldObject({
|
|
782
|
+
parentId: this.consts.sudoCore.referralsParent,
|
|
783
|
+
name: {
|
|
784
|
+
type: 'address',
|
|
785
|
+
value: referree,
|
|
786
|
+
},
|
|
787
|
+
})
|
|
788
|
+
return raw
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Gets user orders for SLP
|
|
793
|
+
*/
|
|
794
|
+
public async getOrderInfoList(
|
|
795
|
+
orderCapInfoList: ISLPOrderCapInfo[],
|
|
796
|
+
owner: string
|
|
797
|
+
): Promise<ISLPOrderInfo[]> {
|
|
798
|
+
const orderInfoList: ISLPOrderInfo[] = []
|
|
799
|
+
await Promise.all(
|
|
800
|
+
orderCapInfoList.map(async orderCapInfo => {
|
|
801
|
+
const orderRaw = await this.provider.getDynamicFieldObject({
|
|
802
|
+
parentId: this.consts.sudoCore.ordersParent,
|
|
803
|
+
name: {
|
|
804
|
+
type: `${this.consts.sudoCore.package}::market::OrderName<${orderCapInfo.symbol0
|
|
805
|
+
}, ${orderCapInfo.symbol1}, ${this.consts.sudoCore.package
|
|
806
|
+
}::market::${orderCapInfo.long ? 'LONG' : 'SHORT'}, ${this.consts.coins['sui'].module
|
|
807
|
+
}>`,
|
|
808
|
+
value: {
|
|
809
|
+
owner,
|
|
810
|
+
id: orderCapInfo.orderCapId,
|
|
811
|
+
position_id: {
|
|
812
|
+
vec: orderCapInfo.positionId ? [orderCapInfo.positionId] : [],
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
},
|
|
816
|
+
})
|
|
817
|
+
orderInfoList.push(
|
|
818
|
+
this.parseOrderInfo(orderRaw, orderCapInfo.orderCapId),
|
|
819
|
+
)
|
|
820
|
+
}),
|
|
821
|
+
)
|
|
822
|
+
return orderInfoList.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1))
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
public async getCumulativeApr() {
|
|
826
|
+
const refetchDate = new Date(Date.now() - 3600_000)
|
|
827
|
+
// fetch new every hour
|
|
828
|
+
if (
|
|
829
|
+
!aprResponse?.generatedAt ||
|
|
830
|
+
(aprResponse?.generatedAt &&
|
|
831
|
+
refetchDate > new Date(aprResponse?.generatedAt))
|
|
832
|
+
) {
|
|
833
|
+
try {
|
|
834
|
+
const url = `${this.apiEndpoint}/cumulativeApr`
|
|
835
|
+
const res = await fetch(url, {
|
|
836
|
+
method: 'GET',
|
|
837
|
+
headers: {
|
|
838
|
+
'Content-Type': 'application/json',
|
|
839
|
+
},
|
|
840
|
+
})
|
|
841
|
+
const data = await res.json()
|
|
842
|
+
aprResponse = { ...data }
|
|
843
|
+
return data.cumulativeApr
|
|
844
|
+
} catch (e) {
|
|
845
|
+
console.error('Failed to get cumulative APR')
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
return 0
|
|
849
|
+
} else {
|
|
850
|
+
return aprResponse.apr
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Private helper methods
|
|
855
|
+
private calculatePositionFundingFee(position: ISLPPositionInfo, symbol: ISLPSymbolInfo, model: ISLPFundingFeeModel, price: number, lpSupplyAmount: number, timestamp: number): number {
|
|
856
|
+
const accFundingRate = this.calcAccFundingFeeRate(symbol, model, price, lpSupplyAmount, timestamp, position.long);
|
|
857
|
+
return position.fundingFeeValue + (accFundingRate - position.lastFundingRate) * position.positionSize;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
private calcAccFundingFeeRate(symbol: ISLPSymbolInfo, model: ISLPFundingFeeModel, price: number, lpSupplyAmount: number, timestamp: number, isLong: boolean): number {
|
|
861
|
+
if (symbol.lastUpdate > 0) {
|
|
862
|
+
const elapsed = timestamp - symbol.lastUpdate;
|
|
863
|
+
if (elapsed > 0) {
|
|
864
|
+
const deltaSize = this.calcDeltaSize(symbol, price, isLong);
|
|
865
|
+
const pnlPerLp = (symbol.realisedPnl + symbol.unrealisedFundingFeeValue + deltaSize) / lpSupplyAmount;
|
|
866
|
+
return symbol.accFundingRate + this.calcFundingFeeRate(model, pnlPerLp, elapsed);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return symbol.accFundingRate;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
private calcDeltaSize(symbol: ISLPSymbolInfo, price: number, isLong: boolean): number {
|
|
873
|
+
const latestSize = symbol.openingAmount * price;
|
|
874
|
+
return isLong ? symbol.openingSize - latestSize : latestSize - symbol.openingSize;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
private calcFundingFeeRate(model: ISLPFundingFeeModel, pnlPerRate: number, elapsed: number): number {
|
|
878
|
+
const dailyRate = Math.min(model.multiplier * Math.abs(pnlPerRate), model.max);
|
|
879
|
+
const secondsRate = dailyRate * elapsed / SECONDS_PER_EIGHT_HOUR;
|
|
880
|
+
return pnlPerRate >= 0 ? -secondsRate : secondsRate;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
private calculatePositionReserveFee(position: ISLPPositionInfo, vault: ISLPVaultInfo, model: ISLPReservingFeeModel, timestamp: number): number {
|
|
884
|
+
const accReservingRate = this.calcAccReservingFeeRate(vault, model, timestamp);
|
|
885
|
+
return position.reservingFeeAmount + (accReservingRate - position.lastReservingRate) * position.collateralAmount;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
private calcAccReservingFeeRate(vault: ISLPVaultInfo, model: ISLPReservingFeeModel, timestamp: number): number {
|
|
889
|
+
if (vault.lastUpdate > 0) {
|
|
890
|
+
const elapsed = timestamp - vault.lastUpdate;
|
|
891
|
+
if (elapsed > 0) {
|
|
892
|
+
const utilization = this.vaultUtilization(vault);
|
|
893
|
+
return vault.accReservingRate + this.calcReservingFeeRate(model, utilization, elapsed);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return vault.accReservingRate;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
private vaultUtilization(vault: ISLPVaultInfo): number {
|
|
900
|
+
return vault.liquidity > 0 ? vault.reservedAmount / vault.liquidity : 0;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
private calcReservingFeeRate(model: ISLPReservingFeeModel, utilization: number, elapsed: number): number {
|
|
904
|
+
return model.multiplier * utilization * elapsed / SECONDS_PER_EIGHT_HOUR
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
private parsePositionConfig(raw: any): ISLPPositionConfig {
|
|
908
|
+
const positionConfigFields = raw.data.content.fields.inner.fields
|
|
909
|
+
|
|
910
|
+
return {
|
|
911
|
+
decreaseFeeBps: parseValue(positionConfigFields.decrease_fee_bps),
|
|
912
|
+
liquidationBonus: parseValue(positionConfigFields.liquidation_bonus),
|
|
913
|
+
liquidationThreshold: parseValue(
|
|
914
|
+
positionConfigFields.liquidation_threshold,
|
|
915
|
+
),
|
|
916
|
+
maxLeverage: parseValue(positionConfigFields.max_leverage),
|
|
917
|
+
minHoldingDuration: parseValue(positionConfigFields.min_holding_duration),
|
|
918
|
+
openFeeBps: parseValue(positionConfigFields.open_fee_bps),
|
|
919
|
+
maxReservedMultiplier: parseValue(
|
|
920
|
+
positionConfigFields.max_reserved_multiplier,
|
|
921
|
+
),
|
|
922
|
+
minCollateralValue: parseValue(positionConfigFields.min_collateral_value),
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
private parseRebaseFeeModel(raw: any): ISLPRebaseFeeModel {
|
|
927
|
+
const { fields } = raw.data.content
|
|
928
|
+
|
|
929
|
+
return {
|
|
930
|
+
base: parseValue(fields.base),
|
|
931
|
+
multiplier: parseValue(fields.multiplier),
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
private parseFundingFeeModel(raw: any): ISLPFundingFeeModel {
|
|
936
|
+
const { fields } = raw.data.content
|
|
937
|
+
|
|
938
|
+
return {
|
|
939
|
+
multiplier: parseValue(fields.multiplier),
|
|
940
|
+
max: parseValue(fields.max),
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
private parseMarketInfo(raw: any): ISLPMarketInfo {
|
|
945
|
+
const content = raw.data.content.fields
|
|
946
|
+
|
|
947
|
+
return {
|
|
948
|
+
lpSupply: content.lp_supply.fields.value,
|
|
949
|
+
positionId: content.positions.fields.id.id,
|
|
950
|
+
vaultId: content.vaults.fields.id.id,
|
|
951
|
+
symbolId: content.symbols.fields.id.id,
|
|
952
|
+
lpSupplyWithDecimals:
|
|
953
|
+
content.lp_supply.fields.value / 10 ** SLP_TOKEN_DECIMALS,
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
private async parseVaultInfo(raw: any): Promise<ISLPVaultInfo> {
|
|
958
|
+
const vaultFields = raw.data.content.fields.value.fields
|
|
959
|
+
const reservingFeeModelAddr = vaultFields.reserving_fee_model
|
|
960
|
+
const reservingFeeModelRaw = await this.provider.getObject({
|
|
961
|
+
id: reservingFeeModelAddr,
|
|
962
|
+
options: {
|
|
963
|
+
showContent: true,
|
|
964
|
+
},
|
|
965
|
+
})
|
|
966
|
+
const reservingFeeModel = this.parseReservingFeeModel(reservingFeeModelRaw)
|
|
967
|
+
|
|
968
|
+
return {
|
|
969
|
+
liquidity: parseValue(vaultFields.liquidity),
|
|
970
|
+
reservedAmount: parseValue(vaultFields.reserved_amount),
|
|
971
|
+
unrealisedReservingFeeAmount: parseValue(
|
|
972
|
+
vaultFields.unrealised_reserving_fee_amount,
|
|
973
|
+
),
|
|
974
|
+
accReservingRate: parseValue(vaultFields.acc_reserving_rate),
|
|
975
|
+
enabled: vaultFields.enabled,
|
|
976
|
+
weight: parseValue(vaultFields.weight),
|
|
977
|
+
lastUpdate: parseValue(vaultFields.last_update),
|
|
978
|
+
reservingFeeModel,
|
|
979
|
+
priceConfig: {
|
|
980
|
+
maxInterval: parseValue(vaultFields.price_config.fields.max_interval),
|
|
981
|
+
maxConfidence: parseValue(vaultFields.price_config.fields.max_confidence),
|
|
982
|
+
precision: parseValue(vaultFields.price_config.fields.precision),
|
|
983
|
+
feeder: vaultFields.price_config.fields.feeder,
|
|
984
|
+
},
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
private parseReservingFeeModel(raw: any): { multiplier: number } {
|
|
989
|
+
const content = raw.data.content.fields
|
|
990
|
+
return {
|
|
991
|
+
multiplier: parseValue(content.multiplier),
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
private async parseSymbolInfo(raw: any, long: boolean): Promise<ISLPSymbolInfo> {
|
|
996
|
+
const fields = raw.data.content.fields.value.fields
|
|
997
|
+
const fundingFeeModelAddr = fields.funding_fee_model
|
|
998
|
+
const fundingFeeModelRaw = await this.provider.getObject({
|
|
999
|
+
id: fundingFeeModelAddr,
|
|
1000
|
+
options: {
|
|
1001
|
+
showContent: true,
|
|
1002
|
+
},
|
|
1003
|
+
})
|
|
1004
|
+
const fundingFeeModel = this.parseFundingFeeModel(fundingFeeModelRaw)
|
|
1005
|
+
|
|
1006
|
+
return {
|
|
1007
|
+
openingSize: parseValue(fields.opening_size),
|
|
1008
|
+
openingAmount: parseValue(fields.opening_amount),
|
|
1009
|
+
accFundingRate: parseValue(fields.acc_funding_rate),
|
|
1010
|
+
realisedPnl: parseValue(fields.realised_pnl),
|
|
1011
|
+
unrealisedFundingFeeValue: parseValue(
|
|
1012
|
+
fields.unrealised_funding_fee_value,
|
|
1013
|
+
),
|
|
1014
|
+
openEnabled: fields.open_enabled,
|
|
1015
|
+
liquidateEnabled: fields.liquidate_enabled,
|
|
1016
|
+
decreaseEnabled: fields.decrease_enabled,
|
|
1017
|
+
lastUpdate: parseValue(fields.last_update),
|
|
1018
|
+
fundingFeeModel,
|
|
1019
|
+
long,
|
|
1020
|
+
priceConfig: {
|
|
1021
|
+
maxInterval: parseValue(fields.price_config.fields.max_interval),
|
|
1022
|
+
maxConfidence: parseValue(fields.price_config.fields.max_confidence),
|
|
1023
|
+
precision: parseValue(fields.price_config.fields.precision),
|
|
1024
|
+
feeder: fields.price_config.fields.feeder,
|
|
1025
|
+
},
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
private async parsePositionInfo(raw: any, id_: string): Promise<ISLPPositionInfo> {
|
|
1030
|
+
const content = raw.data.content
|
|
1031
|
+
const fields = content.fields
|
|
1032
|
+
const positionFields = fields.value.fields
|
|
1033
|
+
const dataType = fields.name.type
|
|
1034
|
+
|
|
1035
|
+
const positionInfo = {
|
|
1036
|
+
id: id_,
|
|
1037
|
+
long: dataType.includes('::market::LONG'),
|
|
1038
|
+
owner: fields.name.fields.owner,
|
|
1039
|
+
version: parseInt(raw.data.version, 10),
|
|
1040
|
+
collateralToken: suiSymbolToSymbol(
|
|
1041
|
+
dataType.split('<')[1].split(',')[0].trim(),
|
|
1042
|
+
this.consts,
|
|
1043
|
+
),
|
|
1044
|
+
indexToken: suiSymbolToSymbol(dataType.split(',')[1].trim(), this.consts),
|
|
1045
|
+
collateralAmount: parseValue(positionFields.collateral),
|
|
1046
|
+
positionAmount: parseValue(positionFields.position_amount),
|
|
1047
|
+
reservedAmount: parseValue(positionFields.reserved),
|
|
1048
|
+
positionSize: parseValue(positionFields.position_size),
|
|
1049
|
+
lastFundingRate: parseValue(positionFields.last_funding_rate),
|
|
1050
|
+
lastReservingRate: parseValue(positionFields.last_reserving_rate),
|
|
1051
|
+
reservingFeeAmount: parseValue(positionFields.reserving_fee_amount),
|
|
1052
|
+
fundingFeeValue: parseValue(positionFields.funding_fee_value),
|
|
1053
|
+
closed: positionFields.closed,
|
|
1054
|
+
openTimestamp: parseValue(positionFields.open_timestamp),
|
|
1055
|
+
protocol: 'sudo',
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if (!positionFields.closed) {
|
|
1059
|
+
try {
|
|
1060
|
+
positionInfo.reservingFeeAmount = this.calculatePositionReserveFee(
|
|
1061
|
+
positionInfo,
|
|
1062
|
+
await this.getVaultInfo(positionInfo.collateralToken),
|
|
1063
|
+
(await this.getVaultInfo(positionInfo.collateralToken)).reservingFeeModel,
|
|
1064
|
+
Date.now() / 1000
|
|
1065
|
+
)
|
|
1066
|
+
positionInfo.fundingFeeValue = this.calculatePositionFundingFee(
|
|
1067
|
+
positionInfo,
|
|
1068
|
+
await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long),
|
|
1069
|
+
(await this.getSymbolInfo(positionInfo.indexToken, positionInfo.long)).fundingFeeModel,
|
|
1070
|
+
(await this.getOraclePrice(positionInfo.indexToken)).getPriceUnchecked().getPriceAsNumberUnchecked(),
|
|
1071
|
+
(await this.getMarketInfo()).lpSupplyWithDecimals,
|
|
1072
|
+
Date.now() / 1000
|
|
1073
|
+
)
|
|
1074
|
+
} catch (e) {
|
|
1075
|
+
console.error(e)
|
|
1076
|
+
positionInfo.reservingFeeAmount = 0
|
|
1077
|
+
positionInfo.fundingFeeValue = 0
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
return positionInfo
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
private parseOrderInfo(raw: any, capId: string): ISLPOrderInfo {
|
|
1085
|
+
let content = raw.data.content
|
|
1086
|
+
let fields = content.fields.value.fields
|
|
1087
|
+
|
|
1088
|
+
// Extract tokens from dataType
|
|
1089
|
+
let dataType = content.type
|
|
1090
|
+
|
|
1091
|
+
const orderType = content.fields.value.type.includes('OpenPositionOrder')
|
|
1092
|
+
? 'OPEN_POSITION'
|
|
1093
|
+
: 'DECREASE_POSITION'
|
|
1094
|
+
|
|
1095
|
+
let ret: ISLPOrderInfo = {
|
|
1096
|
+
id: content.fields.id.id,
|
|
1097
|
+
capId,
|
|
1098
|
+
executed: fields.executed,
|
|
1099
|
+
owner: content.fields.name.fields.owner,
|
|
1100
|
+
collateralToken: suiSymbolToSymbol(
|
|
1101
|
+
dataType.split('<')[2].split(',')[0].trim(),
|
|
1102
|
+
this.consts,
|
|
1103
|
+
),
|
|
1104
|
+
indexToken: suiSymbolToSymbol(dataType.split(',')[1].trim(), this.consts),
|
|
1105
|
+
feeToken: suiSymbolToSymbol(
|
|
1106
|
+
dataType.split(',')[3].split('>')[0].trim(),
|
|
1107
|
+
this.consts,
|
|
1108
|
+
),
|
|
1109
|
+
indexPrice: decimalToObject(fields.limited_index_price.fields),
|
|
1110
|
+
collateralPriceThreshold: parseValue(fields.collateral_price_threshold),
|
|
1111
|
+
feeAmount: BigInt(fields.fee),
|
|
1112
|
+
long: dataType.includes('::market::LONG'),
|
|
1113
|
+
orderType,
|
|
1114
|
+
createdAt: parseValue(fields.created_at),
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
if (orderType === 'OPEN_POSITION') {
|
|
1118
|
+
ret.openOrder = {
|
|
1119
|
+
reserveAmount: BigInt(fields.reserve_amount),
|
|
1120
|
+
collateralAmount: BigInt(fields.collateral),
|
|
1121
|
+
openAmount: BigInt(fields.open_amount),
|
|
1122
|
+
}
|
|
1123
|
+
} else {
|
|
1124
|
+
ret.decreaseOrder = {
|
|
1125
|
+
decreaseAmount: BigInt(fields.decrease_amount),
|
|
1126
|
+
takeProfit: fields.take_profit,
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
return ret
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
private parseCredential(raw: any, pool: ISLPStakePool): ISLPCredential {
|
|
1134
|
+
const stakedAmount = BigInt(raw.data.content.fields.stake)
|
|
1135
|
+
const accRewardPerShare = BigInt(
|
|
1136
|
+
raw.data.content.fields.acc_reward_per_share,
|
|
1137
|
+
)
|
|
1138
|
+
return {
|
|
1139
|
+
id: raw.data.objectId,
|
|
1140
|
+
lockUntil: parseValue(raw.data.content.fields.lock_until),
|
|
1141
|
+
amount: stakedAmount,
|
|
1142
|
+
accRewardPerShare,
|
|
1143
|
+
claimable:
|
|
1144
|
+
((pool.accRewardPerShare - accRewardPerShare) * stakedAmount) /
|
|
1145
|
+
BigInt(1e18),
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
private parseStakePool(raw: any): ISLPStakePool {
|
|
1150
|
+
const content = raw.data.content.fields
|
|
1151
|
+
const pool = {
|
|
1152
|
+
id: content.id.id,
|
|
1153
|
+
enabled: content.enabled,
|
|
1154
|
+
lastUpdatedTime: parseValue(content.last_updated_time),
|
|
1155
|
+
stakedAmount: BigInt(content.staked_amount),
|
|
1156
|
+
reward: BigInt(content.reward),
|
|
1157
|
+
startTime: parseValue(content.start_time),
|
|
1158
|
+
endTime: parseValue(content.end_time),
|
|
1159
|
+
accRewardPerShare: BigInt(content.acc_reward_per_share),
|
|
1160
|
+
lockDuration: parseValue(content.lock_duration),
|
|
1161
|
+
}
|
|
1162
|
+
this.refreshPool(pool, Math.floor(Date.now() / 1000))
|
|
1163
|
+
return pool
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
private refreshPool(pool: ISLPStakePool, timestamp: number): void {
|
|
1167
|
+
if (timestamp === pool.lastUpdatedTime || timestamp < pool.startTime) {
|
|
1168
|
+
return
|
|
1169
|
+
}
|
|
1170
|
+
if (
|
|
1171
|
+
pool.lastUpdatedTime === pool.endTime ||
|
|
1172
|
+
pool.stakedAmount === BigInt(0)
|
|
1173
|
+
) {
|
|
1174
|
+
return
|
|
1175
|
+
}
|
|
1176
|
+
if (timestamp > pool.endTime) {
|
|
1177
|
+
timestamp = pool.endTime
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
const rewardAmount =
|
|
1181
|
+
(pool.reward * BigInt(timestamp - pool.lastUpdatedTime)) /
|
|
1182
|
+
BigInt(pool.endTime - pool.lastUpdatedTime)
|
|
1183
|
+
|
|
1184
|
+
pool.lastUpdatedTime = timestamp
|
|
1185
|
+
const rewardPerShare = (rewardAmount * BigInt(1e18)) / pool.stakedAmount
|
|
1186
|
+
pool.accRewardPerShare += rewardPerShare
|
|
1187
|
+
}
|
|
1188
|
+
}
|