@scallop-io/sui-scallop-sdk 1.4.6 → 1.4.7
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/dist/constants/index.d.ts +0 -1
- package/dist/constants/poolAddress.d.ts +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1636 -1470
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1845 -1675
- package/dist/index.mjs.map +1 -1
- package/dist/models/scallop.d.ts +1 -2
- package/dist/models/scallopCache.d.ts +16 -10
- package/dist/models/scallopQuery.d.ts +67 -3
- package/dist/queries/poolAddressesQuery.d.ts +2 -0
- package/dist/queries/portfolioQuery.d.ts +60 -3
- package/dist/types/model.d.ts +8 -2
- package/dist/utils/index.d.ts +0 -2
- package/package.json +1 -1
- package/src/constants/index.ts +0 -1
- package/src/constants/poolAddress.ts +93 -25
- package/src/index.ts +0 -1
- package/src/models/scallop.ts +8 -13
- package/src/models/scallopAddress.ts +2 -9
- package/src/models/scallopBuilder.ts +3 -6
- package/src/models/scallopCache.ts +88 -47
- package/src/models/scallopClient.ts +3 -6
- package/src/models/scallopIndexer.ts +1 -5
- package/src/models/scallopQuery.ts +42 -16
- package/src/models/scallopUtils.ts +7 -11
- package/src/queries/coreQuery.ts +4 -4
- package/src/queries/poolAddressesQuery.ts +6 -0
- package/src/queries/portfolioQuery.ts +209 -8
- package/src/queries/sCoinQuery.ts +2 -2
- package/src/types/model.ts +13 -2
- package/src/utils/index.ts +0 -2
- package/src/utils/indexer.ts +3 -1
- package/dist/constants/tokenBucket.d.ts +0 -2
- package/dist/utils/tokenBucket.d.ts +0 -11
- package/src/constants/tokenBucket.ts +0 -2
- package/src/utils/tokenBucket.ts +0 -68
|
@@ -26,9 +26,12 @@ import type {
|
|
|
26
26
|
ObligationBorrowIcentiveReward,
|
|
27
27
|
SupportBorrowIncentiveRewardCoins,
|
|
28
28
|
SupportAssetCoins,
|
|
29
|
+
MarketPools,
|
|
30
|
+
MarketCollaterals,
|
|
29
31
|
} from '../types';
|
|
30
32
|
import { SuiObjectRef } from '@mysten/sui/client';
|
|
31
33
|
import { queryMultipleObjects } from './objectsQuery';
|
|
34
|
+
import { normalizeStructTag, SUI_TYPE_ARG } from '@scallop-io/sui-kit';
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* Get user lending infomation for specific pools.
|
|
@@ -43,6 +46,7 @@ export const getLendings = async (
|
|
|
43
46
|
query: ScallopQuery,
|
|
44
47
|
poolCoinNames: SupportPoolCoins[] = [...SUPPORT_POOLS],
|
|
45
48
|
ownerAddress?: string,
|
|
49
|
+
marketPools?: MarketPools,
|
|
46
50
|
indexer: boolean = false
|
|
47
51
|
) => {
|
|
48
52
|
const marketCoinNames = poolCoinNames.map((poolCoinName) =>
|
|
@@ -53,12 +57,14 @@ export const getLendings = async (
|
|
|
53
57
|
) as SupportStakeMarketCoins[];
|
|
54
58
|
|
|
55
59
|
const coinPrices = await query.utils.getCoinPrices();
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
marketPools =
|
|
61
|
+
marketPools ??
|
|
62
|
+
(
|
|
63
|
+
await query.getMarketPools(poolCoinNames, {
|
|
64
|
+
indexer,
|
|
65
|
+
coinPrices,
|
|
66
|
+
})
|
|
67
|
+
).pools;
|
|
62
68
|
|
|
63
69
|
const spools = await query.getSpools(stakeMarketCoinNames, {
|
|
64
70
|
indexer,
|
|
@@ -311,9 +317,13 @@ export const getLending = async (
|
|
|
311
317
|
export const getObligationAccounts = async (
|
|
312
318
|
query: ScallopQuery,
|
|
313
319
|
ownerAddress?: string,
|
|
320
|
+
market?: {
|
|
321
|
+
pools: MarketPools;
|
|
322
|
+
collaterals: MarketCollaterals;
|
|
323
|
+
},
|
|
314
324
|
indexer: boolean = false
|
|
315
325
|
) => {
|
|
316
|
-
|
|
326
|
+
market = market ?? (await query.getMarketPools(undefined, { indexer }));
|
|
317
327
|
const coinPrices = await query.getAllCoinPrices({
|
|
318
328
|
marketPools: market.pools,
|
|
319
329
|
});
|
|
@@ -379,7 +389,6 @@ export const getObligationAccount = async (
|
|
|
379
389
|
query.queryObligation(obligation),
|
|
380
390
|
query.getBorrowIncentivePools(undefined, {
|
|
381
391
|
coinPrices,
|
|
382
|
-
indexer,
|
|
383
392
|
marketPools: market.pools,
|
|
384
393
|
}),
|
|
385
394
|
query.getBorrowIncentiveAccounts(obligation),
|
|
@@ -838,3 +847,195 @@ export const getTotalValueLocked = async (
|
|
|
838
847
|
|
|
839
848
|
return tvl;
|
|
840
849
|
};
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Get user portfolio by wallet address
|
|
853
|
+
*/
|
|
854
|
+
export const getUserPortfolio = async (
|
|
855
|
+
query: ScallopQuery,
|
|
856
|
+
walletAddress: string,
|
|
857
|
+
indexer: boolean = false
|
|
858
|
+
) => {
|
|
859
|
+
const market = await query.getMarketPools();
|
|
860
|
+
const [lendings, obligationAccounts, borrowIncentivePools] =
|
|
861
|
+
await Promise.all([
|
|
862
|
+
query.getLendings(undefined, walletAddress, {
|
|
863
|
+
indexer,
|
|
864
|
+
marketPools: market.pools,
|
|
865
|
+
}),
|
|
866
|
+
query.getObligationAccounts(walletAddress, {
|
|
867
|
+
indexer,
|
|
868
|
+
market: market,
|
|
869
|
+
}),
|
|
870
|
+
query.getBorrowIncentivePools(undefined, {
|
|
871
|
+
marketPools: market.pools,
|
|
872
|
+
}),
|
|
873
|
+
]);
|
|
874
|
+
|
|
875
|
+
// get pending rewards (spool and borrow incentive)
|
|
876
|
+
const parsedLendings = Object.values(lendings)
|
|
877
|
+
.filter((t) => t.availableWithdrawCoin > 0)
|
|
878
|
+
.map((lending) => ({
|
|
879
|
+
suppliedCoin: lending.availableWithdrawCoin,
|
|
880
|
+
suppliedValue: lending.suppliedValue,
|
|
881
|
+
stakedCoin: lending.availableUnstakeCoin,
|
|
882
|
+
coinName: lending.coinName,
|
|
883
|
+
symbol: lending.symbol,
|
|
884
|
+
coinType: lending.coinType,
|
|
885
|
+
coinPrice: lending.coinPrice,
|
|
886
|
+
coinDecimals: lending.coinDecimal,
|
|
887
|
+
supplyApr: lending.supplyApr,
|
|
888
|
+
supplyApy: lending.supplyApy,
|
|
889
|
+
incentiveApr: isFinite(lending.rewardApr) ? lending.rewardApr : 0,
|
|
890
|
+
}));
|
|
891
|
+
|
|
892
|
+
const parsedObligationAccounts = Object.values(obligationAccounts)
|
|
893
|
+
.filter(
|
|
894
|
+
(t): t is NonNullable<typeof t> =>
|
|
895
|
+
!!t && t.totalBorrowedValueWithWeight > 0
|
|
896
|
+
)
|
|
897
|
+
.map((obligationAccount) => {
|
|
898
|
+
return {
|
|
899
|
+
obligationId: obligationAccount.obligationId,
|
|
900
|
+
totalDebtsInUsd: obligationAccount.totalBorrowedValueWithWeight,
|
|
901
|
+
totalCollateralInUsd: obligationAccount.totalDepositedValue,
|
|
902
|
+
riskLevel: obligationAccount.totalRiskLevel,
|
|
903
|
+
availableCollateralInUsd:
|
|
904
|
+
obligationAccount.totalAvailableCollateralValue,
|
|
905
|
+
totalUnhealthyCollateralInUsd:
|
|
906
|
+
obligationAccount.totalUnhealthyCollateralValue,
|
|
907
|
+
borrowedPools: Object.values(obligationAccount.debts)
|
|
908
|
+
.filter((debt) => debt.borrowedCoin > 0)
|
|
909
|
+
.map((debt) => ({
|
|
910
|
+
coinName: debt.coinName,
|
|
911
|
+
symbol: debt.symbol,
|
|
912
|
+
coinDecimals: debt.coinDecimal,
|
|
913
|
+
coinType: debt.coinType,
|
|
914
|
+
coinPrice: debt.coinPrice,
|
|
915
|
+
borrowedCoin: debt.borrowedCoin,
|
|
916
|
+
borrowedValueInUsd: debt.borrowedValueWithWeight,
|
|
917
|
+
borrowApr: market.pools[debt.coinName]?.borrowApr,
|
|
918
|
+
borrowApy: market.pools[debt.coinName]?.borrowApy,
|
|
919
|
+
incentiveInfos: Object.values(
|
|
920
|
+
borrowIncentivePools[debt.coinName]?.points ?? {}
|
|
921
|
+
)
|
|
922
|
+
.filter((t) => isFinite(t.rewardApr))
|
|
923
|
+
.map((t) => ({
|
|
924
|
+
coinName: t.coinName,
|
|
925
|
+
symbol: t.symbol,
|
|
926
|
+
coinType: t.coinType,
|
|
927
|
+
incentiveApr: t.rewardApr,
|
|
928
|
+
})),
|
|
929
|
+
})),
|
|
930
|
+
};
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
const pendingLendingRewards = Object.values(lendings).reduce(
|
|
934
|
+
(acc, reward) => {
|
|
935
|
+
if (reward.availableClaimCoin === 0) return acc;
|
|
936
|
+
if (!acc[reward.symbol]) {
|
|
937
|
+
acc[reward.symbol] = {
|
|
938
|
+
symbol: reward.symbol,
|
|
939
|
+
coinType: normalizeStructTag(SUI_TYPE_ARG), // @TODO: for now lending reward is all in SUI
|
|
940
|
+
coinPrice: reward.coinPrice,
|
|
941
|
+
pendingRewardInCoin: reward.availableClaimCoin,
|
|
942
|
+
};
|
|
943
|
+
} else {
|
|
944
|
+
acc[reward.symbol].pendingRewardInCoin += reward.availableClaimCoin;
|
|
945
|
+
}
|
|
946
|
+
return acc;
|
|
947
|
+
},
|
|
948
|
+
{} as Record<
|
|
949
|
+
string,
|
|
950
|
+
{
|
|
951
|
+
coinType: string;
|
|
952
|
+
symbol: string;
|
|
953
|
+
coinPrice: number;
|
|
954
|
+
pendingRewardInCoin: number;
|
|
955
|
+
}
|
|
956
|
+
>
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
const pendingBorrowIncentiveRewards = Object.values(obligationAccounts)
|
|
960
|
+
.filter((t): t is NonNullable<typeof t> => !!t)
|
|
961
|
+
.reduce(
|
|
962
|
+
(acc, curr) => {
|
|
963
|
+
Object.values(curr.borrowIncentives).forEach((incentive) => {
|
|
964
|
+
incentive.rewards.forEach((reward) => {
|
|
965
|
+
if (reward.availableClaimCoin === 0) return acc;
|
|
966
|
+
if (!acc[reward.coinName]) {
|
|
967
|
+
acc[reward.coinName] = {
|
|
968
|
+
symbol: reward.symbol,
|
|
969
|
+
coinType: reward.coinType,
|
|
970
|
+
coinPrice: reward.coinPrice,
|
|
971
|
+
pendingRewardInCoin: reward.availableClaimCoin,
|
|
972
|
+
};
|
|
973
|
+
} else {
|
|
974
|
+
acc[reward.coinName].pendingRewardInCoin +=
|
|
975
|
+
reward.availableClaimCoin;
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
return acc;
|
|
980
|
+
},
|
|
981
|
+
{} as Record<
|
|
982
|
+
string,
|
|
983
|
+
{
|
|
984
|
+
coinType: string;
|
|
985
|
+
symbol: string;
|
|
986
|
+
coinPrice: number;
|
|
987
|
+
pendingRewardInCoin: number;
|
|
988
|
+
}
|
|
989
|
+
>
|
|
990
|
+
);
|
|
991
|
+
return {
|
|
992
|
+
lendings: {
|
|
993
|
+
totalSupplyValue: parsedLendings.reduce((acc, curr) => {
|
|
994
|
+
acc += curr.suppliedValue;
|
|
995
|
+
return acc;
|
|
996
|
+
}, 0),
|
|
997
|
+
suppliedPools: parsedLendings,
|
|
998
|
+
},
|
|
999
|
+
borrowings: {
|
|
1000
|
+
...parsedObligationAccounts.reduce(
|
|
1001
|
+
(acc, curr) => {
|
|
1002
|
+
acc.totalDebtValue += curr.totalDebtsInUsd;
|
|
1003
|
+
acc.totalCollateralValue += curr.totalCollateralInUsd;
|
|
1004
|
+
return acc;
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
totalDebtValue: 0,
|
|
1008
|
+
totalCollateralValue: 0,
|
|
1009
|
+
} as {
|
|
1010
|
+
totalDebtValue: number;
|
|
1011
|
+
totalCollateralValue: number;
|
|
1012
|
+
}
|
|
1013
|
+
),
|
|
1014
|
+
obligations: parsedObligationAccounts,
|
|
1015
|
+
},
|
|
1016
|
+
pendingRewards: {
|
|
1017
|
+
lendings: Object.entries(pendingLendingRewards).reduce(
|
|
1018
|
+
(acc, [key, value]) => {
|
|
1019
|
+
acc.push({
|
|
1020
|
+
...value,
|
|
1021
|
+
coinName: key,
|
|
1022
|
+
pendingRewardInUsd: value.coinPrice * value.pendingRewardInCoin,
|
|
1023
|
+
});
|
|
1024
|
+
return acc;
|
|
1025
|
+
},
|
|
1026
|
+
[] as any
|
|
1027
|
+
),
|
|
1028
|
+
borrowIncentives: Object.entries(pendingBorrowIncentiveRewards).reduce(
|
|
1029
|
+
(acc, [key, value]) => {
|
|
1030
|
+
acc.push({
|
|
1031
|
+
coinName: key,
|
|
1032
|
+
...value,
|
|
1033
|
+
pendingRewardInUsd: value.coinPrice * value.pendingRewardInCoin,
|
|
1034
|
+
});
|
|
1035
|
+
return acc;
|
|
1036
|
+
},
|
|
1037
|
+
[] as any
|
|
1038
|
+
),
|
|
1039
|
+
},
|
|
1040
|
+
};
|
|
1041
|
+
};
|
|
@@ -95,11 +95,11 @@ export const getSCoinAmount = async (
|
|
|
95
95
|
) => {
|
|
96
96
|
const owner = ownerAddress || utils.suiKit.currentAddress();
|
|
97
97
|
const sCoinType = utils.parseSCoinType(sCoinName);
|
|
98
|
-
const
|
|
98
|
+
const coinBalance = await utils.cache.queryGetCoinBalance({
|
|
99
99
|
owner,
|
|
100
100
|
coinType: sCoinType,
|
|
101
101
|
});
|
|
102
|
-
return BigNumber(
|
|
102
|
+
return BigNumber(coinBalance?.totalBalance ?? '0').toNumber();
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
const isSupportStakeCoins = (value: string): value is SupportSCoin => {
|
package/src/types/model.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
} from '../models';
|
|
11
11
|
import { ScallopCache } from 'src/models/scallopCache';
|
|
12
12
|
import { AddressesInterface } from './address';
|
|
13
|
+
import { QueryClient, QueryClientConfig } from '@tanstack/query-core';
|
|
13
14
|
|
|
14
15
|
export type ScallopClientFnReturnType<T extends boolean> = T extends true
|
|
15
16
|
? SuiTransactionBlockResponse
|
|
@@ -26,7 +27,9 @@ export type ScallopBaseInstanceParams = {
|
|
|
26
27
|
suiKit?: SuiKit;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
export type ScallopCacheInstanceParams = ScallopBaseInstanceParams
|
|
30
|
+
export type ScallopCacheInstanceParams = ScallopBaseInstanceParams & {
|
|
31
|
+
queryClient?: QueryClient;
|
|
32
|
+
};
|
|
30
33
|
|
|
31
34
|
export type ScallopAddressInstanceParams = ScallopBaseInstanceParams & {
|
|
32
35
|
cache?: ScallopCache;
|
|
@@ -69,7 +72,8 @@ export type ScallopParams = {
|
|
|
69
72
|
export type ScallopClientParams = ScallopParams &
|
|
70
73
|
ScallopBuilderParams &
|
|
71
74
|
ScallopQueryParams &
|
|
72
|
-
ScallopUtilsParams
|
|
75
|
+
ScallopUtilsParams &
|
|
76
|
+
ScallopCacheParams;
|
|
73
77
|
|
|
74
78
|
export type ScallopBuilderParams = ScallopParams & {
|
|
75
79
|
pythEndpoints?: string[];
|
|
@@ -81,3 +85,10 @@ export type ScallopQueryParams = ScallopParams & ScallopUtilsParams;
|
|
|
81
85
|
export type ScallopUtilsParams = ScallopParams & {
|
|
82
86
|
pythEndpoints?: string[];
|
|
83
87
|
};
|
|
88
|
+
|
|
89
|
+
export type ScallopCacheParams = Omit<
|
|
90
|
+
ScallopParams,
|
|
91
|
+
'addressId' | 'forceAddressesInterface'
|
|
92
|
+
> & {
|
|
93
|
+
cacheOptions?: QueryClientConfig;
|
|
94
|
+
};
|
package/src/utils/index.ts
CHANGED
package/src/utils/indexer.ts
CHANGED
|
@@ -17,7 +17,9 @@ export async function callMethodWithIndexerFallback(
|
|
|
17
17
|
try {
|
|
18
18
|
return await method.apply(context, args);
|
|
19
19
|
} catch (e: any) {
|
|
20
|
-
console.warn(
|
|
20
|
+
console.warn(
|
|
21
|
+
`Indexer requests failed: ${e.message}. Retrying without indexer..`
|
|
22
|
+
);
|
|
21
23
|
return await method.apply(context, [
|
|
22
24
|
...args.slice(0, -1),
|
|
23
25
|
{
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
declare class TokenBucket {
|
|
2
|
-
private tokensPerInterval;
|
|
3
|
-
private interval;
|
|
4
|
-
private tokens;
|
|
5
|
-
private lastRefill;
|
|
6
|
-
constructor(tokensPerInterval: number, intervalInMs: number);
|
|
7
|
-
refill(): void;
|
|
8
|
-
removeTokens(count: number): boolean;
|
|
9
|
-
}
|
|
10
|
-
declare const callWithRateLimit: <T>(tokenBucket: TokenBucket, fn: () => Promise<T>, retryDelayInMs?: number, maxRetries?: number, backoffFactor?: number) => Promise<T | null>;
|
|
11
|
-
export { TokenBucket, callWithRateLimit };
|
package/src/utils/tokenBucket.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_INTERVAL_IN_MS } from 'src/constants/tokenBucket';
|
|
2
|
-
|
|
3
|
-
class TokenBucket {
|
|
4
|
-
private tokensPerInterval: number;
|
|
5
|
-
private interval: number;
|
|
6
|
-
private tokens: number;
|
|
7
|
-
private lastRefill: number;
|
|
8
|
-
|
|
9
|
-
constructor(tokensPerInterval: number, intervalInMs: number) {
|
|
10
|
-
this.tokensPerInterval = tokensPerInterval;
|
|
11
|
-
this.interval = intervalInMs;
|
|
12
|
-
this.tokens = tokensPerInterval;
|
|
13
|
-
this.lastRefill = Date.now();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
refill() {
|
|
17
|
-
const now = Date.now();
|
|
18
|
-
const elapsed = now - this.lastRefill;
|
|
19
|
-
|
|
20
|
-
if (elapsed >= this.interval) {
|
|
21
|
-
const tokensToAdd =
|
|
22
|
-
Math.floor(elapsed / this.interval) * this.tokensPerInterval;
|
|
23
|
-
this.tokens = Math.min(this.tokens + tokensToAdd, this.tokensPerInterval);
|
|
24
|
-
|
|
25
|
-
// Update lastRefill to reflect the exact time of the last "refill"
|
|
26
|
-
this.lastRefill += Math.floor(elapsed / this.interval) * this.interval;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
removeTokens(count: number) {
|
|
31
|
-
this.refill();
|
|
32
|
-
if (this.tokens >= count) {
|
|
33
|
-
this.tokens -= count;
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const callWithRateLimit = async <T>(
|
|
41
|
-
tokenBucket: TokenBucket,
|
|
42
|
-
fn: () => Promise<T>,
|
|
43
|
-
retryDelayInMs = DEFAULT_INTERVAL_IN_MS,
|
|
44
|
-
maxRetries = 15,
|
|
45
|
-
backoffFactor = 1.25 // The factor by which to increase the delay
|
|
46
|
-
): Promise<T | null> => {
|
|
47
|
-
let retries = 0;
|
|
48
|
-
|
|
49
|
-
const tryRequest = async (): Promise<T | null> => {
|
|
50
|
-
if (tokenBucket.removeTokens(1)) {
|
|
51
|
-
const result = await fn();
|
|
52
|
-
return result;
|
|
53
|
-
} else if (retries < maxRetries) {
|
|
54
|
-
retries++;
|
|
55
|
-
const delay = retryDelayInMs * Math.pow(backoffFactor, retries);
|
|
56
|
-
// console.error(`Rate limit exceeded, retrying in ${delay} ms`);
|
|
57
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
58
|
-
return tryRequest();
|
|
59
|
-
} else {
|
|
60
|
-
console.error('Maximum retries reached');
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return tryRequest();
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export { TokenBucket, callWithRateLimit };
|