@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 +1 -1
- package/strategies.js +383 -88
- package/utils/simulate.d.ts +3 -1
- package/utils/simulate.js +9 -9
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@suilend/sdk","version":"14.2.
|
|
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
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
.
|
|
2407
|
-
.
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2703
|
+
finalWithdrawnCoin = {
|
|
2704
|
+
coin: swappedCoin,
|
|
2705
|
+
estimatedAmount: swappedAmount,
|
|
2706
|
+
};
|
|
2464
2707
|
}
|
|
2465
2708
|
else {
|
|
2466
|
-
|
|
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
|
-
|
|
2474
|
-
|
|
2719
|
+
finalWithdrawnCoin = {
|
|
2720
|
+
coin: suiWithdrawnCoin,
|
|
2721
|
+
estimatedAmount: getRedeemedSuiAmount(finalWithdrawnAmount, lst),
|
|
2722
|
+
};
|
|
2475
2723
|
}
|
|
2476
2724
|
else {
|
|
2477
|
-
|
|
2478
|
-
|
|
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
|
|
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
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
swappedCoin
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
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
|
-
|
|
2681
|
-
|
|
2959
|
+
else {
|
|
2960
|
+
finalWithdrawnCoin = {
|
|
2961
|
+
coin: withdrawnCoin,
|
|
2962
|
+
estimatedAmount: estimatedWithdrawnAmount,
|
|
2963
|
+
};
|
|
2682
2964
|
}
|
|
2683
|
-
await sendWithdrawnCoin(swappedCoin, swappedAmount);
|
|
2684
2965
|
}
|
|
2685
2966
|
else {
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
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
|
package/utils/simulate.d.ts
CHANGED
|
@@ -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,
|
|
191
|
+
const refreshAbortsOnZeroPrice = (marketVersion, priceFeed) => marketVersion !== undefined &&
|
|
190
192
|
marketVersion >= ZERO_PRICE_ABORT_MARKET_VERSION &&
|
|
191
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
299
|
+
return withFeedPrice(reserve, priceFeed);
|
|
300
300
|
});
|
|
301
301
|
return { reserves: updatedReserves, unpricedCoinTypes };
|
|
302
302
|
};
|