@scallop-io/sui-scallop-sdk 2.3.10 → 2.3.12

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.
@@ -427,12 +427,6 @@ export const getObligationAccount = async (
427
427
  query.getBorrowIncentiveAccounts(obligation),
428
428
  ]);
429
429
 
430
- console.log({
431
- obligationQuery,
432
- borrowIncentivePools,
433
- borrowIncentiveAccounts,
434
- });
435
-
436
430
  const collaterals: ObligationAccount['collaterals'] = {};
437
431
  const debts: ObligationAccount['debts'] = {};
438
432
  const borrowIncentives: ObligationAccount['borrowIncentives'] = {};
@@ -518,69 +512,6 @@ export const getObligationAccount = async (
518
512
  ]),
519
513
  ];
520
514
 
521
- for (const assetCoinName of borrowAssetCoinNames) {
522
- const debt = obligationQuery?.debts.find((debt) => {
523
- const poolCoinName = query.utils.parseCoinNameFromType(debt.type.name);
524
- return assetCoinName === poolCoinName;
525
- });
526
-
527
- const marketPool = market.pools[assetCoinName];
528
- const coinDecimal = query.utils.getCoinDecimal(assetCoinName);
529
- const coinPrice = coinPrices?.[assetCoinName] ?? 0;
530
- const coinAmount = coinAmounts?.[assetCoinName] ?? 0;
531
-
532
- if (marketPool) {
533
- const increasedRate = debt?.borrowIndex
534
- ? marketPool.borrowIndex / Number(debt.borrowIndex) - 1
535
- : 0;
536
- const borrowedAmount = BigNumber(debt?.amount ?? 0).multipliedBy(
537
- increasedRate + 1
538
- );
539
- const borrowedCoin = borrowedAmount.shiftedBy(-1 * coinDecimal);
540
-
541
- const requiredRepayAmount = borrowedAmount;
542
- const requiredRepayCoin = requiredRepayAmount.shiftedBy(-1 * coinDecimal);
543
-
544
- const availableRepayAmount = BigNumber(coinAmount);
545
- const availableRepayCoin = availableRepayAmount.shiftedBy(
546
- -1 * coinDecimal
547
- );
548
-
549
- const borrowedValue = requiredRepayCoin.multipliedBy(coinPrice);
550
- const borrowedValueWithWeight = borrowedValue.multipliedBy(
551
- marketPool.borrowWeight
552
- );
553
-
554
- totalBorrowedValue = totalBorrowedValue.plus(borrowedValue);
555
- totalBorrowedValueWithWeight = totalBorrowedValueWithWeight.plus(
556
- borrowedValueWithWeight
557
- );
558
-
559
- if (borrowedAmount.isGreaterThan(0)) {
560
- totalBorrowedPools++;
561
- }
562
-
563
- debts[assetCoinName] = {
564
- coinName: assetCoinName,
565
- coinType: query.utils.parseCoinType(assetCoinName),
566
- symbol: query.utils.parseSymbol(assetCoinName),
567
- coinDecimal: coinDecimal,
568
- coinPrice: coinPrice,
569
- borrowedAmount: borrowedAmount.toNumber(),
570
- borrowedCoin: borrowedCoin.toNumber(),
571
- borrowedValue: borrowedValue.toNumber(),
572
- borrowedValueWithWeight: borrowedValueWithWeight.toNumber(),
573
- borrowIndex: Number(debt?.borrowIndex ?? 0),
574
- requiredRepayAmount: requiredRepayAmount.toNumber(),
575
- requiredRepayCoin: requiredRepayCoin.toNumber(),
576
- availableBorrowAmount: 0,
577
- availableBorrowCoin: 0,
578
- availableRepayAmount: availableRepayAmount.toNumber(),
579
- availableRepayCoin: availableRepayCoin.toNumber(),
580
- };
581
- }
582
- }
583
-
584
515
  for (const [poolCoinName, borrowIncentiveAccount] of Object.entries(
585
516
  borrowIncentiveAccounts
586
517
  )) {
@@ -622,22 +553,29 @@ export const getObligationAccount = async (
622
553
 
623
554
  // for veSCA
624
555
  const weightScale = BigNumber(1_000_000_000_000);
556
+ const boostScale = BigNumber(poolPoint.baseWeight).dividedBy(
557
+ weightScale
558
+ );
625
559
  const boostValue = BigNumber(accountPoint.weightedAmount)
626
560
  .div(
627
- BigNumber(borrowIncentiveAccount.debtAmount)
628
- .multipliedBy(poolPoint.baseWeight)
629
- .dividedBy(weightScale)
561
+ BigNumber(borrowIncentiveAccount.debtAmount).multipliedBy(
562
+ boostScale
563
+ )
630
564
  )
631
565
  .isFinite()
632
566
  ? BigNumber(accountPoint.weightedAmount)
633
567
  .div(
634
- BigNumber(borrowIncentiveAccount.debtAmount)
635
- .multipliedBy(poolPoint.baseWeight)
636
- .dividedBy(weightScale)
568
+ BigNumber(borrowIncentiveAccount.debtAmount).multipliedBy(
569
+ boostScale
570
+ )
637
571
  )
638
572
  .toNumber()
639
573
  : 1;
640
574
 
575
+ const rewardApr = isFinite(poolPoint.rewardApr)
576
+ ? poolPoint.rewardApr
577
+ : 0;
578
+
641
579
  if (availableClaimAmount.isGreaterThanOrEqualTo(0)) {
642
580
  rewards.push({
643
581
  coinName: poolPoint.coinName,
@@ -648,6 +586,9 @@ export const getObligationAccount = async (
648
586
  weightedBorrowAmount: accountBorrowedAmount.toNumber(),
649
587
  availableClaimAmount: availableClaimAmount.toNumber(),
650
588
  availableClaimCoin: availableClaimCoin.toNumber(),
589
+ baseRewardApr: rewardApr,
590
+ boostedRewardApr: rewardApr * boostValue,
591
+ maxBoost: 1 / boostScale.toNumber(),
651
592
  boostValue,
652
593
  });
653
594
  }
@@ -680,6 +621,72 @@ export const getObligationAccount = async (
680
621
  }
681
622
  }
682
623
 
624
+ for (const assetCoinName of borrowAssetCoinNames) {
625
+ const debt = obligationQuery?.debts.find((debt) => {
626
+ const poolCoinName = query.utils.parseCoinNameFromType(debt.type.name);
627
+ return assetCoinName === poolCoinName;
628
+ });
629
+
630
+ const marketPool = market.pools[assetCoinName];
631
+ const coinDecimal = query.utils.getCoinDecimal(assetCoinName);
632
+ const coinPrice = coinPrices?.[assetCoinName] ?? 0;
633
+ const coinAmount = coinAmounts?.[assetCoinName] ?? 0;
634
+
635
+ if (marketPool) {
636
+ const increasedRate = debt?.borrowIndex
637
+ ? marketPool.borrowIndex / Number(debt.borrowIndex) - 1
638
+ : 0;
639
+ const borrowedAmount = BigNumber(debt?.amount ?? 0).multipliedBy(
640
+ increasedRate + 1
641
+ );
642
+ const borrowedCoin = borrowedAmount.shiftedBy(-1 * coinDecimal);
643
+
644
+ const requiredRepayAmount = borrowedAmount;
645
+ const requiredRepayCoin = requiredRepayAmount.shiftedBy(-1 * coinDecimal);
646
+
647
+ const availableRepayAmount = BigNumber(coinAmount);
648
+ const availableRepayCoin = availableRepayAmount.shiftedBy(
649
+ -1 * coinDecimal
650
+ );
651
+
652
+ const borrowedValue = requiredRepayCoin.multipliedBy(coinPrice);
653
+ const borrowedValueWithWeight = borrowedValue.multipliedBy(
654
+ marketPool.borrowWeight
655
+ );
656
+
657
+ totalBorrowedValue = totalBorrowedValue.plus(borrowedValue);
658
+ totalBorrowedValueWithWeight = totalBorrowedValueWithWeight.plus(
659
+ borrowedValueWithWeight
660
+ );
661
+
662
+ if (borrowedAmount.isGreaterThan(0)) {
663
+ totalBorrowedPools++;
664
+ }
665
+
666
+ debts[assetCoinName] = {
667
+ coinName: assetCoinName,
668
+ coinType: query.utils.parseCoinType(assetCoinName),
669
+ symbol: query.utils.parseSymbol(assetCoinName),
670
+ coinDecimal: coinDecimal,
671
+ coinPrice: coinPrice,
672
+ borrowedAmount: borrowedAmount.toNumber(),
673
+ borrowedCoin: borrowedCoin.toNumber(),
674
+ borrowedValue: borrowedValue.toNumber(),
675
+ borrowedValueWithWeight: borrowedValueWithWeight.toNumber(),
676
+ borrowIndex: Number(debt?.borrowIndex ?? 0),
677
+ requiredRepayAmount: requiredRepayAmount.toNumber(),
678
+ requiredRepayCoin: requiredRepayCoin.toNumber(),
679
+ availableBorrowAmount: 0,
680
+ availableBorrowCoin: 0,
681
+ availableRepayAmount: availableRepayAmount.toNumber(),
682
+ availableRepayCoin: availableRepayCoin.toNumber(),
683
+ rewards: (borrowIncentives[assetCoinName]?.rewards ?? []).filter(
684
+ ({ weightedBorrowAmount }) => weightedBorrowAmount > 0
685
+ ),
686
+ };
687
+ }
688
+ }
689
+
683
690
  let riskLevel = totalRequiredCollateralValue.isZero()
684
691
  ? // Note: when there is no collateral and debt is not zero, then it's a bad-debt situation.
685
692
  totalBorrowedValueWithWeight.isGreaterThan(0)
@@ -911,24 +918,19 @@ export const getUserPortfolio = async (
911
918
  const coinPrices = await query.getAllCoinPrices({ indexer });
912
919
  const market = await query.getMarketPools(undefined, { indexer, coinPrices });
913
920
 
914
- const [lendings, obligationAccounts, borrowIncentivePools, veScas] =
915
- await Promise.all([
916
- query.getLendings(undefined, walletAddress, {
917
- indexer,
918
- marketPools: market.pools,
919
- coinPrices,
920
- }),
921
- query.getObligationAccounts(walletAddress, {
922
- indexer,
923
- market: market,
924
- coinPrices,
925
- }),
926
- query.getBorrowIncentivePools(undefined, {
927
- marketPools: market.pools,
928
- coinPrices,
929
- }),
930
- query.getVeScas({ walletAddress, excludeEmpty: true }),
931
- ]);
921
+ const [lendings, obligationAccounts, veScas] = await Promise.all([
922
+ query.getLendings(undefined, walletAddress, {
923
+ indexer,
924
+ marketPools: market.pools,
925
+ coinPrices,
926
+ }),
927
+ query.getObligationAccounts(walletAddress, {
928
+ indexer,
929
+ market: market,
930
+ coinPrices,
931
+ }),
932
+ query.getVeScas({ walletAddress, excludeEmpty: true }),
933
+ ]);
932
934
 
933
935
  // get pending rewards (spool and borrow incentive)
934
936
  const parsedLendings = Object.values(lendings)
@@ -983,29 +985,35 @@ export const getUserPortfolio = async (
983
985
  (debt): debt is NonNullable<typeof debt> =>
984
986
  !!debt && debt.borrowedCoin > 0
985
987
  )
986
- .map((debt) => ({
987
- coinName: debt.coinName,
988
- symbol: debt.symbol,
989
- coinDecimals: debt.coinDecimal,
990
- coinType: debt.coinType,
991
- coinPrice: debt.coinPrice,
992
- borrowedCoin: debt.borrowedCoin,
993
- borrowedValueInUsd: debt.borrowedValueWithWeight,
994
- borrowApr: market.pools[debt.coinName]?.borrowApr,
995
- borrowApy: market.pools[debt.coinName]?.borrowApy,
996
- incentiveInfos: Object.values(
997
- borrowIncentivePools[debt.coinName]?.points ?? {}
998
- )
999
- .filter(
1000
- (t): t is NonNullable<typeof t> => !!t && isFinite(t.rewardApr)
988
+ .map((debt) => {
989
+ return {
990
+ coinName: debt.coinName,
991
+ symbol: debt.symbol,
992
+ coinDecimals: debt.coinDecimal,
993
+ coinType: debt.coinType,
994
+ coinPrice: debt.coinPrice,
995
+ borrowedCoin: debt.borrowedCoin,
996
+ borrowedValueInUsd: debt.borrowedValueWithWeight,
997
+ borrowApr: market.pools[debt.coinName]?.borrowApr,
998
+ borrowApy: market.pools[debt.coinName]?.borrowApy,
999
+ incentiveInfos: (
1000
+ obligationAccount.borrowIncentives[debt.coinName]?.rewards ?? []
1001
1001
  )
1002
- .map((t) => ({
1003
- coinName: t.coinName,
1004
- symbol: t.symbol,
1005
- coinType: t.coinType,
1006
- incentiveApr: t.rewardApr,
1007
- })),
1008
- })),
1002
+ .filter(
1003
+ (t): t is NonNullable<typeof t> =>
1004
+ !!t && isFinite(t.baseRewardApr)
1005
+ )
1006
+ .map((t) => ({
1007
+ coinName: t.coinName,
1008
+ symbol: t.symbol,
1009
+ coinType: t.coinType,
1010
+ boostValue: t.boostValue,
1011
+ maxBoost: t.maxBoost,
1012
+ incentiveApr: t.baseRewardApr,
1013
+ boostedIncentiveApr: t.boostedRewardApr,
1014
+ })),
1015
+ };
1016
+ }),
1009
1017
  };
1010
1018
  });
1011
1019
 
@@ -1,134 +1,6 @@
1
- import { SuiObjectData } from '@mysten/sui/client';
2
- import type { ScallopAddress, ScallopQuery, ScallopSuiKit } from 'src/models';
3
- import type { CoinPrices, MarketPools, OptionalKeys } from '../types';
4
1
  import BigNumber from 'bignumber.js';
5
-
6
- /**
7
- * Get price from pyth fee object.
8
- *
9
- * @param query - The Scallop query instance.
10
- * @param assetCoinName - Specific support asset coin name.
11
- * @return Asset coin price.
12
- */
13
- export const getPythPrice = async (
14
- {
15
- address,
16
- scallopSuiKit,
17
- }: {
18
- address: ScallopAddress;
19
- scallopSuiKit: ScallopSuiKit;
20
- },
21
- assetCoinName: string,
22
- priceFeedObject?: SuiObjectData | null
23
- ) => {
24
- const pythFeedObjectId = address.get(
25
- `core.coins.${assetCoinName}.oracle.pyth.feedObject`
26
- );
27
- priceFeedObject =
28
- priceFeedObject ||
29
- (await scallopSuiKit.queryGetObject(pythFeedObjectId))?.data;
30
-
31
- if (priceFeedObject) {
32
- const priceFeedPoolObject = priceFeedObject;
33
- if (
34
- priceFeedPoolObject.content &&
35
- 'fields' in priceFeedPoolObject.content
36
- ) {
37
- const fields = priceFeedPoolObject.content.fields as any;
38
- const expoMagnitude = Number(
39
- fields.price_info.fields.price_feed.fields.price.fields.expo.fields
40
- .magnitude
41
- );
42
- const expoNegative = Number(
43
- fields.price_info.fields.price_feed.fields.price.fields.expo.fields
44
- .negative
45
- );
46
- const priceMagnitude = Number(
47
- fields.price_info.fields.price_feed.fields.price.fields.price.fields
48
- .magnitude
49
- );
50
- const priceNegative = Number(
51
- fields.price_info.fields.price_feed.fields.price.fields.price.fields
52
- .negative
53
- );
54
-
55
- return (
56
- priceMagnitude *
57
- 10 ** ((expoNegative ? -1 : 1) * expoMagnitude) *
58
- (priceNegative ? -1 : 1)
59
- );
60
- }
61
- }
62
-
63
- return 0;
64
- };
65
-
66
- export const getPythPrices = async (
67
- {
68
- address,
69
- scallopSuiKit,
70
- }: {
71
- address: ScallopAddress;
72
- scallopSuiKit: ScallopSuiKit;
73
- },
74
- assetCoinNames: string[]
75
- ) => {
76
- const pythPriceFeedIds = assetCoinNames.reduce(
77
- (prev, assetCoinName) => {
78
- const pythPriceFeed = address.get(
79
- `core.coins.${assetCoinName}.oracle.pyth.feedObject`
80
- );
81
- if (pythPriceFeed) {
82
- if (!prev[pythPriceFeed]) {
83
- prev[pythPriceFeed] = [assetCoinName];
84
- } else {
85
- prev[pythPriceFeed].push(assetCoinName);
86
- }
87
- }
88
- return prev;
89
- },
90
- {} as Record<string, string[]>
91
- );
92
-
93
- // Fetch multiple objects at once to save rpc calls
94
- const priceFeedObjects = await scallopSuiKit.queryGetObjects(
95
- Object.keys(pythPriceFeedIds)
96
- );
97
-
98
- const assetToPriceFeedMapping = priceFeedObjects.reduce(
99
- (prev, priceFeedObject) => {
100
- pythPriceFeedIds[priceFeedObject.objectId].forEach((assetCoinName) => {
101
- prev[assetCoinName] = priceFeedObject;
102
- });
103
- return prev;
104
- },
105
- {} as Record<string, SuiObjectData>
106
- );
107
-
108
- return (
109
- await Promise.all(
110
- Object.entries(assetToPriceFeedMapping).map(
111
- async ([assetCoinName, priceFeedObject]) => ({
112
- coinName: assetCoinName,
113
- price: await getPythPrice(
114
- {
115
- address,
116
- scallopSuiKit,
117
- },
118
- assetCoinName as string,
119
- priceFeedObject
120
- ),
121
- })
122
- )
123
- )
124
- ).reduce(
125
- (prev, curr) => {
126
- prev[curr.coinName as string] = curr.price;
127
- return prev;
128
- },
129
- {} as Record<string, number>
130
- );
131
- };
2
+ import type { ScallopQuery } from 'src/models';
3
+ import type { CoinPrices, MarketPools, OptionalKeys } from '../types';
132
4
 
133
5
  export const getAllCoinPrices = async (
134
6
  query: ScallopQuery,
@@ -192,13 +192,18 @@ const getTotalVeScaTreasuryAmount = async (
192
192
  initialSharedVersion: '1',
193
193
  });
194
194
 
195
- const treasuryRef = txb.sharedObjectRef({
196
- ...(await getSharedObjectData(
195
+ const [treasuryVersion, veScaConfigVersion] = await Promise.all([
196
+ getSharedObjectData(
197
197
  typeof veScaTreasury === 'string'
198
198
  ? veScaTreasury
199
199
  : veScaTreasury.objectId,
200
200
  utils.scallopSuiKit
201
- )),
201
+ ),
202
+ getSharedObjectData(veScaConfig, utils.scallopSuiKit),
203
+ ]);
204
+
205
+ const treasuryRef = txb.sharedObjectRef({
206
+ ...treasuryVersion,
202
207
  mutable: true,
203
208
  });
204
209
 
@@ -206,7 +211,7 @@ const getTotalVeScaTreasuryAmount = async (
206
211
  const refreshQueryTarget = `${veScaPkgId}::treasury::refresh`;
207
212
  const refreshArgs = [
208
213
  txb.sharedObjectRef({
209
- ...(await getSharedObjectData(veScaConfig, utils.scallopSuiKit)),
214
+ ...veScaConfigVersion,
210
215
  mutable: false,
211
216
  }),
212
217
  treasuryRef,
@@ -69,6 +69,7 @@ export type ObligationAccount = {
69
69
  totalRewardedPools: number;
70
70
  collaterals: OptionalKeys<Record<string, ObligationCollateral>>;
71
71
  debts: OptionalKeys<Record<string, ObligationDebt>>;
72
+ // @deprecated: incentive info moved to 'debts' field
72
73
  borrowIncentives: OptionalKeys<Record<string, ObligationBorrowIncentive>>;
73
74
  };
74
75
 
@@ -106,6 +107,12 @@ export type ObligationDebt = {
106
107
  availableBorrowCoin: number;
107
108
  availableRepayAmount: number;
108
109
  availableRepayCoin: number;
110
+ rewards: {
111
+ boostValue: number;
112
+ maxBoost: number;
113
+ baseRewardApr: number;
114
+ boostedRewardApr: number;
115
+ }[];
109
116
  };
110
117
 
111
118
  export type ObligationBorrowIncentiveReward = {
@@ -117,7 +124,10 @@ export type ObligationBorrowIncentiveReward = {
117
124
  weightedBorrowAmount: number;
118
125
  availableClaimCoin: number;
119
126
  availableClaimAmount: number;
127
+ baseRewardApr: number;
120
128
  boostValue: number;
129
+ boostedRewardApr: number;
130
+ maxBoost: number;
121
131
  };
122
132
 
123
133
  export type ObligationBorrowIncentive = {
@@ -524,22 +524,17 @@ export const calculateBorrowIncentivePoolPointData = (
524
524
  .multipliedBy(rewardCoinPrice);
525
525
 
526
526
  const weightScale = BigNumber(1_000_000_000_000);
527
+ const rewardScale = BigNumber(
528
+ parsedBorrowIncentivePoolPointData.baseWeight
529
+ ).dividedBy(weightScale);
527
530
 
528
531
  const rewardRate =
529
532
  rewardValueForYear
530
- .multipliedBy(
531
- BigNumber(parsedBorrowIncentivePoolPointData.baseWeight).dividedBy(
532
- weightScale
533
- )
534
- )
533
+ .multipliedBy(rewardScale)
535
534
  .dividedBy(weightedStakedValue)
536
535
  .isFinite() && parsedBorrowIncentivePoolPointData.points > 0
537
536
  ? rewardValueForYear
538
- .multipliedBy(
539
- BigNumber(parsedBorrowIncentivePoolPointData.baseWeight).dividedBy(
540
- weightScale
541
- )
542
- )
537
+ .multipliedBy(rewardScale)
543
538
  .dividedBy(weightedStakedValue)
544
539
  .toNumber()
545
540
  : Infinity;