@alphafi/alphafi-sdk 0.0.2
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/.babelrc +6 -0
- package/.eslintrc.json +30 -0
- package/README.md +1 -0
- package/dist/common/cetus_mainnet_config.d.ts +3 -0
- package/dist/common/cetus_mainnet_config.d.ts.map +1 -0
- package/dist/common/coins.d.ts +11 -0
- package/dist/common/coins.d.ts.map +1 -0
- package/dist/common/constants.d.ts +294 -0
- package/dist/common/constants.d.ts.map +1 -0
- package/dist/common/maps.d.ts +30 -0
- package/dist/common/maps.d.ts.map +1 -0
- package/dist/common/pyth.d.ts +7 -0
- package/dist/common/pyth.d.ts.map +1 -0
- package/dist/common/types.d.ts +331 -0
- package/dist/common/types.d.ts.map +1 -0
- package/dist/functions.d.ts +20 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/getVaultBalances.d.ts +5 -0
- package/dist/getVaultBalances.d.ts.map +1 -0
- package/dist/getVaults.d.ts +3 -0
- package/dist/getVaults.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.LICENSE.txt +28 -0
- package/dist/index.js.map +1 -0
- package/dist/portfolioAmount.d.ts +36 -0
- package/dist/portfolioAmount.d.ts.map +1 -0
- package/dist/price.d.ts +19 -0
- package/dist/price.d.ts.map +1 -0
- package/jest.config.js +14 -0
- package/package.json +51 -0
- package/src/common/cetus_mainnet_config.ts +72 -0
- package/src/common/coins.ts +126 -0
- package/src/common/constants.ts +820 -0
- package/src/common/maps.ts +200 -0
- package/src/common/pyth.ts +23 -0
- package/src/common/types.ts +446 -0
- package/src/functions.ts +299 -0
- package/src/getVaultBalances.ts +128 -0
- package/src/getVaults.ts +63 -0
- package/src/index.ts +13 -0
- package/src/portfolioAmount.ts +365 -0
- package/src/price.ts +397 -0
- package/tsconfig.json +21 -0
- package/webpack.config.js +59 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { SuiClient } from "@mysten/sui/client";
|
|
2
|
+
import Decimal from "decimal.js";
|
|
3
|
+
import { coins, poolTokenMap } from "./common/coins";
|
|
4
|
+
import { poolInfo, poolPairMap } from "./common/maps";
|
|
5
|
+
import { PythPriceIdPair } from "./common/pyth";
|
|
6
|
+
import {
|
|
7
|
+
AlphaPoolType,
|
|
8
|
+
CoinName,
|
|
9
|
+
PoolName,
|
|
10
|
+
PoolType,
|
|
11
|
+
SimpleCache,
|
|
12
|
+
} from "./common/types";
|
|
13
|
+
import {
|
|
14
|
+
getCoinAmountsFromLiquidity,
|
|
15
|
+
getPoolExchangeRate,
|
|
16
|
+
getReceipts,
|
|
17
|
+
} from "./functions";
|
|
18
|
+
import { getAlphaPrice, getLatestPrice } from "./price";
|
|
19
|
+
|
|
20
|
+
export async function getAlphaPortfolioAmount(
|
|
21
|
+
poolName: PoolName,
|
|
22
|
+
options: { suiClient: SuiClient; address: string; isLocked?: boolean },
|
|
23
|
+
) {
|
|
24
|
+
const receipts = await getReceipts(poolName, options);
|
|
25
|
+
const pool = await getPool(poolName, { suiClient: options.suiClient });
|
|
26
|
+
if (!pool) {
|
|
27
|
+
throw new Error("Pool not found");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const exchangeRate = await getPoolExchangeRate(poolName, {
|
|
31
|
+
suiClient: options.suiClient,
|
|
32
|
+
});
|
|
33
|
+
let totalXTokens = new Decimal(0);
|
|
34
|
+
if (!exchangeRate) {
|
|
35
|
+
return "0"; // if pool has 0 xtokens
|
|
36
|
+
}
|
|
37
|
+
if (options.isLocked === true) {
|
|
38
|
+
receipts.forEach(async (receipt) => {
|
|
39
|
+
const xTokens = new Decimal(receipt.content.fields.xTokenBalance);
|
|
40
|
+
const bal = new Decimal(
|
|
41
|
+
Number(receipt.content.fields.unlocked_xtokens) *
|
|
42
|
+
exchangeRate.toNumber(),
|
|
43
|
+
);
|
|
44
|
+
totalXTokens = totalXTokens.add(xTokens.sub(bal));
|
|
45
|
+
});
|
|
46
|
+
} else if (options.isLocked === false) {
|
|
47
|
+
receipts.forEach((receipt) => {
|
|
48
|
+
const bal = new Decimal(
|
|
49
|
+
Number(receipt.content.fields.unlocked_xtokens) *
|
|
50
|
+
exchangeRate.toNumber(),
|
|
51
|
+
);
|
|
52
|
+
totalXTokens = totalXTokens.add(bal);
|
|
53
|
+
});
|
|
54
|
+
} else {
|
|
55
|
+
receipts.forEach((receipt) => {
|
|
56
|
+
const xTokens = receipt.content.fields.xTokenBalance;
|
|
57
|
+
totalXTokens = totalXTokens.add(xTokens);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (totalXTokens.gt(0)) {
|
|
61
|
+
const poolExchangeRate = await getPoolExchangeRate(poolName, options);
|
|
62
|
+
if (poolExchangeRate) {
|
|
63
|
+
const tokens = totalXTokens.div(1e9).mul(poolExchangeRate);
|
|
64
|
+
return `${tokens}`;
|
|
65
|
+
} else {
|
|
66
|
+
console.error(`Could not get poolExchangeRate for poolName: ${poolName}`);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
return "0";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function getAlphaPortfolioAmountInUSD(
|
|
74
|
+
poolName: PoolName,
|
|
75
|
+
options: { suiClient: SuiClient; address: string; isLocked?: boolean },
|
|
76
|
+
) {
|
|
77
|
+
const tokens = await getAlphaPortfolioAmount(poolName, options);
|
|
78
|
+
const priceOfAlpha = await getAlphaPrice();
|
|
79
|
+
if (priceOfAlpha && tokens) {
|
|
80
|
+
let amount = new Decimal(tokens);
|
|
81
|
+
amount = amount.mul(priceOfAlpha);
|
|
82
|
+
return amount.toString();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const portfolioAmountCache = new SimpleCache<[number, number]>();
|
|
87
|
+
const portfolioAmountPromiseCache = new SimpleCache<
|
|
88
|
+
Promise<[number, number]>
|
|
89
|
+
>();
|
|
90
|
+
export async function getPortfolioAmount(
|
|
91
|
+
poolName: PoolName,
|
|
92
|
+
options: { suiClient: SuiClient; address: string; isLocked?: boolean },
|
|
93
|
+
ignoreCache: boolean = false,
|
|
94
|
+
): Promise<[number, number] | undefined> {
|
|
95
|
+
let portfolioAmount: [number, number] = [0, 0];
|
|
96
|
+
const portfolioAmountCacheKey = `getPortfolioAmount:${poolName}-${options.address}}`;
|
|
97
|
+
if (ignoreCache) {
|
|
98
|
+
portfolioAmountCache.delete(portfolioAmountCacheKey);
|
|
99
|
+
portfolioAmountPromiseCache.delete(portfolioAmountCacheKey);
|
|
100
|
+
}
|
|
101
|
+
const cachedResponse = portfolioAmountCache.get(portfolioAmountCacheKey);
|
|
102
|
+
|
|
103
|
+
if (cachedResponse) {
|
|
104
|
+
return cachedResponse;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let cachedPromise = portfolioAmountPromiseCache.get(portfolioAmountCacheKey);
|
|
108
|
+
|
|
109
|
+
if (!cachedPromise) {
|
|
110
|
+
cachedPromise = (async () => {
|
|
111
|
+
const receipts = await getReceipts(poolName, options);
|
|
112
|
+
let totalXTokens = new Decimal(0);
|
|
113
|
+
if (receipts) {
|
|
114
|
+
receipts.forEach((receipt) => {
|
|
115
|
+
const xTokens = receipt.content.fields.xTokenBalance;
|
|
116
|
+
totalXTokens = totalXTokens.add(xTokens);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (totalXTokens.gt(0)) {
|
|
121
|
+
const poolExchangeRate = await getPoolExchangeRate(poolName, options);
|
|
122
|
+
if (poolExchangeRate) {
|
|
123
|
+
const tokens = totalXTokens.mul(poolExchangeRate);
|
|
124
|
+
const poolTokenAmounts = await getCoinAmountsFromLiquidity(
|
|
125
|
+
poolName,
|
|
126
|
+
tokens.toNumber(),
|
|
127
|
+
{ suiClient: options.suiClient },
|
|
128
|
+
);
|
|
129
|
+
portfolioAmount = poolTokenAmounts;
|
|
130
|
+
} else {
|
|
131
|
+
console.error(
|
|
132
|
+
`Could not get poolExchangeRate for poolName: ${poolName}`,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
portfolioAmount = [0, 0];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
portfolioAmountCache.set(portfolioAmountCacheKey, portfolioAmount);
|
|
140
|
+
portfolioAmountPromiseCache.delete(portfolioAmountCacheKey); // Remove the promise from cache
|
|
141
|
+
return portfolioAmount;
|
|
142
|
+
})().catch((error) => {
|
|
143
|
+
portfolioAmountPromiseCache.delete(portfolioAmountCacheKey); // Remove the promise from cache
|
|
144
|
+
throw error;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
portfolioAmountPromiseCache.set(portfolioAmountCacheKey, cachedPromise);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return cachedPromise;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function getPortfolioAmountInUSD(
|
|
154
|
+
poolName: PoolName,
|
|
155
|
+
options: { suiClient: SuiClient; address: string; isLocked?: boolean },
|
|
156
|
+
ignoreCache: boolean = false,
|
|
157
|
+
): Promise<string | undefined> {
|
|
158
|
+
if (
|
|
159
|
+
poolName === "ALPHA-SUI" ||
|
|
160
|
+
poolName === "USDT-USDC" ||
|
|
161
|
+
poolName === "HASUI-SUI" ||
|
|
162
|
+
poolName === "USDY-USDC" ||
|
|
163
|
+
poolName === "USDC-SUI" ||
|
|
164
|
+
poolName === "WETH-USDC" ||
|
|
165
|
+
poolName === "USDC-WBTC" ||
|
|
166
|
+
poolName === "NAVX-SUI"
|
|
167
|
+
) {
|
|
168
|
+
const amounts = await getPortfolioAmount(poolName, options, ignoreCache);
|
|
169
|
+
if (amounts !== undefined) {
|
|
170
|
+
const ten = new Decimal(10);
|
|
171
|
+
const pool1 = poolPairMap[poolName].pool1 as CoinName;
|
|
172
|
+
const pool2 = poolPairMap[poolName].pool2 as CoinName;
|
|
173
|
+
const amount0 = new Decimal(amounts[0]).div(
|
|
174
|
+
ten.pow(coins[pool1 as CoinName].expo),
|
|
175
|
+
);
|
|
176
|
+
const amount1 = new Decimal(amounts[1]).div(
|
|
177
|
+
ten.pow(coins[pool2 as CoinName].expo),
|
|
178
|
+
);
|
|
179
|
+
const tokens = poolName.split("-");
|
|
180
|
+
const priceOfCoin0 = await getLatestPrice(
|
|
181
|
+
`${tokens[0]}/USD` as PythPriceIdPair,
|
|
182
|
+
);
|
|
183
|
+
const priceOfCoin1 = await getLatestPrice(
|
|
184
|
+
`${tokens[1]}/USD` as PythPriceIdPair,
|
|
185
|
+
);
|
|
186
|
+
if (priceOfCoin0 && priceOfCoin1) {
|
|
187
|
+
const amount = amount0.mul(priceOfCoin0).add(amount1.mul(priceOfCoin1));
|
|
188
|
+
return amount.toString();
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
console.error(
|
|
192
|
+
`getPortfolioAmountInUSD is not implemented for poolName: ${poolName}`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return "0";
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const singleAssetPortfolioAmountCache = new SimpleCache<number>();
|
|
200
|
+
const singleAssetPortfolioAmountPromiseCache = new SimpleCache<
|
|
201
|
+
Promise<number>
|
|
202
|
+
>();
|
|
203
|
+
export async function getSingleAssetPortfolioAmount(
|
|
204
|
+
poolName: PoolName,
|
|
205
|
+
options: { suiClient: SuiClient; address: string; isLocked?: boolean },
|
|
206
|
+
ignoreCache: boolean = false,
|
|
207
|
+
) {
|
|
208
|
+
let portfolioAmount: number = 0;
|
|
209
|
+
const portfolioAmountCacheKey = `getPortfolioAmount:${poolName}-${options.address}}`;
|
|
210
|
+
if (ignoreCache) {
|
|
211
|
+
singleAssetPortfolioAmountCache.delete(portfolioAmountCacheKey);
|
|
212
|
+
singleAssetPortfolioAmountPromiseCache.delete(portfolioAmountCacheKey);
|
|
213
|
+
}
|
|
214
|
+
const cachedResponse = singleAssetPortfolioAmountCache.get(
|
|
215
|
+
portfolioAmountCacheKey,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
if (cachedResponse) {
|
|
219
|
+
return cachedResponse;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let cachedPromise = singleAssetPortfolioAmountPromiseCache.get(
|
|
223
|
+
portfolioAmountCacheKey,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
if (!cachedPromise) {
|
|
227
|
+
cachedPromise = (async () => {
|
|
228
|
+
const receipts = await getReceipts(poolName, options, ignoreCache);
|
|
229
|
+
let totalXTokens = new Decimal(0);
|
|
230
|
+
if (receipts) {
|
|
231
|
+
receipts.forEach((receipt) => {
|
|
232
|
+
const xTokens = receipt.content.fields.xTokenBalance;
|
|
233
|
+
totalXTokens = totalXTokens.add(xTokens);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (totalXTokens.gt(0)) {
|
|
237
|
+
const poolExchangeRate = await getPoolExchangeRate(
|
|
238
|
+
poolName,
|
|
239
|
+
options,
|
|
240
|
+
ignoreCache,
|
|
241
|
+
);
|
|
242
|
+
if (poolExchangeRate) {
|
|
243
|
+
let tokens = totalXTokens.mul(poolExchangeRate);
|
|
244
|
+
tokens = tokens.div(
|
|
245
|
+
Math.pow(10, 9 - coins[poolTokenMap[poolName].coinName].expo),
|
|
246
|
+
);
|
|
247
|
+
portfolioAmount = tokens.toNumber();
|
|
248
|
+
} else {
|
|
249
|
+
console.error(
|
|
250
|
+
`Could not get poolExchangeRate for poolName: ${poolName}`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
portfolioAmount = 0;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
singleAssetPortfolioAmountCache.set(
|
|
258
|
+
portfolioAmountCacheKey,
|
|
259
|
+
portfolioAmount,
|
|
260
|
+
);
|
|
261
|
+
singleAssetPortfolioAmountPromiseCache.delete(portfolioAmountCacheKey); // Remove the promise from cache
|
|
262
|
+
return portfolioAmount;
|
|
263
|
+
})().catch((error) => {
|
|
264
|
+
singleAssetPortfolioAmountPromiseCache.delete(portfolioAmountCacheKey); // Remove the promise from cache
|
|
265
|
+
throw error;
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
singleAssetPortfolioAmountPromiseCache.set(
|
|
269
|
+
portfolioAmountCacheKey,
|
|
270
|
+
cachedPromise,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return cachedPromise;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export async function getSingleAssetPortfolioAmountInUSD(
|
|
278
|
+
poolName: PoolName,
|
|
279
|
+
options: { suiClient: SuiClient; address: string; isLocked?: boolean },
|
|
280
|
+
ignoreCache: boolean = false,
|
|
281
|
+
): Promise<string | undefined> {
|
|
282
|
+
const amounts = await getSingleAssetPortfolioAmount(
|
|
283
|
+
poolName,
|
|
284
|
+
options,
|
|
285
|
+
ignoreCache,
|
|
286
|
+
);
|
|
287
|
+
if (amounts !== undefined) {
|
|
288
|
+
const amount = new Decimal(amounts).div(
|
|
289
|
+
new Decimal(Math.pow(10, coins[poolTokenMap[poolName].coinName].expo)),
|
|
290
|
+
);
|
|
291
|
+
const priceOfCoin = await getLatestPrice(
|
|
292
|
+
`${poolTokenMap[poolName].coinName}/USD` as PythPriceIdPair,
|
|
293
|
+
);
|
|
294
|
+
if (priceOfCoin) {
|
|
295
|
+
const amountInUSD = amount.mul(priceOfCoin);
|
|
296
|
+
return amountInUSD.toString();
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
console.error(
|
|
300
|
+
`getPortfolioAmountInUSD is not implemented for poolName: ${poolName}`,
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
return "0";
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const poolCache = new SimpleCache<PoolType | AlphaPoolType>();
|
|
307
|
+
const poolPromiseCache = new SimpleCache<
|
|
308
|
+
Promise<PoolType | AlphaPoolType | undefined>
|
|
309
|
+
>();
|
|
310
|
+
|
|
311
|
+
export async function getPool(
|
|
312
|
+
poolName: string,
|
|
313
|
+
options: {
|
|
314
|
+
suiClient: SuiClient;
|
|
315
|
+
},
|
|
316
|
+
ignoreCache: boolean = false,
|
|
317
|
+
): Promise<PoolType | AlphaPoolType | undefined> {
|
|
318
|
+
const cacheKey = `pool_${poolInfo[poolName.toUpperCase()].poolId}`;
|
|
319
|
+
|
|
320
|
+
if (ignoreCache) {
|
|
321
|
+
poolCache.delete(cacheKey);
|
|
322
|
+
poolPromiseCache.delete(cacheKey);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Check if the pool is already in the cache
|
|
326
|
+
const cachedPool = poolCache.get(cacheKey);
|
|
327
|
+
if (cachedPool) {
|
|
328
|
+
return cachedPool;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check if there is already a promise in the cache
|
|
332
|
+
let poolPromise = poolPromiseCache.get(cacheKey);
|
|
333
|
+
if (poolPromise) {
|
|
334
|
+
return poolPromise;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// If not, create a new promise and cache it
|
|
338
|
+
poolPromise = (async () => {
|
|
339
|
+
try {
|
|
340
|
+
const o = await options.suiClient.getObject({
|
|
341
|
+
id: poolInfo[poolName].poolId,
|
|
342
|
+
options: {
|
|
343
|
+
showContent: true,
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const poolData =
|
|
348
|
+
poolName === "ALPHA" ? (o.data as AlphaPoolType) : (o.data as PoolType);
|
|
349
|
+
|
|
350
|
+
// Cache the pool object
|
|
351
|
+
poolCache.set(cacheKey, poolData);
|
|
352
|
+
return poolData;
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.error(`Error in getPool; poolName => ${poolName}`);
|
|
355
|
+
return undefined;
|
|
356
|
+
} finally {
|
|
357
|
+
// Remove the promise from the cache after it resolves
|
|
358
|
+
poolPromiseCache.delete(cacheKey);
|
|
359
|
+
}
|
|
360
|
+
})();
|
|
361
|
+
|
|
362
|
+
// Cache the promise
|
|
363
|
+
poolPromiseCache.set(cacheKey, poolPromise);
|
|
364
|
+
return poolPromise;
|
|
365
|
+
}
|