@suilend/sdk 14.2.0-next.gf80403d → 14.2.1-next.gbfd3a97

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/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@suilend/sdk","version":"14.2.0-next.gf80403d","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/interpolate":"./utils/interpolate.js","./utils/obligation":"./utils/obligation.js","./utils/priceGuard":"./utils/priceGuard.js","./utils/simulate":"./utils/simulate.js","./_generated/_envs":"./_generated/_envs/index.js","./_generated/_envs/mainnet":"./_generated/_envs/mainnet.js","./_generated/_framework/env":"./_generated/_framework/env.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/pyth-2/price-identifier/structs":"./_generated/_dependencies/pyth-2/price-identifier/structs.js","./_generated/_dependencies/std/ascii/structs":"./_generated/_dependencies/std/ascii/structs.js","./_generated/_dependencies/std/option/structs":"./_generated/_dependencies/std/option/structs.js","./_generated/_dependencies/std/type-name/structs":"./_generated/_dependencies/std/type-name/structs.js","./_generated/_dependencies/sui/bag/structs":"./_generated/_dependencies/sui/bag/structs.js","./_generated/_dependencies/sui/balance/structs":"./_generated/_dependencies/sui/balance/structs.js","./_generated/_dependencies/sui/object/structs":"./_generated/_dependencies/sui/object/structs.js","./_generated/_dependencies/sui/object-table/structs":"./_generated/_dependencies/sui/object-table/structs.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js"},"types":"./index.d.ts","scripts":{"codegen":"node ./codegen.js","codegen:install":"node ./install-codegen.js","build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@cetusprotocol/aggregator-sdk":"1.7.3","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.3","uuid":"14.0.2"},"devDependencies":{"@mysten/bcs":"2.1.2","@mysten/sui":"2.35.0","@suilend/sui-core":"2.2.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.26","@types/node":"26.6.5","fast-check":"4.10.2","prettier":"3.9.10","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.4","vitest":"5.0.4"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.31.3 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
1
+ {"name":"@suilend/sdk","version":"14.2.1-next.gbfd3a97","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/interpolate":"./utils/interpolate.js","./utils/obligation":"./utils/obligation.js","./utils/priceGuard":"./utils/priceGuard.js","./utils/simulate":"./utils/simulate.js","./_generated/_envs":"./_generated/_envs/index.js","./_generated/_envs/mainnet":"./_generated/_envs/mainnet.js","./_generated/_framework/env":"./_generated/_framework/env.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/pyth-2/price-identifier/structs":"./_generated/_dependencies/pyth-2/price-identifier/structs.js","./_generated/_dependencies/std/ascii/structs":"./_generated/_dependencies/std/ascii/structs.js","./_generated/_dependencies/std/option/structs":"./_generated/_dependencies/std/option/structs.js","./_generated/_dependencies/std/type-name/structs":"./_generated/_dependencies/std/type-name/structs.js","./_generated/_dependencies/sui/bag/structs":"./_generated/_dependencies/sui/bag/structs.js","./_generated/_dependencies/sui/balance/structs":"./_generated/_dependencies/sui/balance/structs.js","./_generated/_dependencies/sui/object/structs":"./_generated/_dependencies/sui/object/structs.js","./_generated/_dependencies/sui/object-table/structs":"./_generated/_dependencies/sui/object-table/structs.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js"},"types":"./index.d.ts","scripts":{"codegen":"node ./codegen.js","codegen:install":"node ./install-codegen.js","build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@cetusprotocol/aggregator-sdk":"1.7.5","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.3","uuid":"14.0.3"},"devDependencies":{"@mysten/bcs":"2.1.3","@mysten/sui":"2.37.0","@suilend/sui-core":"2.2.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.27","@types/node":"26.6.5","fast-check":"4.10.2","prettier":"3.9.10","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.5","vitest":"5.0.5"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.31.3 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
package/strategies.js CHANGED
@@ -274,6 +274,37 @@ export const getStrategySuiReserve = (
274
274
  // AppContext
275
275
  reserveMap) => reserveMap[NORMALIZED_SUI_COINTYPE] ??
276
276
  { price: new BigNumber(1) };
277
+ const getStrategyDepositUnitValueUsd = (reserveMap, lstMap, coinType) => {
278
+ const suiPrice = getStrategySuiReserve(reserveMap).price;
279
+ const lstToSuiExchangeRate = lstMap[coinType]?.lstToSuiExchangeRate;
280
+ if (isSui(coinType))
281
+ return suiPrice;
282
+ if (lstToSuiExchangeRate !== undefined)
283
+ return new BigNumber(lstToSuiExchangeRate).times(suiPrice);
284
+ return reserveMap[coinType].price;
285
+ };
286
+ const getStrategyExtraDeposits = (reserveMap, lstMap, strategyType, deposits) => {
287
+ const { depositBaseCoinType, depositLstCoinType } = STRATEGY_TYPE_INFO_MAP[strategyType];
288
+ const getValueUsd = (deposit) => deposit.depositedAmount.times(getStrategyDepositUnitValueUsd(reserveMap, lstMap, deposit.coinType));
289
+ return deposits
290
+ .filter((deposit) => {
291
+ const reserve = reserveMap[deposit.coinType];
292
+ return (deposit.coinType !== depositBaseCoinType &&
293
+ deposit.coinType !== depositLstCoinType &&
294
+ reserve !== undefined &&
295
+ deposit.depositedAmount
296
+ .decimalPlaces(reserve.token.decimals, BigNumber.ROUND_DOWN)
297
+ .gt(0));
298
+ })
299
+ .sort((a, b) => getValueUsd(b).comparedTo(getValueUsd(a)) ?? 0);
300
+ };
301
+ const getStrategyWithdrawnCtokenAmount = (reserve, amount) => BigInt(new BigNumber(amount
302
+ .times(10 ** reserve.token.decimals)
303
+ .integerValue(BigNumber.ROUND_DOWN)
304
+ .toString())
305
+ .div(reserve.cTokenExchangeRate)
306
+ .integerValue(BigNumber.ROUND_UP)
307
+ .toString());
277
308
  export const fetchStrategyLstMap = async (suiGrpcClient) => {
278
309
  try {
279
310
  const lstCoinTypes = Array.from(new Set([
@@ -1310,6 +1341,87 @@ dryRunTransaction) => {
1310
1341
  }));
1311
1342
  if (borrowedAmount.eq(targetBorrowedAmount))
1312
1343
  return { deposits, borrowedAmount, transaction };
1344
+ const repayBorrowsUsingExtraDeposits = async () => {
1345
+ const skippedCoinTypes = new Set();
1346
+ for (let i = 0; i < 30; i++) {
1347
+ const pendingBorrowedAmount = borrowedAmount.minus(targetBorrowedAmount);
1348
+ if (pendingBorrowedAmount.lte(STRATEGY_E))
1349
+ return false;
1350
+ const extraDeposit = getStrategyExtraDeposits(reserveMap, lstMap, strategyType, deposits).find((deposit) => !skippedCoinTypes.has(deposit.coinType));
1351
+ if (extraDeposit === undefined)
1352
+ return false;
1353
+ const extraReserve = reserveMap[extraDeposit.coinType];
1354
+ const extraToBorrowExchangeRate = extraReserve.minPrice.div(borrowReserve.maxPrice);
1355
+ const stepMaxWithdrawnAmount = getStrategyStepMaxWithdrawnAmount(reserveMap, lstMap, strategyType, deposits, borrowedAmount, extraReserve.coinType, 0.9);
1356
+ const stepMaxRepaidAmount = stepMaxWithdrawnAmount
1357
+ .times(extraToBorrowExchangeRate)
1358
+ .decimalPlaces(borrowReserve.token.decimals, BigNumber.ROUND_DOWN);
1359
+ const stepTargetRepaidAmount = pendingBorrowedAmount.div(1 - SWAP_MAX_DEVIATION_PERCENT / 100);
1360
+ const isFinalStep = stepTargetRepaidAmount.lte(stepMaxRepaidAmount);
1361
+ const stepWithdrawnAmount = BigNumber.min(stepTargetRepaidAmount, stepMaxRepaidAmount)
1362
+ .div(extraToBorrowExchangeRate)
1363
+ .decimalPlaces(extraReserve.token.decimals, BigNumber.ROUND_DOWN);
1364
+ console.log(`[unloopStrategyToExposure] ${i} repay_using_extra_deposit |`, JSON.stringify({
1365
+ coinType: extraReserve.coinType,
1366
+ pendingBorrowedAmount: pendingBorrowedAmount.toFixed(20),
1367
+ stepMaxWithdrawnAmount: stepMaxWithdrawnAmount.toFixed(20),
1368
+ stepWithdrawnAmount: stepWithdrawnAmount.toFixed(20),
1369
+ isFinalStep,
1370
+ }, null, 2));
1371
+ if (!extraToBorrowExchangeRate.gt(0) || !stepWithdrawnAmount.gt(0)) {
1372
+ skippedCoinTypes.add(extraReserve.coinType);
1373
+ continue;
1374
+ }
1375
+ const transactionBeforeStep = Transaction.from(transaction);
1376
+ const depositsBeforeStep = cloneDeep(deposits);
1377
+ try {
1378
+ const [stepWithdrawnCoin] = strategyWithdraw(strategyType, extraReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(extraReserve.coinType), getStrategyWithdrawnCtokenAmount(extraReserve, stepWithdrawnAmount), transaction);
1379
+ deposits = addOrInsertStrategyDeposit(deposits, {
1380
+ coinType: extraReserve.coinType,
1381
+ depositedAmount: stepWithdrawnAmount.times(-1),
1382
+ });
1383
+ const routers = await cetusSdk.findRouters({
1384
+ from: extraReserve.coinType,
1385
+ target: borrowReserve.coinType,
1386
+ amount: new BN(stepWithdrawnAmount
1387
+ .times(10 ** extraReserve.token.decimals)
1388
+ .integerValue(BigNumber.ROUND_DOWN)
1389
+ .toString()),
1390
+ byAmountIn: true,
1391
+ splitCount: 0,
1392
+ });
1393
+ if (!routers)
1394
+ throw new Error("No swap quote found");
1395
+ const { slippagePercent, guaranteedAmountOut } = resolveSwapSlippage(stepWithdrawnAmount, new BigNumber(routers.amountOut.toString()).div(10 ** borrowReserve.token.decimals), extraReserve, borrowReserve);
1396
+ const stepBorrowCoin = (await cetusSdk.fixableRouterSwapV3({
1397
+ router: routers,
1398
+ inputCoin: stepWithdrawnCoin,
1399
+ slippage: slippagePercent / 100,
1400
+ txb: transaction,
1401
+ partner: cetusPartnerId,
1402
+ }));
1403
+ const txCopy = Transaction.from(transaction);
1404
+ suilendClient.repay(obligationId, borrowReserve.coinType, stepBorrowCoin, txCopy);
1405
+ txCopy.transferObjects([stepBorrowCoin], _address);
1406
+ await dryRunTransaction(txCopy);
1407
+ transaction = txCopy;
1408
+ if (isFinalStep) {
1409
+ borrowedAmount = targetBorrowedAmount;
1410
+ return true;
1411
+ }
1412
+ borrowedAmount = borrowedAmount.minus(guaranteedAmountOut.decimalPlaces(borrowReserve.token.decimals, BigNumber.ROUND_DOWN));
1413
+ }
1414
+ catch (err) {
1415
+ console.error(err);
1416
+ transaction = transactionBeforeStep;
1417
+ deposits = depositsBeforeStep;
1418
+ skippedCoinTypes.add(extraReserve.coinType);
1419
+ }
1420
+ }
1421
+ return false;
1422
+ };
1423
+ if (await repayBorrowsUsingExtraDeposits())
1424
+ return { deposits, borrowedAmount, transaction };
1313
1425
  const fullyRepayBorrowsUsingLst = async (maxWithdrawRemainingLstAndRedepositAsBase) => {
1314
1426
  if (depositReserves.lst === undefined)
1315
1427
  throw new Error("LST reserve not found");
@@ -2288,6 +2400,100 @@ const sendStrategyWithdrawnCoin = async (transaction, coin, estimatedAmount, add
2288
2400
  const getRedeemedSuiAmount = (lstAmount, lst) => lstAmount
2289
2401
  .times(new BigNumber(lst?.lstToSuiExchangeRate ?? 1).times(1 - +(lst?.redeemFeePercent ?? 0) / 100))
2290
2402
  .decimalPlaces(SUI_DECIMALS, BigNumber.ROUND_DOWN);
2403
+ const swapStrategyWithdrawnCoin = async (cetusSdk, cetusPartnerId, transaction, coin, amount, fromReserve, toReserve) => {
2404
+ const swapTransaction = Transaction.from(transaction);
2405
+ try {
2406
+ const routers = await cetusSdk.findRouters({
2407
+ from: fromReserve.coinType,
2408
+ target: toReserve.coinType,
2409
+ amount: new BN(amount
2410
+ .times(10 ** fromReserve.token.decimals)
2411
+ .integerValue(BigNumber.ROUND_DOWN)
2412
+ .toString()),
2413
+ byAmountIn: true,
2414
+ splitCount: 0,
2415
+ });
2416
+ if (!routers)
2417
+ return undefined;
2418
+ const swappedAmount = new BigNumber(routers.amountOut.toString()).div(10 ** toReserve.token.decimals);
2419
+ const { slippagePercent } = resolveSwapSlippage(amount, swappedAmount, fromReserve, toReserve);
2420
+ const swappedCoin = (await cetusSdk.fixableRouterSwapV3({
2421
+ router: routers,
2422
+ inputCoin: coin,
2423
+ slippage: slippagePercent / 100,
2424
+ txb: swapTransaction,
2425
+ partner: cetusPartnerId,
2426
+ }));
2427
+ return {
2428
+ transaction: swapTransaction,
2429
+ withdrawnCoin: { coin: swappedCoin, estimatedAmount: swappedAmount },
2430
+ };
2431
+ }
2432
+ catch (err) {
2433
+ console.error(err);
2434
+ return undefined;
2435
+ }
2436
+ };
2437
+ const withdrawStrategyExtraDepositsTx = async (strategyType, suilendClient, cetusSdk, cetusPartnerId, address, strategyOwnerCapId, extraWithdrawals, withdrawReserve, withdrawnCoin, transaction, dryRunTransaction) => {
2438
+ if (extraWithdrawals.length === 0)
2439
+ return { transaction, withdrawnCoin, isWithdrawn: false };
2440
+ console.log(`[strategyWithdraw] extra_deposits |`, JSON.stringify(extraWithdrawals.map(({ reserve, amount, isMax }) => ({
2441
+ coinType: reserve.coinType,
2442
+ amount: amount.toFixed(20),
2443
+ isMax,
2444
+ })), null, 2));
2445
+ const build = async (swap) => {
2446
+ let extraTransaction = Transaction.from(transaction);
2447
+ const coins = withdrawnCoin !== undefined ? [withdrawnCoin] : [];
2448
+ for (const { reserve, amount, isMax } of extraWithdrawals) {
2449
+ const [coin] = strategyWithdraw(strategyType, reserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(reserve.coinType), isMax
2450
+ ? BigInt(MAX_U64.toString())
2451
+ : getStrategyWithdrawnCtokenAmount(reserve, amount), extraTransaction);
2452
+ if (reserve.coinType === withdrawReserve.coinType) {
2453
+ coins.push({ coin, estimatedAmount: amount });
2454
+ continue;
2455
+ }
2456
+ const swapped = swap
2457
+ ? await swapStrategyWithdrawnCoin(cetusSdk, cetusPartnerId, extraTransaction, coin, amount, reserve, withdrawReserve)
2458
+ : undefined;
2459
+ if (swapped === undefined) {
2460
+ extraTransaction.transferObjects([coin], address);
2461
+ continue;
2462
+ }
2463
+ extraTransaction = swapped.transaction;
2464
+ coins.push(swapped.withdrawnCoin);
2465
+ }
2466
+ const [mergedCoin, ...otherCoins] = coins;
2467
+ if (mergedCoin !== undefined && otherCoins.length > 0)
2468
+ extraTransaction.mergeCoins(mergedCoin.coin, otherCoins.map(({ coin }) => coin));
2469
+ return {
2470
+ transaction: extraTransaction,
2471
+ withdrawnCoin: mergedCoin === undefined
2472
+ ? undefined
2473
+ : {
2474
+ coin: mergedCoin.coin,
2475
+ estimatedAmount: coins.reduce((acc, { estimatedAmount }) => acc.plus(estimatedAmount), new BigNumber(0)),
2476
+ },
2477
+ };
2478
+ };
2479
+ const needsSwap = extraWithdrawals.some(({ reserve }) => reserve.coinType !== withdrawReserve.coinType);
2480
+ let error;
2481
+ for (const swap of needsSwap ? [true, false] : [false]) {
2482
+ try {
2483
+ const built = await build(swap);
2484
+ const probe = Transaction.from(built.transaction);
2485
+ if (built.withdrawnCoin !== undefined)
2486
+ probe.transferObjects([built.withdrawnCoin.coin], address);
2487
+ await dryRunTransaction(probe);
2488
+ return { ...built, isWithdrawn: true };
2489
+ }
2490
+ catch (err) {
2491
+ console.error(err);
2492
+ error = err;
2493
+ }
2494
+ }
2495
+ return { transaction, withdrawnCoin, isWithdrawn: false, error };
2496
+ };
2291
2497
  const tryClaimStrategyRewardsToUser = async (strategyType, rewardsMap, strategyOwnerCapId, address, transaction, dryRunTransaction) => {
2292
2498
  const transactionBeforeClaim = Transaction.from(transaction);
2293
2499
  try {
@@ -2400,18 +2606,51 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2400
2606
  remainingDepositedAmount: remainingDepositedAmount.toFixed(20),
2401
2607
  finalWithdrawnAmount: finalWithdrawnAmount.toFixed(20),
2402
2608
  }, null, 2));
2403
- const [withdrawnCoin] = strategyWithdraw(strategyType, depositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserve.coinType), BigInt(new BigNumber(finalWithdrawnAmount
2404
- .times(10 ** depositReserve.token.decimals)
2405
- .integerValue(BigNumber.ROUND_DOWN)
2406
- .toString())
2407
- .div(depositReserve.cTokenExchangeRate)
2408
- .integerValue(BigNumber.ROUND_UP)
2409
- .toString()), transaction);
2410
- // 2.2) Update state
2411
- deposits = addOrInsertStrategyDeposit(deposits, {
2412
- coinType: depositReserve.coinType,
2413
- depositedAmount: finalWithdrawnAmount.times(-1),
2414
- });
2609
+ const finalWithdrawnCtokenAmount = getStrategyWithdrawnCtokenAmount(depositReserve, finalWithdrawnAmount);
2610
+ let withdrawnCoin;
2611
+ if (finalWithdrawnCtokenAmount > BigInt(0)) {
2612
+ [withdrawnCoin] = strategyWithdraw(strategyType, depositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserve.coinType), finalWithdrawnCtokenAmount, transaction);
2613
+ // 2.2) Update state
2614
+ deposits = addOrInsertStrategyDeposit(deposits, {
2615
+ coinType: depositReserve.coinType,
2616
+ depositedAmount: finalWithdrawnAmount.times(-1),
2617
+ });
2618
+ }
2619
+ const extraWithdrawals = [];
2620
+ if (!returnWithdrawnCoin) {
2621
+ let shortfallUsd = withdrawnAmount.gt(0)
2622
+ ? withdrawnAmountUsd
2623
+ .times(withdrawnAmount.minus(finalWithdrawnAmount))
2624
+ .div(withdrawnAmount)
2625
+ : new BigNumber(0);
2626
+ let remainingDeposits = deposits;
2627
+ for (const extraDeposit of getStrategyExtraDeposits(reserveMap, lstMap, strategyType, deposits)) {
2628
+ if (!shortfallUsd.gt(0))
2629
+ break;
2630
+ const reserve = reserveMap[extraDeposit.coinType];
2631
+ const unitValueUsd = getStrategyDepositUnitValueUsd(reserveMap, lstMap, reserve.coinType);
2632
+ if (!unitValueUsd.gt(0))
2633
+ continue;
2634
+ const amount = BigNumber.min(shortfallUsd
2635
+ .div(unitValueUsd)
2636
+ .decimalPlaces(reserve.token.decimals, BigNumber.ROUND_UP), getStrategyStepMaxWithdrawnAmount(reserveMap, lstMap, strategyType, remainingDeposits, borrowedAmount, reserve.coinType, borrowedAmount.gt(0) ? 0.9 : 1));
2637
+ if (!amount.gt(0))
2638
+ continue;
2639
+ extraWithdrawals.push({
2640
+ reserve,
2641
+ amount,
2642
+ isMax: borrowedAmount.lte(0) &&
2643
+ amount.gte(extraDeposit.depositedAmount.decimalPlaces(reserve.token.decimals, BigNumber.ROUND_DOWN)),
2644
+ });
2645
+ remainingDeposits = addOrInsertStrategyDeposit(remainingDeposits, {
2646
+ coinType: reserve.coinType,
2647
+ depositedAmount: amount.times(-1),
2648
+ });
2649
+ shortfallUsd = shortfallUsd.minus(amount.times(unitValueUsd));
2650
+ }
2651
+ }
2652
+ if (withdrawnCoin === undefined && extraWithdrawals.length === 0)
2653
+ throw new Error("Nothing left to withdraw");
2415
2654
  const newExposure = getStrategyExposure(reserveMap, lstMap, strategyType, getStrategySimulatedObligation(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
2416
2655
  const newTvlAmountUsd = getStrategyTvlAmount(reserveMap, lstMap, strategyType, getStrategySimulatedObligation(reserveMap, lstMap, strategyType, deposits, borrowedAmount)).times(defaultCurrencyReserve.price);
2417
2656
  console.log(`[withdraw] withdraw.update_state |`, JSON.stringify({
@@ -2430,7 +2669,8 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2430
2669
  const sendWithdrawnCoin = (coin, estimatedAmount) => sendStrategyWithdrawnCoin(transaction, coin, estimatedAmount, _address, consumeWithdrawnCoin, dryRunTransaction);
2431
2670
  if (returnWithdrawnCoin)
2432
2671
  return { deposits, borrowedAmount, transaction, withdrawnCoin };
2433
- else {
2672
+ let finalWithdrawnCoin;
2673
+ if (withdrawnCoin !== undefined) {
2434
2674
  if (depositReserve.coinType === depositReserves.base?.coinType) {
2435
2675
  if (withdraw.coinType !== depositReserve.coinType) {
2436
2676
  const routers = await cetusSdk.findRouters({
@@ -2460,25 +2700,47 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2460
2700
  catch (err) {
2461
2701
  throw new Error("No swap quote found");
2462
2702
  }
2463
- await sendWithdrawnCoin(swappedCoin, swappedAmount);
2703
+ finalWithdrawnCoin = {
2704
+ coin: swappedCoin,
2705
+ estimatedAmount: swappedAmount,
2706
+ };
2464
2707
  }
2465
2708
  else {
2466
- await sendWithdrawnCoin(withdrawnCoin, finalWithdrawnAmount);
2709
+ finalWithdrawnCoin = {
2710
+ coin: withdrawnCoin,
2711
+ estimatedAmount: finalWithdrawnAmount,
2712
+ };
2467
2713
  }
2468
2714
  }
2469
2715
  else {
2470
2716
  if (isSui(withdraw.coinType)) {
2471
2717
  // 3.1) Unstake LST for SUI
2472
2718
  const suiWithdrawnCoin = lst.client.redeem(transaction, withdrawnCoin);
2473
- // 3.2) Transfer SUI to user
2474
- await sendWithdrawnCoin(suiWithdrawnCoin, getRedeemedSuiAmount(finalWithdrawnAmount, lst));
2719
+ finalWithdrawnCoin = {
2720
+ coin: suiWithdrawnCoin,
2721
+ estimatedAmount: getRedeemedSuiAmount(finalWithdrawnAmount, lst),
2722
+ };
2475
2723
  }
2476
2724
  else {
2477
- // 3.1) Transfer LST to user
2478
- await sendWithdrawnCoin(withdrawnCoin, finalWithdrawnAmount);
2725
+ finalWithdrawnCoin = {
2726
+ coin: withdrawnCoin,
2727
+ estimatedAmount: finalWithdrawnAmount,
2728
+ };
2479
2729
  }
2480
2730
  }
2481
2731
  }
2732
+ const extras = await withdrawStrategyExtraDepositsTx(strategyType, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, extraWithdrawals, reserveMap[withdraw.coinType], finalWithdrawnCoin, transaction, dryRunTransaction);
2733
+ if (extras.withdrawnCoin === undefined && !extras.isWithdrawn)
2734
+ throw extras.error ?? new Error("Nothing left to withdraw");
2735
+ transaction = extras.transaction;
2736
+ if (extras.isWithdrawn)
2737
+ for (const { reserve, amount } of extraWithdrawals)
2738
+ deposits = addOrInsertStrategyDeposit(deposits, {
2739
+ coinType: reserve.coinType,
2740
+ depositedAmount: amount.times(-1),
2741
+ });
2742
+ if (extras.withdrawnCoin !== undefined)
2743
+ await sendWithdrawnCoin(extras.withdrawnCoin.coin, extras.withdrawnCoin.estimatedAmount);
2482
2744
  if (hasStrategyRewardsToClaim(rewardsMap) &&
2483
2745
  isStrategyPositionClosed(reserveMap, deposits))
2484
2746
  transaction = await tryClaimStrategyRewardsToUser(strategyType, rewardsMap, strategyOwnerCapId, _address, transaction, dryRunTransaction);
@@ -2639,7 +2901,14 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2639
2901
  }
2640
2902
  const estimatedWithdrawnAmount = deposits.find((d) => d.coinType === depositReserve.coinType)
2641
2903
  ?.depositedAmount ?? new BigNumber(0);
2642
- const [withdrawnCoin] = strategyWithdraw(strategyType, depositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserve.coinType), BigInt(MAX_U64.toString()), transaction);
2904
+ const extraDeposits = getStrategyExtraDeposits(reserveMap, lstMap, strategyType, deposits);
2905
+ const withdrawsDepositReserve = _borrowedAmount.gt(0) ||
2906
+ deposits.some((d) => d.coinType === depositReserve.coinType);
2907
+ if (!withdrawsDepositReserve && extraDeposits.length === 0)
2908
+ throw new Error("Nothing left to withdraw");
2909
+ let withdrawnCoin;
2910
+ if (withdrawsDepositReserve)
2911
+ [withdrawnCoin] = strategyWithdraw(strategyType, depositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserve.coinType), BigInt(MAX_U64.toString()), transaction);
2643
2912
  // 2.2) Update state
2644
2913
  deposits = [];
2645
2914
  console.log(`[strategyMaxWithdraw] max_withdraw.update_state |`, JSON.stringify({
@@ -2651,81 +2920,107 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2651
2920
  }, null, 2));
2652
2921
  const sendWithdrawnCoin = (coin, estimatedAmount) => sendStrategyWithdrawnCoin(transaction, coin, estimatedAmount, _address, consumeWithdrawnCoin, dryRunTransaction);
2653
2922
  // 3) Transfer coin to user
2654
- if (depositReserve.coinType === depositReserves.base?.coinType) {
2655
- if (withdrawCoinType !== depositReserve.coinType) {
2656
- const routers = await cetusSdk.findRouters({
2657
- from: depositReserve.coinType,
2658
- target: withdrawCoinType,
2659
- amount: new BN(estimatedWithdrawnAmount
2660
- .times(10 ** depositReserve.token.decimals)
2661
- .integerValue(BigNumber.ROUND_DOWN)
2662
- .toString()),
2663
- byAmountIn: true,
2664
- splitCount: 0,
2665
- });
2666
- if (!routers)
2667
- throw new Error("No swap quote found");
2668
- const swappedAmount = new BigNumber(routers.amountOut.toString()).div(10 ** reserveMap[withdrawCoinType].token.decimals);
2669
- const { slippagePercent } = resolveSwapSlippage(estimatedWithdrawnAmount, swappedAmount, depositReserve, reserveMap[withdrawCoinType]);
2670
- let swappedCoin;
2671
- try {
2672
- swappedCoin = (await cetusSdk.fixableRouterSwapV3({
2673
- router: routers,
2674
- inputCoin: withdrawnCoin,
2675
- slippage: slippagePercent / 100,
2676
- txb: transaction,
2677
- partner: cetusPartnerId,
2678
- }));
2923
+ let finalWithdrawnCoin;
2924
+ if (withdrawnCoin !== undefined) {
2925
+ if (depositReserve.coinType === depositReserves.base?.coinType) {
2926
+ if (withdrawCoinType !== depositReserve.coinType) {
2927
+ const routers = await cetusSdk.findRouters({
2928
+ from: depositReserve.coinType,
2929
+ target: withdrawCoinType,
2930
+ amount: new BN(estimatedWithdrawnAmount
2931
+ .times(10 ** depositReserve.token.decimals)
2932
+ .integerValue(BigNumber.ROUND_DOWN)
2933
+ .toString()),
2934
+ byAmountIn: true,
2935
+ splitCount: 0,
2936
+ });
2937
+ if (!routers)
2938
+ throw new Error("No swap quote found");
2939
+ const swappedAmount = new BigNumber(routers.amountOut.toString()).div(10 ** reserveMap[withdrawCoinType].token.decimals);
2940
+ const { slippagePercent } = resolveSwapSlippage(estimatedWithdrawnAmount, swappedAmount, depositReserve, reserveMap[withdrawCoinType]);
2941
+ let swappedCoin;
2942
+ try {
2943
+ swappedCoin = (await cetusSdk.fixableRouterSwapV3({
2944
+ router: routers,
2945
+ inputCoin: withdrawnCoin,
2946
+ slippage: slippagePercent / 100,
2947
+ txb: transaction,
2948
+ partner: cetusPartnerId,
2949
+ }));
2950
+ }
2951
+ catch (err) {
2952
+ throw new Error("No swap quote found");
2953
+ }
2954
+ finalWithdrawnCoin = {
2955
+ coin: swappedCoin,
2956
+ estimatedAmount: swappedAmount,
2957
+ };
2679
2958
  }
2680
- catch (err) {
2681
- throw new Error("No swap quote found");
2959
+ else {
2960
+ finalWithdrawnCoin = {
2961
+ coin: withdrawnCoin,
2962
+ estimatedAmount: estimatedWithdrawnAmount,
2963
+ };
2682
2964
  }
2683
- await sendWithdrawnCoin(swappedCoin, swappedAmount);
2684
2965
  }
2685
2966
  else {
2686
- await sendWithdrawnCoin(withdrawnCoin, estimatedWithdrawnAmount);
2687
- }
2688
- }
2689
- else {
2690
- if (isSui(withdrawCoinType)) {
2691
- // 3.1) Unstake LST for SUI
2692
- const suiWithdrawnCoin = lst.client.redeem(transaction, withdrawnCoin);
2693
- // 3.2) Repay flash loan + fee
2694
- if (flashLoan !== undefined) {
2695
- transaction.mergeCoins(suiWithdrawnCoin, [flashLoan.remainingCoin]);
2696
- const receiptDebts = transaction.moveCall({
2697
- target: `${MMT_CONTRACT_PACKAGE_ID}::trade::flash_receipt_debts`,
2698
- typeArguments: [],
2699
- arguments: [flashLoan.receipt],
2700
- });
2701
- const [flashLoanRepayCoin] = transaction.splitCoins(suiWithdrawnCoin, [
2702
- receiptDebts[1],
2703
- ]);
2704
- const flashLoanRepayBalance = transaction.moveCall({
2705
- target: "0x2::coin::into_balance",
2706
- typeArguments: [flashLoan.obj.coinTypeB],
2707
- arguments: [flashLoanRepayCoin],
2708
- });
2709
- transaction.moveCall({
2710
- target: `${MMT_CONTRACT_PACKAGE_ID}::trade::repay_flash_loan`,
2711
- typeArguments: [flashLoan.obj.coinTypeA, flashLoan.obj.coinTypeB],
2712
- arguments: [
2713
- transaction.object(flashLoan.obj.poolId),
2714
- flashLoan.receipt,
2715
- flashLoan.borrowedBalanceA,
2716
- flashLoanRepayBalance,
2717
- transaction.object(MMT_VERSION_OBJECT_ID),
2718
- ],
2719
- });
2967
+ if (isSui(withdrawCoinType)) {
2968
+ // 3.1) Unstake LST for SUI
2969
+ const suiWithdrawnCoin = lst.client.redeem(transaction, withdrawnCoin);
2970
+ // 3.2) Repay flash loan + fee
2971
+ if (flashLoan !== undefined) {
2972
+ transaction.mergeCoins(suiWithdrawnCoin, [flashLoan.remainingCoin]);
2973
+ const receiptDebts = transaction.moveCall({
2974
+ target: `${MMT_CONTRACT_PACKAGE_ID}::trade::flash_receipt_debts`,
2975
+ typeArguments: [],
2976
+ arguments: [flashLoan.receipt],
2977
+ });
2978
+ const [flashLoanRepayCoin] = transaction.splitCoins(suiWithdrawnCoin, [receiptDebts[1]]);
2979
+ const flashLoanRepayBalance = transaction.moveCall({
2980
+ target: "0x2::coin::into_balance",
2981
+ typeArguments: [flashLoan.obj.coinTypeB],
2982
+ arguments: [flashLoanRepayCoin],
2983
+ });
2984
+ transaction.moveCall({
2985
+ target: `${MMT_CONTRACT_PACKAGE_ID}::trade::repay_flash_loan`,
2986
+ typeArguments: [flashLoan.obj.coinTypeA, flashLoan.obj.coinTypeB],
2987
+ arguments: [
2988
+ transaction.object(flashLoan.obj.poolId),
2989
+ flashLoan.receipt,
2990
+ flashLoan.borrowedBalanceA,
2991
+ flashLoanRepayBalance,
2992
+ transaction.object(MMT_VERSION_OBJECT_ID),
2993
+ ],
2994
+ });
2995
+ }
2996
+ finalWithdrawnCoin = {
2997
+ coin: suiWithdrawnCoin,
2998
+ estimatedAmount: BigNumber.max(0, getRedeemedSuiAmount(estimatedWithdrawnAmount, lst).minus(flashLoan?.borrowedAmount ?? 0)),
2999
+ };
3000
+ }
3001
+ else {
3002
+ finalWithdrawnCoin = {
3003
+ coin: withdrawnCoin,
3004
+ estimatedAmount: estimatedWithdrawnAmount,
3005
+ };
2720
3006
  }
2721
- // 3.3) Transfer SUI to user
2722
- await sendWithdrawnCoin(suiWithdrawnCoin, BigNumber.max(0, getRedeemedSuiAmount(estimatedWithdrawnAmount, lst).minus(flashLoan?.borrowedAmount ?? 0)));
2723
- }
2724
- else {
2725
- // 3.1) Transfer LST to user
2726
- await sendWithdrawnCoin(withdrawnCoin, estimatedWithdrawnAmount);
2727
3007
  }
2728
3008
  }
3009
+ const extras = await withdrawStrategyExtraDepositsTx(strategyType, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, extraDeposits.map((deposit) => {
3010
+ const reserve = reserveMap[deposit.coinType];
3011
+ return {
3012
+ reserve,
3013
+ amount: deposit.depositedAmount.decimalPlaces(reserve.token.decimals, BigNumber.ROUND_DOWN),
3014
+ isMax: true,
3015
+ };
3016
+ }), reserveMap[withdrawCoinType], finalWithdrawnCoin, transaction, dryRunTransaction);
3017
+ if (extras.withdrawnCoin === undefined && !extras.isWithdrawn)
3018
+ throw extras.error ?? new Error("Nothing left to withdraw");
3019
+ transaction = extras.transaction;
3020
+ if (!extras.isWithdrawn)
3021
+ deposits = extraDeposits;
3022
+ if (extras.withdrawnCoin !== undefined)
3023
+ await sendWithdrawnCoin(extras.withdrawnCoin.coin, extras.withdrawnCoin.estimatedAmount);
2729
3024
  // 4) Claim rewards and transfer to user
2730
3025
  if (hasClaimableRewards) {
2731
3026
  // Claiming is a bonus, so a failure must not block the withdraw — but the calls
@@ -59,7 +59,9 @@ export { normalizeFeedId };
59
59
  /**
60
60
  * The first lending market version whose refresh aborts `EInvalidPrice` on a
61
61
  * spot that parses to a zero `Decimal`, instead of storing it. Earlier versions
62
- * store the zero, so the model follows the market's own version.
62
+ * store the zero, so the model follows the market's own version. The chain
63
+ * builds that `Decimal` with flooring division, so any spot below 1e-18 is
64
+ * zero there, while `stringToDecimal` rounds 7e-19 up to 1e-18.
63
65
  */
64
66
  export declare const ZERO_PRICE_ABORT_MARKET_VERSION: bigint;
65
67
  export declare const reservePriceFeedIds: (reserves: Reserve<string>[]) => string[];
package/utils/simulate.js CHANGED
@@ -183,12 +183,14 @@ const withFeedPrice = (reserve, priceFeed) => {
183
183
  /**
184
184
  * The first lending market version whose refresh aborts `EInvalidPrice` on a
185
185
  * spot that parses to a zero `Decimal`, instead of storing it. Earlier versions
186
- * store the zero, so the model follows the market's own version.
186
+ * store the zero, so the model follows the market's own version. The chain
187
+ * builds that `Decimal` with flooring division, so any spot below 1e-18 is
188
+ * zero there, while `stringToDecimal` rounds 7e-19 up to 1e-18.
187
189
  */
188
190
  export const ZERO_PRICE_ABORT_MARKET_VERSION = BigInt(8);
189
- const refreshAbortsOnZeroPrice = (marketVersion, refreshed) => marketVersion !== undefined &&
191
+ const refreshAbortsOnZeroPrice = (marketVersion, priceFeed) => marketVersion !== undefined &&
190
192
  marketVersion >= ZERO_PRICE_ABORT_MARKET_VERSION &&
191
- refreshed.price.value === BigInt(0);
193
+ new BigNumber(priceFeed.price).shiftedBy(18).lt(1);
192
194
  export const reservePriceFeedIds = (reserves) => Array.from(new Set(reserves.map(reserveFeedId)));
193
195
  export const applyReservePriceFeeds = (reserves, priceFeeds, marketVersion) => {
194
196
  // Deliberately NOT a throw, and deliberately not expressed over the tolerant
@@ -203,10 +205,9 @@ export const applyReservePriceFeeds = (reserves, priceFeeds, marketVersion) => {
203
205
  const priceFeed = byId.get(reserveFeedId(reserve));
204
206
  if (!priceFeed)
205
207
  throw new Error(`Price feed not found for reserve ${reserve.coinType}`);
206
- const refreshed = withFeedPrice(reserve, priceFeed);
207
- if (refreshAbortsOnZeroPrice(marketVersion, refreshed))
208
+ if (refreshAbortsOnZeroPrice(marketVersion, priceFeed))
208
209
  throw new Error(`Price feed for reserve ${reserve.coinType} reads a zero price, which a market at version ${marketVersion} rejects with EInvalidPrice`);
209
- return refreshed;
210
+ return withFeedPrice(reserve, priceFeed);
210
211
  });
211
212
  };
212
213
  export const refreshReservePrice = async (reserves, pythConnection, marketVersion) => applyReservePriceFeeds(reserves, await pythConnection.getLatestPriceFeeds(reservePriceFeedIds(reserves)), marketVersion);
@@ -286,8 +287,7 @@ export const applyReservePriceFeedsTolerant = (reserves, priceFeeds, marketVersi
286
287
  const unpricedCoinTypes = [];
287
288
  const updatedReserves = reserves.map((reserve) => {
288
289
  const priceFeed = byId.get(reserveFeedId(reserve));
289
- const refreshed = priceFeed && withFeedPrice(reserve, priceFeed);
290
- if (!refreshed || refreshAbortsOnZeroPrice(marketVersion, refreshed)) {
290
+ if (!priceFeed || refreshAbortsOnZeroPrice(marketVersion, priceFeed)) {
291
291
  // Normalized to match every other coin type this SDK returns
292
292
  // (`reserveMap`, `reserveCoinTypes`, and the parsers are all
293
293
  // normalizeStructTag'd). The raw on-chain form never compares equal to
@@ -296,7 +296,7 @@ export const applyReservePriceFeedsTolerant = (reserves, priceFeeds, marketVersi
296
296
  unpricedCoinTypes.push(normalizeStructTag(reserve.coinType));
297
297
  return reserve;
298
298
  }
299
- return refreshed;
299
+ return withFeedPrice(reserve, priceFeed);
300
300
  });
301
301
  return { reserves: updatedReserves, unpricedCoinTypes };
302
302
  };