@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.
Files changed (46) hide show
  1. package/.babelrc +6 -0
  2. package/.eslintrc.json +30 -0
  3. package/README.md +1 -0
  4. package/dist/common/cetus_mainnet_config.d.ts +3 -0
  5. package/dist/common/cetus_mainnet_config.d.ts.map +1 -0
  6. package/dist/common/coins.d.ts +11 -0
  7. package/dist/common/coins.d.ts.map +1 -0
  8. package/dist/common/constants.d.ts +294 -0
  9. package/dist/common/constants.d.ts.map +1 -0
  10. package/dist/common/maps.d.ts +30 -0
  11. package/dist/common/maps.d.ts.map +1 -0
  12. package/dist/common/pyth.d.ts +7 -0
  13. package/dist/common/pyth.d.ts.map +1 -0
  14. package/dist/common/types.d.ts +331 -0
  15. package/dist/common/types.d.ts.map +1 -0
  16. package/dist/functions.d.ts +20 -0
  17. package/dist/functions.d.ts.map +1 -0
  18. package/dist/getVaultBalances.d.ts +5 -0
  19. package/dist/getVaultBalances.d.ts.map +1 -0
  20. package/dist/getVaults.d.ts +3 -0
  21. package/dist/getVaults.d.ts.map +1 -0
  22. package/dist/index.d.ts +4 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +3 -0
  25. package/dist/index.js.LICENSE.txt +28 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/portfolioAmount.d.ts +36 -0
  28. package/dist/portfolioAmount.d.ts.map +1 -0
  29. package/dist/price.d.ts +19 -0
  30. package/dist/price.d.ts.map +1 -0
  31. package/jest.config.js +14 -0
  32. package/package.json +51 -0
  33. package/src/common/cetus_mainnet_config.ts +72 -0
  34. package/src/common/coins.ts +126 -0
  35. package/src/common/constants.ts +820 -0
  36. package/src/common/maps.ts +200 -0
  37. package/src/common/pyth.ts +23 -0
  38. package/src/common/types.ts +446 -0
  39. package/src/functions.ts +299 -0
  40. package/src/getVaultBalances.ts +128 -0
  41. package/src/getVaults.ts +63 -0
  42. package/src/index.ts +13 -0
  43. package/src/portfolioAmount.ts +365 -0
  44. package/src/price.ts +397 -0
  45. package/tsconfig.json +21 -0
  46. package/webpack.config.js +59 -0
@@ -0,0 +1,299 @@
1
+ import BN from "bn.js";
2
+ import { ClmmPoolUtil, TickMath } from "@cetusprotocol/cetus-sui-clmm-sdk";
3
+ import { PaginatedObjectsResponse, SuiClient } from "@mysten/sui/client";
4
+ import Decimal from "decimal.js";
5
+ import { cetusPoolMap, poolInfo } from "./common/maps";
6
+ import {
7
+ CetusInvestor,
8
+ CetusPoolType,
9
+ CoinAmounts,
10
+ PoolName,
11
+ Receipt,
12
+ SimpleCache,
13
+ } from "./common/types";
14
+ import { getPool } from "./portfolioAmount";
15
+
16
+ const receiptsCache = new SimpleCache<Receipt[]>();
17
+ const receiptsPromiseCache = new SimpleCache<Promise<Receipt[]>>();
18
+ export async function getReceipts(
19
+ poolName: string,
20
+ options: {
21
+ address: string;
22
+ suiClient: SuiClient;
23
+ },
24
+ ignoreCache: boolean = false,
25
+ ): Promise<Receipt[]> {
26
+ const receiptsCacheKey = `getReceipts-${poolName}-${options.address}`;
27
+ if (ignoreCache) {
28
+ receiptsCache.delete(receiptsCacheKey);
29
+ receiptsPromiseCache.delete(receiptsCacheKey);
30
+ }
31
+ const cachedResponse = receiptsCache.get(receiptsCacheKey);
32
+ if (cachedResponse) {
33
+ return cachedResponse;
34
+ }
35
+
36
+ const nfts: Receipt[] = [];
37
+
38
+ let cachedPromise = receiptsPromiseCache.get(receiptsCacheKey);
39
+ if (!cachedPromise) {
40
+ cachedPromise = (async (): Promise<Receipt[]> => {
41
+ // const first_package = conf[CONF_ENV].ALPHA_FIRST_PACKAGE_ID;
42
+ let currentCursor: string | null | undefined = null;
43
+ /* eslint-disable-next-line no-constant-condition */
44
+ while (true) {
45
+ const paginatedObjects: PaginatedObjectsResponse =
46
+ await options.suiClient.getOwnedObjects({
47
+ owner: options.address,
48
+ cursor: currentCursor,
49
+ filter: {
50
+ // StructType: `${first_package}::${module}::Receipt`,
51
+ StructType: poolInfo[poolName].receiptType,
52
+ },
53
+ options: {
54
+ showType: true,
55
+ showContent: true,
56
+ },
57
+ });
58
+ // Traverse the current page data and push to coins array
59
+
60
+ paginatedObjects.data.forEach((obj) => {
61
+ const o = obj.data as Receipt;
62
+ if (o) {
63
+ if (poolInfo[poolName].receiptName === o.content.fields.name) {
64
+ nfts.push(o);
65
+ }
66
+ }
67
+ });
68
+
69
+ // Check if there's a next page
70
+ if (paginatedObjects.hasNextPage && paginatedObjects.nextCursor) {
71
+ currentCursor = paginatedObjects.nextCursor;
72
+ } else {
73
+ // No more pages available
74
+ break;
75
+ }
76
+ }
77
+ receiptsCache.set(receiptsCacheKey, nfts);
78
+ receiptsPromiseCache.delete(receiptsCacheKey);
79
+ return nfts;
80
+ })().catch((error) => {
81
+ // TODO: Jugaad
82
+ if (poolInfo[poolName].parentProtocolName === "NAVI") {
83
+ return nfts;
84
+ } else {
85
+ receiptsPromiseCache.delete(receiptsCacheKey); // Remove the promise from cache
86
+ throw error;
87
+ }
88
+ });
89
+ receiptsPromiseCache.set(receiptsCacheKey, cachedPromise);
90
+ }
91
+ return cachedPromise;
92
+ }
93
+
94
+ const poolExchangeRateCache = new SimpleCache<Decimal>();
95
+
96
+ export async function getPoolExchangeRate(
97
+ poolName: PoolName,
98
+ options: { suiClient: SuiClient },
99
+ ignoreCache: boolean = false,
100
+ ): Promise<Decimal | undefined> {
101
+ const poolExchangeRateCacheKey = `getPoolExchangeRate-${poolName}`;
102
+ if (ignoreCache) {
103
+ poolExchangeRateCache.delete(poolExchangeRateCacheKey);
104
+ }
105
+ const cachedResponse = poolExchangeRateCache.get(poolExchangeRateCacheKey);
106
+ if (cachedResponse) {
107
+ return cachedResponse;
108
+ }
109
+
110
+ let pool = undefined;
111
+ try {
112
+ if (poolName === "ALPHA") {
113
+ pool = await getPool("ALPHA", options);
114
+ } else if (
115
+ poolName === "ALPHA-SUI" ||
116
+ poolName === "USDT-USDC" ||
117
+ poolName === "USDY-USDC" ||
118
+ poolName === "HASUI-SUI" ||
119
+ poolName === "USDC-SUI" ||
120
+ poolName === "WETH-USDC"
121
+ ) {
122
+ pool = await getPool(poolName, options);
123
+ } else {
124
+ pool = await getPool(poolName, options);
125
+ }
126
+ if (pool) {
127
+ const xTokenSupply = new Decimal(pool.content.fields.xTokenSupply);
128
+ let tokensInvested = new Decimal(pool.content.fields.tokensInvested);
129
+ if (poolName == "ALPHA") {
130
+ tokensInvested = new Decimal(pool.content.fields.alpha_bal);
131
+ }
132
+
133
+ // Check for division by zero
134
+ if (xTokenSupply.eq(0)) {
135
+ console.error("Division by zero error: tokensInvested is zero.");
136
+ return undefined;
137
+ }
138
+ const poolExchangeRate = tokensInvested.div(xTokenSupply);
139
+
140
+ poolExchangeRateCache.set(poolExchangeRateCacheKey, poolExchangeRate);
141
+
142
+ return poolExchangeRate;
143
+ }
144
+ } catch (e) {
145
+ console.error(`getPoolExchangeRate failed for poolName: ${poolName}`);
146
+ }
147
+
148
+ return undefined;
149
+ }
150
+
151
+ export async function getCoinAmountsFromLiquidity(
152
+ poolName: PoolName,
153
+ liquidity: number,
154
+ options: { suiClient: SuiClient },
155
+ ): Promise<[number, number]> {
156
+ const cetus_pool = await getCetusPool(poolName, options);
157
+ const cetusInvestor = await getCetusInvestor(poolName, options);
158
+
159
+ const upper_bound = 443636;
160
+ let lower_tick = Number(cetusInvestor!.content.fields.lower_tick);
161
+ let upper_tick = Number(cetusInvestor!.content.fields.upper_tick);
162
+
163
+ if (lower_tick > upper_bound) {
164
+ lower_tick = -~(lower_tick - 1);
165
+ }
166
+ if (upper_tick > upper_bound) {
167
+ upper_tick = -~(upper_tick - 1);
168
+ }
169
+
170
+ if (cetus_pool) {
171
+ const liquidityInt = Math.floor(liquidity);
172
+ const coin_amounts: CoinAmounts = ClmmPoolUtil.getCoinAmountFromLiquidity(
173
+ new BN(`${liquidityInt}`),
174
+ new BN(cetus_pool.content.fields.current_sqrt_price),
175
+ TickMath.tickIndexToSqrtPriceX64(lower_tick),
176
+ TickMath.tickIndexToSqrtPriceX64(upper_tick),
177
+ true,
178
+ );
179
+
180
+ return [coin_amounts.coinA.toNumber(), coin_amounts.coinB.toNumber()];
181
+ } else {
182
+ return [0, 0];
183
+ }
184
+ }
185
+
186
+ const cetusPoolCache = new SimpleCache<CetusPoolType>();
187
+ const cetusPoolPromiseCache = new SimpleCache<
188
+ Promise<CetusPoolType | undefined>
189
+ >();
190
+
191
+ export async function getCetusPool(
192
+ poolName: string,
193
+ options: {
194
+ suiClient: SuiClient;
195
+ },
196
+ ignoreCache: boolean = false,
197
+ ): Promise<CetusPoolType | undefined> {
198
+ const cacheKey = `pool_${cetusPoolMap[poolName.toUpperCase()]}`;
199
+ if (ignoreCache) {
200
+ cetusPoolCache.delete(cacheKey);
201
+ cetusPoolPromiseCache.delete(cacheKey);
202
+ }
203
+
204
+ // Check if the pool is already in the cache
205
+ const cachedPool = cetusPoolCache.get(cacheKey);
206
+ if (cachedPool) {
207
+ return cachedPool;
208
+ }
209
+
210
+ // Check if there is already a promise in the cache
211
+ let cetusPoolPromise = cetusPoolPromiseCache.get(cacheKey);
212
+ if (cetusPoolPromise) {
213
+ return cetusPoolPromise;
214
+ }
215
+
216
+ // If not, create a new promise and cache it
217
+ cetusPoolPromise = (async () => {
218
+ try {
219
+ const o = await options.suiClient.getObject({
220
+ id: cetusPoolMap[poolName.toUpperCase()],
221
+ options: {
222
+ showContent: true,
223
+ },
224
+ });
225
+ const cetusPool = o.data as CetusPoolType;
226
+
227
+ // Cache the pool object
228
+ cetusPoolCache.set(cacheKey, cetusPool);
229
+ return cetusPool;
230
+ } catch (e) {
231
+ console.error(`getCetusPool failed for poolName: ${poolName}`);
232
+ return undefined;
233
+ } finally {
234
+ // Remove the promise from the cache after it resolves
235
+ cetusPoolPromiseCache.delete(cacheKey);
236
+ }
237
+ })();
238
+
239
+ // Cache the promise
240
+ cetusPoolPromiseCache.set(cacheKey, cetusPoolPromise);
241
+ return cetusPoolPromise;
242
+ }
243
+
244
+ const cetusInvestorCache = new SimpleCache<CetusInvestor>();
245
+ const cetusInvestorPromiseCache = new SimpleCache<
246
+ Promise<CetusInvestor | undefined>
247
+ >();
248
+
249
+ export async function getCetusInvestor(
250
+ poolName: PoolName,
251
+ options: {
252
+ suiClient: SuiClient;
253
+ },
254
+ ignoreCache: boolean = false,
255
+ ): Promise<CetusInvestor | undefined> {
256
+ const cacheKey = `investor_${poolInfo[poolName.toUpperCase()].investorId}`;
257
+ if (ignoreCache) {
258
+ cetusInvestorCache.delete(cacheKey);
259
+ cetusInvestorPromiseCache.delete(cacheKey);
260
+ }
261
+ // Check if the investor is already in the cache
262
+ const cachedInvestor = cetusInvestorCache.get(cacheKey);
263
+ if (cachedInvestor) {
264
+ return cachedInvestor;
265
+ }
266
+
267
+ // Check if there is already a promise in the cache
268
+ let cetusInvestorPromise = cetusInvestorPromiseCache.get(cacheKey);
269
+ if (cetusInvestorPromise) {
270
+ return cetusInvestorPromise;
271
+ }
272
+
273
+ // If not, create a new promise and cache it
274
+ cetusInvestorPromise = (async () => {
275
+ try {
276
+ const o = await options.suiClient.getObject({
277
+ id: poolInfo[poolName.toUpperCase()].investorId,
278
+ options: {
279
+ showContent: true,
280
+ },
281
+ });
282
+ const cetus_investor = o.data as CetusInvestor;
283
+
284
+ // Cache the investor object
285
+ cetusInvestorCache.set(cacheKey, cetus_investor);
286
+ return cetus_investor;
287
+ } catch (e) {
288
+ console.error(`getCetusInvestor failed for pool: ${poolName}`);
289
+ return undefined;
290
+ } finally {
291
+ // Remove the promise from the cache after it resolves
292
+ cetusInvestorPromiseCache.delete(cacheKey);
293
+ }
294
+ })();
295
+
296
+ // Cache the promise
297
+ cetusInvestorPromiseCache.set(cacheKey, cetusInvestorPromise);
298
+ return cetusInvestorPromise;
299
+ }
@@ -0,0 +1,128 @@
1
+ import { getFullnodeUrl, SuiClient } from "@mysten/sui/client";
2
+ import {
3
+ AlphaVaultBalance,
4
+ DoubleAssetVaultBalance,
5
+ PoolName,
6
+ SingleAssetVaultBalance,
7
+ } from "./common/types";
8
+ import {
9
+ getAlphaPortfolioAmount,
10
+ getAlphaPortfolioAmountInUSD,
11
+ getPortfolioAmount,
12
+ getPortfolioAmountInUSD,
13
+ getSingleAssetPortfolioAmount,
14
+ getSingleAssetPortfolioAmountInUSD,
15
+ } from "./portfolioAmount";
16
+
17
+ export async function getAlphaVaultBalance(
18
+ address: string,
19
+ ): Promise<AlphaVaultBalance | undefined> {
20
+ const suiClient = new SuiClient({
21
+ url: getFullnodeUrl("mainnet"),
22
+ });
23
+ if (address) {
24
+ const lockedPortfolioAmount = await getAlphaPortfolioAmount("ALPHA", {
25
+ suiClient,
26
+ address,
27
+ isLocked: true,
28
+ });
29
+ const lockedPortfolioAmountInUSD = await getAlphaPortfolioAmountInUSD(
30
+ "ALPHA",
31
+ { suiClient, address, isLocked: true },
32
+ );
33
+ const unlockedPortfolioAmount = await getAlphaPortfolioAmount("ALPHA", {
34
+ suiClient,
35
+ address,
36
+ isLocked: false,
37
+ });
38
+ const unlockedPortfolioAmountInUSD = await getAlphaPortfolioAmountInUSD(
39
+ "ALPHA",
40
+ { suiClient, address, isLocked: false },
41
+ );
42
+ const portfolioAmount = await getAlphaPortfolioAmount("ALPHA", {
43
+ suiClient,
44
+ address,
45
+ });
46
+ const portfolioAmountInUSD = await getAlphaPortfolioAmountInUSD("ALPHA", {
47
+ suiClient,
48
+ address,
49
+ });
50
+ if (
51
+ lockedPortfolioAmount !== undefined &&
52
+ lockedPortfolioAmountInUSD !== undefined &&
53
+ unlockedPortfolioAmount !== undefined &&
54
+ unlockedPortfolioAmountInUSD !== undefined &&
55
+ portfolioAmount !== undefined &&
56
+ portfolioAmountInUSD !== undefined
57
+ ) {
58
+ const res: AlphaVaultBalance = {
59
+ lockedAlphaCoins: lockedPortfolioAmount,
60
+ lockedAlphaCoinsInUSD: lockedPortfolioAmountInUSD,
61
+ unlockedAlphaCoins: unlockedPortfolioAmount,
62
+ unlockedAlphaCoinsInUSD: unlockedPortfolioAmountInUSD,
63
+ totalAlphaCoins: portfolioAmount,
64
+ totalAlphaCoinsInUSD: portfolioAmountInUSD,
65
+ };
66
+ return res;
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+
72
+ export async function getDoubleAssetVaultBalance(
73
+ address: string,
74
+ poolName: PoolName,
75
+ ): Promise<DoubleAssetVaultBalance | undefined> {
76
+ const suiClient = new SuiClient({
77
+ url: getFullnodeUrl("mainnet"),
78
+ });
79
+ if (address && poolName) {
80
+ const portfolioAmount = await getPortfolioAmount(poolName as PoolName, {
81
+ suiClient,
82
+ address,
83
+ });
84
+ const portfolioAmountInUSD = await getPortfolioAmountInUSD(
85
+ poolName as PoolName,
86
+ { suiClient, address },
87
+ );
88
+ if (portfolioAmount !== undefined && portfolioAmountInUSD !== undefined) {
89
+ const res: DoubleAssetVaultBalance = {
90
+ coinA: portfolioAmount[0].toString(),
91
+ coinB: portfolioAmount[1].toString(),
92
+ valueInUSD: portfolioAmountInUSD,
93
+ };
94
+ return res;
95
+ }
96
+ }
97
+ }
98
+
99
+ export async function getSingleAssetVaultBalance(
100
+ address: string,
101
+ poolName: PoolName,
102
+ ): Promise<SingleAssetVaultBalance | undefined> {
103
+ const suiClient = new SuiClient({
104
+ url: getFullnodeUrl("mainnet"),
105
+ });
106
+ const portfolioAmount = await getSingleAssetPortfolioAmount(
107
+ poolName as PoolName,
108
+ {
109
+ suiClient,
110
+ address,
111
+ },
112
+ );
113
+ const portfolioAmountInUSD = await getSingleAssetPortfolioAmountInUSD(
114
+ poolName as PoolName,
115
+ {
116
+ suiClient,
117
+ address,
118
+ },
119
+ );
120
+ if (portfolioAmount !== undefined && portfolioAmountInUSD !== undefined) {
121
+ const res: SingleAssetVaultBalance = {
122
+ coin: portfolioAmount.toString(),
123
+ valueInUSD: portfolioAmountInUSD,
124
+ };
125
+ return res;
126
+ }
127
+ return undefined;
128
+ }
@@ -0,0 +1,63 @@
1
+ import { poolInfo } from "./common/maps";
2
+ import { getFullnodeUrl, SuiClient } from "@mysten/sui/client";
3
+ import { getReceipts } from "./functions";
4
+ import { AlphaFiVault } from "./common/types";
5
+ import { getPool } from "./portfolioAmount";
6
+
7
+ function extractCoinTypes(input: string): {
8
+ coinTypeA: string | null;
9
+ coinTypeB: string | null;
10
+ } {
11
+ let regex = /Pool<([^,>]+),\s*([^>]+)>/;
12
+ let match = input.match(regex);
13
+ if (!match) {
14
+ regex = /Pool<([^,>]+)>/;
15
+ match = input.match(regex);
16
+ if (!match) {
17
+ return { coinTypeA: null, coinTypeB: null };
18
+ }
19
+ }
20
+ const coinTypeA = match[1] || null;
21
+ const coinTypeB = match[2] || null;
22
+ return { coinTypeA, coinTypeB };
23
+ }
24
+
25
+ export async function getVaults(
26
+ address: string,
27
+ ): Promise<AlphaFiVault[] | undefined> {
28
+ const vaultsArr = [];
29
+ const suiClient = new SuiClient({
30
+ url: getFullnodeUrl("mainnet"),
31
+ });
32
+ if (address) {
33
+ for (const pool of Object.keys(poolInfo)) {
34
+ const receipt = await getReceipts(pool, { address, suiClient });
35
+ const poolObject = await getPool(pool, { suiClient });
36
+ if (receipt.length > 0 && poolObject) {
37
+ const name = receipt[0].content.fields.name;
38
+ const res: AlphaFiVault = {
39
+ poolId: poolInfo[pool].poolId,
40
+ poolName: null,
41
+ receiptName: name,
42
+ receiptType: receipt[0].content.type,
43
+ coinTypeA: extractCoinTypes(poolObject.content.type).coinTypeA,
44
+ coinTypeB: extractCoinTypes(poolObject.content.type).coinTypeB,
45
+ };
46
+ if (poolInfo[pool].parentProtocolName === "NAVI") {
47
+ res.poolName = name
48
+ .replace(/^AlphaFi-NAVI /, "")
49
+ .replace(/ Receipt$/, "");
50
+
51
+ res.poolName = "NAVI-" + res.poolName;
52
+ } else if (poolInfo[pool].parentProtocolName === "ALPHAFI") {
53
+ res.poolName = name.replace(/^AlphaFi /, "").replace(/ Receipt$/, "");
54
+ } else {
55
+ res.poolName = name.replace(/^AlphaFi /, "").replace(/ Receipt$/, "");
56
+ }
57
+ vaultsArr.push(res);
58
+ }
59
+ }
60
+ return vaultsArr;
61
+ }
62
+ return undefined;
63
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ export { getVaults } from "./getVaults";
2
+ export {
3
+ getAlphaVaultBalance,
4
+ getSingleAssetVaultBalance,
5
+ getDoubleAssetVaultBalance,
6
+ } from "./getVaultBalances";
7
+ export {
8
+ AlphaFiVault,
9
+ AlphaVaultBalance,
10
+ DoubleAssetVaultBalance,
11
+ SingleAssetVaultBalance,
12
+ PoolName,
13
+ } from "./common/types";