@suilend/sdk 3.0.8 → 5.0.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.
Files changed (73) hide show
  1. package/_generated/_dependencies/source/0x1/ascii/structs.d.ts +4 -4
  2. package/_generated/_dependencies/source/0x1/ascii/structs.js +42 -41
  3. package/_generated/_dependencies/source/0x1/option/structs.js +22 -26
  4. package/_generated/_dependencies/source/0x1/type-name/structs.d.ts +3 -3
  5. package/_generated/_dependencies/source/0x1/type-name/structs.js +21 -25
  6. package/_generated/_dependencies/source/0x2/bag/structs.d.ts +5 -5
  7. package/_generated/_dependencies/source/0x2/bag/structs.js +22 -25
  8. package/_generated/_dependencies/source/0x2/balance/structs.d.ts +4 -4
  9. package/_generated/_dependencies/source/0x2/balance/structs.js +42 -41
  10. package/_generated/_dependencies/source/0x2/object/structs.d.ts +5 -5
  11. package/_generated/_dependencies/source/0x2/object/structs.js +42 -41
  12. package/_generated/_dependencies/source/0x2/object-table/structs.d.ts +5 -5
  13. package/_generated/_dependencies/source/0x2/object-table/structs.js +22 -25
  14. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs.d.ts +3 -3
  15. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs.js +22 -25
  16. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs.d.ts +9 -9
  17. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs.js +24 -25
  18. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs.d.ts +21 -21
  19. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs.js +23 -25
  20. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.d.ts +2 -2
  21. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js +21 -25
  22. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs.d.ts +52 -52
  23. package/_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs.js +45 -41
  24. package/_generated/_framework/vector.js +9 -6
  25. package/_generated/suilend/cell/structs.d.ts +1 -1
  26. package/_generated/suilend/cell/structs.js +21 -25
  27. package/_generated/suilend/decimal/structs.d.ts +2 -2
  28. package/_generated/suilend/decimal/structs.js +21 -25
  29. package/_generated/suilend/lending-market/structs.d.ts +129 -129
  30. package/_generated/suilend/lending-market/structs.js +391 -265
  31. package/_generated/suilend/liquidity-mining/structs.d.ts +44 -44
  32. package/_generated/suilend/liquidity-mining/structs.js +122 -89
  33. package/_generated/suilend/obligation/structs.d.ts +98 -98
  34. package/_generated/suilend/obligation/structs.js +194 -121
  35. package/_generated/suilend/rate-limiter/structs.d.ts +12 -12
  36. package/_generated/suilend/rate-limiter/structs.js +46 -41
  37. package/_generated/suilend/reserve/structs.d.ts +128 -128
  38. package/_generated/suilend/reserve/structs.js +243 -153
  39. package/_generated/suilend/reserve-config/structs.d.ts +30 -30
  40. package/_generated/suilend/reserve-config/structs.js +60 -41
  41. package/api/events.js +5 -14
  42. package/client.js +267 -319
  43. package/lib/initialize.d.ts +85 -3
  44. package/lib/initialize.js +119 -52
  45. package/lib/liquidityMining.js +15 -19
  46. package/lib/pyth.d.ts +16 -0
  47. package/lib/pyth.js +30 -15
  48. package/lib/pythAdapter.js +96 -112
  49. package/lib/strategyOwnerCap.js +8 -17
  50. package/margin/margin/admin_cap.d.ts +2 -2
  51. package/margin/margin/deps/std/type_name.d.ts +1 -1
  52. package/margin/margin/deps/suilend/lending_market.d.ts +2 -2
  53. package/margin/margin/market.d.ts +11 -11
  54. package/margin/margin/market.js +22 -22
  55. package/margin/margin/permissions.js +14 -14
  56. package/margin/margin/position.d.ts +47 -47
  57. package/margin/margin/position.js +10 -10
  58. package/margin/margin/router.d.ts +5 -5
  59. package/margin/margin/router.js +16 -16
  60. package/margin/margin/version.d.ts +1 -1
  61. package/margin/utils/index.js +22 -36
  62. package/package.json +1 -1
  63. package/parsers/apiReserveAssetDataEvent.js +4 -1
  64. package/parsers/lendingMarket.js +3 -4
  65. package/parsers/reserve.js +4 -1
  66. package/strategies.js +188 -204
  67. package/swap/quote.d.ts +1 -1
  68. package/swap/quote.js +28 -39
  69. package/swap/transaction.js +17 -26
  70. package/utils/events.js +20 -32
  71. package/utils/obligation.js +111 -135
  72. package/utils/simulate.d.ts +68 -1
  73. package/utils/simulate.js +149 -31
@@ -1,4 +1,4 @@
1
- import { SuiPriceServiceConnection } from "@pythnetwork/pyth-sui-js";
1
+ import { PriceFeed, SuiPriceServiceConnection } from "@pythnetwork/pyth-sui-js";
2
2
  import BigNumber from "bignumber.js";
3
3
  import { Decimal } from "../_generated/suilend/decimal/structs";
4
4
  import { PoolRewardManager, UserRewardManager } from "../_generated/suilend/liquidity-mining/structs";
@@ -22,6 +22,73 @@ export declare const calculateDepositAprPercent: (reserve: Reserve<string>) => B
22
22
  export declare const compoundReserveInterest: (reserve: Reserve<string>, nowS: number) => Reserve<string>;
23
23
  export declare const updatePoolRewardsManager: (manager: PoolRewardManager, nowMs: number) => PoolRewardManager;
24
24
  export declare const refreshReservePrice: (reserves: Reserve<string>[], pythConnection: SuiPriceServiceConnection) => Promise<Reserve<string>[]>;
25
+ /**
26
+ * A connection that can report which of the requested feeds it could not serve,
27
+ * instead of failing the whole batch. Structural on purpose: any object with
28
+ * this method qualifies, so a caller can inject its own client without this
29
+ * package depending on it.
30
+ */
31
+ export interface PartialPriceFeedSource {
32
+ getLatestPriceFeedsPartial(ids: string[]): Promise<{
33
+ feeds: PriceFeed[];
34
+ missing: string[];
35
+ }>;
36
+ }
37
+ /**
38
+ * Whether this connection can report WHICH requested feeds it could not serve.
39
+ *
40
+ * Exported because it is a precondition callers need to check, not just an
41
+ * internal branch: `tolerateMissingPriceFeeds` is meaningless without it (see
42
+ * `refreshReservesForInitialize`, which refuses the combination).
43
+ */
44
+ export declare function canReportMissingPriceFeeds(connection: SuiPriceServiceConnection | PartialPriceFeedSource): connection is PartialPriceFeedSource;
45
+ /**
46
+ * The remedy both gap-tolerance failure paths point at.
47
+ *
48
+ * One constant because the two paths fail at different moments for the same
49
+ * underlying reason — `refreshReservesForInitialize` refuses up front on
50
+ * capability, `refreshReservePriceTolerant` only once an endpoint actually
51
+ * rejects a batch — and separately worded advice for one cause is how the two
52
+ * drift apart.
53
+ */
54
+ export declare const PARTIAL_FETCH_REQUIRED_HINT = "inject a connection implementing getLatestPriceFeedsPartial (e.g. a failover connection that reports per-feed gaps)";
55
+ /**
56
+ * Like `refreshReservePrice`, but a reserve whose feed no endpoint can serve
57
+ * keeps the price already in its on-chain state instead of failing every other
58
+ * reserve with it.
59
+ *
60
+ * `refreshReservePrice` is all-or-nothing in both directions, and both are
61
+ * hazardous once an endpoint stops carrying every feed: a missing feed throws
62
+ * (so one retired market blinds pricing for the whole lending market), while an
63
+ * undefined response silently returns EVERY reserve at its stale price with no
64
+ * signal at all. Hermes v2 rejects an entire batch when any requested id is
65
+ * unknown, which makes both reachable in practice.
66
+ *
67
+ * This reports `unpricedCoinTypes` rather than deciding what an unpriceable
68
+ * reserve means — that judgement needs reserve config the caller has and this
69
+ * function should not second-guess. Nothing is ever fabricated here.
70
+ *
71
+ * TWO THINGS A CALLER MUST KNOW:
72
+ *
73
+ * 1. An unpriced reserve is returned in `reserves` carrying whatever price its
74
+ * on-chain state holds — which may be ZERO for a reserve that was never
75
+ * priced on-chain. Folding `reserves` into collateral or borrow-limit math
76
+ * without first excluding `unpricedCoinTypes` therefore prices that reserve
77
+ * at literal zero. `unpricedCoinTypes` is the only thing distinguishing a
78
+ * real price from a kept one; it is not optional to read.
79
+ * 2. Tolerance requires a connection that can REPORT a gap
80
+ * (`getLatestPriceFeedsPartial`). Handed a stock `SuiPriceServiceConnection`,
81
+ * this falls back to the all-or-nothing fetch, which Hermes v2 answers with
82
+ * a 404 for the whole batch when any id is unknown — so there is nothing
83
+ * left to be tolerant with. That fetch is kept because it is correct for an
84
+ * endpoint that OMITS unknown ids rather than rejecting the batch, but the
85
+ * failure is re-thrown with attribution rather than surfacing as a bare HTTP
86
+ * error. `refreshReservesForInitialize` rejects the combination outright.
87
+ */
88
+ export declare const refreshReservePriceTolerant: (reserves: Reserve<string>[], pythConnection: SuiPriceServiceConnection | PartialPriceFeedSource) => Promise<{
89
+ reserves: Reserve<string>[];
90
+ unpricedCoinTypes: string[];
91
+ }>;
25
92
  export declare const updateUserRewardManager: (poolManager: PoolRewardManager, userRewardManager: UserRewardManager, nowMs: number) => UserRewardManager;
26
93
  export declare const refreshObligation: (unrefreshedObligation: Obligation<string>, refreshedReserves: Reserve<string>[]) => Obligation<string>;
27
94
  export declare const numberToDecimal: (value: number) => Decimal;
package/utils/simulate.js CHANGED
@@ -1,14 +1,5 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { bcs } from "@mysten/sui/bcs";
11
- import { toHex } from "@mysten/sui/utils";
2
+ import { normalizeStructTag, toHex } from "@mysten/sui/utils";
12
3
  import BigNumber from "bignumber.js";
13
4
  import { v4 as uuidv4 } from "uuid";
14
5
  import { Decimal } from "../_generated/suilend/decimal/structs.js";
@@ -68,7 +59,6 @@ export const calculateDepositAprPercent = (reserve) => {
68
59
  return calculateSupplyApr(reserve);
69
60
  };
70
61
  export const compoundReserveInterest = (reserve, nowS) => {
71
- var _a, _b;
72
62
  const timeElapsedSeconds = nowS - Number(reserve.interestLastUpdateTimestampS);
73
63
  if (timeElapsedSeconds === 0) {
74
64
  return reserve;
@@ -79,13 +69,13 @@ export const compoundReserveInterest = (reserve, nowS) => {
79
69
  .div(100)
80
70
  .div(365 * 24 * 60 * 60))
81
71
  .pow(new BigNumber(timeElapsedSeconds).toNumber());
82
- const updatedReserve = Object.assign({}, reserve);
72
+ const updatedReserve = { ...reserve };
83
73
  const oldBorrowedAmount = decimalToBigNumber(reserve.borrowedAmount);
84
74
  const oldCumulativeBorrowRate = decimalToBigNumber(reserve.cumulativeBorrowRate);
85
75
  const oldUnclaimedSpreadFees = decimalToBigNumber(reserve.unclaimedSpreadFees);
86
76
  updatedReserve.cumulativeBorrowRate = stringToDecimal(compoundedBorrowRate.multipliedBy(oldCumulativeBorrowRate).toString());
87
77
  const netNewDebt = oldBorrowedAmount.multipliedBy(compoundedBorrowRate.minus(1));
88
- const spreadFee = new BigNumber(((_b = (_a = reserve.config.element) === null || _a === void 0 ? void 0 : _a.spreadFeeBps) === null || _b === void 0 ? void 0 : _b.toString()) || 0);
78
+ const spreadFee = new BigNumber(reserve.config.element?.spreadFeeBps?.toString() || 0);
89
79
  updatedReserve.unclaimedSpreadFees = stringToDecimal(oldUnclaimedSpreadFees
90
80
  .plus(netNewDebt.multipliedBy(spreadFee.dividedBy(10000)))
91
81
  .toString());
@@ -96,7 +86,7 @@ export const compoundReserveInterest = (reserve, nowS) => {
96
86
  return updatedReserve;
97
87
  };
98
88
  export const updatePoolRewardsManager = (manager, nowMs) => {
99
- const updatedManager = Object.assign({}, manager);
89
+ const updatedManager = { ...manager };
100
90
  const timeElapsedMs = nowMs - Number(manager.lastUpdateTimeMs);
101
91
  if (manager.totalShares === BigInt(0) || timeElapsedMs === 0) {
102
92
  return updatedManager;
@@ -109,7 +99,7 @@ export const updatePoolRewardsManager = (manager, nowMs) => {
109
99
  manager.lastUpdateTimeMs >= poolReward.endTimeMs) {
110
100
  return poolReward;
111
101
  }
112
- const updatedPoolReward = Object.assign({}, poolReward);
102
+ const updatedPoolReward = { ...poolReward };
113
103
  const endTimeMs = Number(poolReward.endTimeMs);
114
104
  const startTimeMs = Number(poolReward.startTimeMs);
115
105
  const lastUpdateTimeMs = Number(manager.lastUpdateTimeMs);
@@ -128,27 +118,153 @@ export const updatePoolRewardsManager = (manager, nowMs) => {
128
118
  updatedManager.lastUpdateTimeMs = BigInt(nowMs);
129
119
  return updatedManager;
130
120
  };
131
- export const refreshReservePrice = (reserves, pythConnection) => __awaiter(void 0, void 0, void 0, function* () {
132
- const priceIdentifiers = Array.from(new Set(reserves.map((r) => toHex(new Uint8Array(r.priceIdentifier.bytes)))));
133
- const priceFeeds = yield pythConnection.getLatestPriceFeeds(priceIdentifiers);
121
+ /** The Pyth feed id a reserve is priced from, in the response's `id` encoding. */
122
+ const reserveFeedId = (reserve) => toHex(new Uint8Array(reserve.priceIdentifier.bytes));
123
+ /**
124
+ * Index feeds by id for O(1) lookup, keeping the FIRST entry per id.
125
+ *
126
+ * First-wins matches the `priceFeeds.find(...)` this replaced, so a duplicated
127
+ * id in a response resolves to the same feed it always did. Pyth does not
128
+ * duplicate ids today; the point is that switching to a Map is not allowed to
129
+ * quietly change which one is picked if it ever does.
130
+ */
131
+ const indexFeedsById = (priceFeeds) => {
132
+ const byId = new Map();
133
+ for (const feed of priceFeeds)
134
+ if (!byId.has(feed.id))
135
+ byId.set(feed.id, feed);
136
+ return byId;
137
+ };
138
+ /**
139
+ * Apply a feed's price to a COPY of `reserve`.
140
+ *
141
+ * Shared by both refresh functions so the price-mapping math has exactly one
142
+ * definition. It was duplicated verbatim, which meant a change to how a feed
143
+ * becomes a price had to be made twice or the two silently diverged.
144
+ *
145
+ * The two functions still own their own control flow — what a MISSING feed
146
+ * means is precisely where they are supposed to differ (see
147
+ * `refreshReservePriceTolerant`).
148
+ */
149
+ const withFeedPrice = (reserve, priceFeed) => {
150
+ const newReserve = { ...reserve };
151
+ newReserve.price = stringToDecimal(priceFeed.getPriceUnchecked().getPriceAsNumberUnchecked().toString());
152
+ newReserve.smoothedPrice = stringToDecimal(priceFeed.getEmaPriceUnchecked().getPriceAsNumberUnchecked().toString());
153
+ newReserve.priceLastUpdateTimestampS = BigInt(priceFeed.getPriceUnchecked().publishTime);
154
+ return newReserve;
155
+ };
156
+ export const refreshReservePrice = async (reserves, pythConnection) => {
157
+ const priceIdentifiers = Array.from(new Set(reserves.map(reserveFeedId)));
158
+ const priceFeeds = await pythConnection.getLatestPriceFeeds(priceIdentifiers);
159
+ // Deliberately NOT a throw, and deliberately not expressed over the tolerant
160
+ // function below: consumers outside this repo depend on `undefined` meaning
161
+ // "keep every reserve's existing price". Hazardous (see the tolerant
162
+ // function's comment), but changing it here would turn a quiet failure loud
163
+ // under callers who never asked for that.
134
164
  if (!priceFeeds)
135
165
  return reserves;
166
+ const byId = indexFeedsById(priceFeeds);
136
167
  const updatedReserves = [];
137
168
  for (let i = 0; i < reserves.length; i++) {
138
169
  const reserve = reserves[i];
139
- const priceFeed = priceFeeds.find((p) => p.id === toHex(new Uint8Array(reserve.priceIdentifier.bytes)));
170
+ const priceFeed = byId.get(reserveFeedId(reserve));
140
171
  if (!priceFeed)
141
172
  throw new Error(`Price feed not found for reserve ${reserve.coinType.name}`);
142
- const newReserve = Object.assign({}, reserve);
143
- newReserve.price = stringToDecimal(priceFeed.getPriceUnchecked().getPriceAsNumberUnchecked().toString());
144
- newReserve.smoothedPrice = stringToDecimal(priceFeed.getEmaPriceUnchecked().getPriceAsNumberUnchecked().toString());
145
- newReserve.priceLastUpdateTimestampS = BigInt(priceFeed.getPriceUnchecked().publishTime);
146
- updatedReserves.push(newReserve);
173
+ updatedReserves.push(withFeedPrice(reserve, priceFeed));
147
174
  }
148
175
  return updatedReserves;
149
- });
176
+ };
177
+ /**
178
+ * Whether this connection can report WHICH requested feeds it could not serve.
179
+ *
180
+ * Exported because it is a precondition callers need to check, not just an
181
+ * internal branch: `tolerateMissingPriceFeeds` is meaningless without it (see
182
+ * `refreshReservesForInitialize`, which refuses the combination).
183
+ */
184
+ export function canReportMissingPriceFeeds(connection) {
185
+ return (typeof connection.getLatestPriceFeedsPartial ===
186
+ "function");
187
+ }
188
+ /**
189
+ * The remedy both gap-tolerance failure paths point at.
190
+ *
191
+ * One constant because the two paths fail at different moments for the same
192
+ * underlying reason — `refreshReservesForInitialize` refuses up front on
193
+ * capability, `refreshReservePriceTolerant` only once an endpoint actually
194
+ * rejects a batch — and separately worded advice for one cause is how the two
195
+ * drift apart.
196
+ */
197
+ export const PARTIAL_FETCH_REQUIRED_HINT = "inject a connection implementing getLatestPriceFeedsPartial (e.g. a failover connection that reports per-feed gaps)";
198
+ /**
199
+ * Like `refreshReservePrice`, but a reserve whose feed no endpoint can serve
200
+ * keeps the price already in its on-chain state instead of failing every other
201
+ * reserve with it.
202
+ *
203
+ * `refreshReservePrice` is all-or-nothing in both directions, and both are
204
+ * hazardous once an endpoint stops carrying every feed: a missing feed throws
205
+ * (so one retired market blinds pricing for the whole lending market), while an
206
+ * undefined response silently returns EVERY reserve at its stale price with no
207
+ * signal at all. Hermes v2 rejects an entire batch when any requested id is
208
+ * unknown, which makes both reachable in practice.
209
+ *
210
+ * This reports `unpricedCoinTypes` rather than deciding what an unpriceable
211
+ * reserve means — that judgement needs reserve config the caller has and this
212
+ * function should not second-guess. Nothing is ever fabricated here.
213
+ *
214
+ * TWO THINGS A CALLER MUST KNOW:
215
+ *
216
+ * 1. An unpriced reserve is returned in `reserves` carrying whatever price its
217
+ * on-chain state holds — which may be ZERO for a reserve that was never
218
+ * priced on-chain. Folding `reserves` into collateral or borrow-limit math
219
+ * without first excluding `unpricedCoinTypes` therefore prices that reserve
220
+ * at literal zero. `unpricedCoinTypes` is the only thing distinguishing a
221
+ * real price from a kept one; it is not optional to read.
222
+ * 2. Tolerance requires a connection that can REPORT a gap
223
+ * (`getLatestPriceFeedsPartial`). Handed a stock `SuiPriceServiceConnection`,
224
+ * this falls back to the all-or-nothing fetch, which Hermes v2 answers with
225
+ * a 404 for the whole batch when any id is unknown — so there is nothing
226
+ * left to be tolerant with. That fetch is kept because it is correct for an
227
+ * endpoint that OMITS unknown ids rather than rejecting the batch, but the
228
+ * failure is re-thrown with attribution rather than surfacing as a bare HTTP
229
+ * error. `refreshReservesForInitialize` rejects the combination outright.
230
+ */
231
+ export const refreshReservePriceTolerant = async (reserves, pythConnection) => {
232
+ const priceIdentifiers = Array.from(new Set(reserves.map(reserveFeedId)));
233
+ let priceFeeds;
234
+ if (canReportMissingPriceFeeds(pythConnection)) {
235
+ priceFeeds = (await pythConnection.getLatestPriceFeedsPartial(priceIdentifiers)).feeds;
236
+ }
237
+ else {
238
+ try {
239
+ priceFeeds = await pythConnection.getLatestPriceFeeds(priceIdentifiers);
240
+ }
241
+ catch (error) {
242
+ // Not swallowed — a transport failure must stay a failure, or every
243
+ // reserve silently reports as unpriced and the caller cannot tell a dead
244
+ // endpoint from a retired feed. Re-thrown only to name the cause, which a
245
+ // bare "HTTP error! status: 404" does not.
246
+ throw new Error(`Tolerant price refresh failed on an all-or-nothing connection: ${error instanceof Error ? error.message : String(error)}. This connection cannot report which feeds are missing, so an endpoint that rejects the whole batch over one unknown id (Hermes v2) defeats tolerance entirely — ${PARTIAL_FETCH_REQUIRED_HINT}.`, { cause: error });
247
+ }
248
+ }
249
+ const byId = indexFeedsById(priceFeeds ?? []);
250
+ const unpricedCoinTypes = [];
251
+ const updatedReserves = reserves.map((reserve) => {
252
+ const priceFeed = byId.get(reserveFeedId(reserve));
253
+ if (!priceFeed) {
254
+ // Normalized to match every other coin type this SDK returns
255
+ // (`reserveMap`, `reserveCoinTypes`, and the parsers are all
256
+ // normalizeStructTag'd). The raw on-chain form never compares equal to
257
+ // one of those keys — it lacks the `0x` prefix — so reporting it raw made
258
+ // the cross-reference this field exists for silently never match.
259
+ unpricedCoinTypes.push(normalizeStructTag(reserve.coinType.name));
260
+ return reserve;
261
+ }
262
+ return withFeedPrice(reserve, priceFeed);
263
+ });
264
+ return { reserves: updatedReserves, unpricedCoinTypes };
265
+ };
150
266
  export const updateUserRewardManager = (poolManager, userRewardManager, nowMs) => {
151
- const updatedUserRewardManager = Object.assign({}, userRewardManager);
267
+ const updatedUserRewardManager = { ...userRewardManager };
152
268
  for (let i = 0; i < poolManager.poolRewards.length; i++) {
153
269
  const poolReward = poolManager.poolRewards[i];
154
270
  if (poolReward == null) {
@@ -158,7 +274,7 @@ export const updateUserRewardManager = (poolManager, userRewardManager, nowMs) =
158
274
  userRewardManager.rewards.push(null);
159
275
  }
160
276
  const oldReward = updatedUserRewardManager.rewards[i];
161
- const reward = Object.assign({}, oldReward);
277
+ const reward = { ...oldReward };
162
278
  if (oldReward != null) {
163
279
  const newRewards = decimalToBigNumber(poolReward.cumulativeRewardsPerShare)
164
280
  .minus(decimalToBigNumber(oldReward.cumulativeRewardsPerShare))
@@ -183,13 +299,13 @@ export const updateUserRewardManager = (poolManager, userRewardManager, nowMs) =
183
299
  return updatedUserRewardManager;
184
300
  };
185
301
  export const refreshObligation = (unrefreshedObligation, refreshedReserves) => {
186
- const obligation = Object.assign({}, unrefreshedObligation);
302
+ const obligation = { ...unrefreshedObligation };
187
303
  // Refresh Deposits
188
304
  let depositValueUsd = new BigNumber(0);
189
305
  let allowedBorrowValueUsd = new BigNumber(0);
190
306
  let unhealthyBorrowValueUsd = new BigNumber(0);
191
307
  for (let i = 0; i < obligation.deposits.length; i++) {
192
- const deposit = Object.assign({}, obligation.deposits[i]);
308
+ const deposit = { ...obligation.deposits[i] };
193
309
  const reserve = refreshedReserves.find((r) => r.coinType.name === deposit.coinType.name);
194
310
  if (!reserve) {
195
311
  throw new Error(`Unable to find reserve for coin type ${deposit.coinType.name}`);
@@ -219,7 +335,9 @@ export const refreshObligation = (unrefreshedObligation, refreshedReserves) => {
219
335
  throw new Error(`Unable to find reserve for coin type ${unrefreshedBorrow.coinType.name}`);
220
336
  }
221
337
  const config = reserve.config.element;
222
- const borrow = Object.assign({}, compoundDebt(unrefreshedBorrow, reserve));
338
+ const borrow = {
339
+ ...compoundDebt(unrefreshedBorrow, reserve),
340
+ };
223
341
  obligation.userRewardManagers[Number(borrow.userRewardManagerIndex)] =
224
342
  updateUserRewardManager(reserve.borrowsPoolRewardManager, obligation.userRewardManagers[Number(borrow.userRewardManagerIndex)], Date.now());
225
343
  const marketValue = decimalToBigNumber(borrow.borrowedAmount)
@@ -276,7 +394,7 @@ export const totalSupply = (reserve) => {
276
394
  .minus(decimalToBigNumber(reserve.unclaimedSpreadFees));
277
395
  };
278
396
  export const compoundDebt = (borrow, reserve) => {
279
- const borrowCopy = Object.assign({}, borrow);
397
+ const borrowCopy = { ...borrow };
280
398
  const newCumulativeBorrowRate = decimalToBigNumber(reserve.cumulativeBorrowRate);
281
399
  const compoundedInterestRate = newCumulativeBorrowRate.dividedBy(decimalToBigNumber(borrow.cumulativeBorrowRate));
282
400
  borrowCopy.borrowedAmount = stringToDecimal(decimalToBigNumber(borrow.borrowedAmount)