@t2000/sdk 0.17.13 → 0.17.15

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.
@@ -23,6 +23,7 @@ interface BalanceResponse {
23
23
  debt: number;
24
24
  investment: number;
25
25
  investmentPnL: number;
26
+ pendingRewards: number;
26
27
  gasReserve: GasReserve;
27
28
  total: number;
28
29
  assets: Record<string, number>;
@@ -349,6 +350,21 @@ interface AutoInvestRunResult {
349
350
  reason: string;
350
351
  }>;
351
352
  }
353
+ interface PendingReward$1 {
354
+ protocol: string;
355
+ coinType: string;
356
+ symbol: string;
357
+ amount: number;
358
+ estimatedValueUsd: number;
359
+ }
360
+ interface ClaimRewardsResult {
361
+ success: boolean;
362
+ tx: string;
363
+ rewards: PendingReward$1[];
364
+ totalValueUsd: number;
365
+ gasCost: number;
366
+ gasMethod: GasMethod;
367
+ }
352
368
  type PositionSide = 'long' | 'short';
353
369
  interface PerpsPosition {
354
370
  market: string;
@@ -485,6 +501,15 @@ interface LendingAdapter {
485
501
  collectFee?: boolean;
486
502
  }): Promise<void>;
487
503
  addRepayToTx?(tx: Transaction, address: string, coin: TransactionObjectArgument, asset: string): Promise<void>;
504
+ getPendingRewards?(address: string): Promise<PendingReward[]>;
505
+ addClaimRewardsToTx?(tx: Transaction, address: string): Promise<PendingReward[]>;
506
+ }
507
+ interface PendingReward {
508
+ protocol: string;
509
+ coinType: string;
510
+ symbol: string;
511
+ amount: number;
512
+ estimatedValueUsd: number;
488
513
  }
489
514
  interface SwapAdapter {
490
515
  readonly id: string;
@@ -609,6 +634,8 @@ declare class NaviAdapter implements LendingAdapter {
609
634
  collectFee?: boolean;
610
635
  }): Promise<void>;
611
636
  addRepayToTx(tx: Transaction, address: string, coin: TransactionObjectArgument, asset: string): Promise<void>;
637
+ getPendingRewards(address: string): Promise<PendingReward[]>;
638
+ addClaimRewardsToTx(tx: Transaction, address: string): Promise<PendingReward[]>;
612
639
  }
613
640
 
614
641
  declare const descriptor$2: ProtocolDescriptor;
@@ -699,6 +726,9 @@ declare class SuilendAdapter implements LendingAdapter {
699
726
  currentHF: number;
700
727
  }>;
701
728
  private fetchAllCoins;
729
+ private isClaimableReward;
730
+ getPendingRewards(address: string): Promise<PendingReward[]>;
731
+ addClaimRewardsToTx(tx: Transaction, address: string): Promise<PendingReward[]>;
702
732
  }
703
733
 
704
734
  declare const descriptor: ProtocolDescriptor;
@@ -718,4 +748,4 @@ declare function attack(client: SuiJsonRpcClient, signer: Ed25519Keypair, sentin
718
748
  /** All registered protocol descriptors — used by the indexer for event classification */
719
749
  declare const allDescriptors: ProtocolDescriptor[];
720
750
 
721
- export { SuilendAdapter as $, type AutoInvestSchedule as A, type BalanceResponse as B, CetusAdapter as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type GasReserve as J, type HealthInfo as K, type LendingAdapter as L, type MaxWithdrawResult as M, NaviAdapter as N, type PerpsAdapter as O, type PortfolioResult as P, type PerpsPosition as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type PositionEntry as U, type PositionSide as V, type WithdrawResult as W, type ProtocolDescriptor as X, ProtocolRegistry as Y, type RebalanceStep as Z, type SentinelVerdict as _, type AutoInvestStatus as a, type SwapQuote as a0, type TradePositionsResult as a1, type TradeResult as a2, allDescriptors as a3, descriptor$2 as a4, getSentinelInfo as a5, listSentinels as a6, descriptor$3 as a7, requestAttack as a8, attack as a9, descriptor as aa, settleAttack as ab, submitPrompt as ac, descriptor$1 as ad, type SendResult as b, type TransactionRecord as c, type SwapAdapter as d, type SaveResult as e, type BorrowResult as f, type MaxBorrowResult as g, type SwapResult as h, type InvestResult as i, type InvestEarnResult as j, type StrategyBuyResult as k, type StrategySellResult as l, type StrategyRebalanceResult as m, type StrategyStatusResult as n, type AutoInvestRunResult as o, type InvestmentPosition as p, type PositionsResult as q, type RatesResult as r, type LendingRates as s, type RebalanceResult as t, type SentinelAgent as u, type SentinelAttackResult as v, type AdapterCapability as w, type AdapterPositions as x, type AdapterTxResult as y, type AssetRates as z };
751
+ export { type RebalanceStep as $, type AutoInvestSchedule as A, type BalanceResponse as B, type ClaimRewardsResult as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type AssetRates as J, CetusAdapter as K, type LendingAdapter as L, type MaxWithdrawResult as M, type GasReserve as N, type HealthInfo as O, type PendingReward$1 as P, NaviAdapter as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type PerpsAdapter as U, type PerpsPosition as V, type WithdrawResult as W, type PositionEntry as X, type PositionSide as Y, type ProtocolDescriptor as Z, ProtocolRegistry as _, type AutoInvestStatus as a, type SentinelVerdict as a0, SuilendAdapter as a1, type SwapQuote as a2, type TradePositionsResult as a3, type TradeResult as a4, allDescriptors as a5, descriptor$2 as a6, getSentinelInfo as a7, listSentinels as a8, descriptor$3 as a9, requestAttack as aa, attack as ab, descriptor as ac, settleAttack as ad, submitPrompt as ae, descriptor$1 as af, type SendResult as b, type TransactionRecord as c, type SwapAdapter as d, type SaveResult as e, type BorrowResult as f, type MaxBorrowResult as g, type SwapResult as h, type InvestResult as i, type InvestEarnResult as j, type StrategyBuyResult as k, type StrategySellResult as l, type StrategyRebalanceResult as m, type StrategyStatusResult as n, type AutoInvestRunResult as o, type PortfolioResult as p, type InvestmentPosition as q, type PositionsResult as r, type RatesResult as s, type LendingRates as t, type RebalanceResult as u, type SentinelAgent as v, type SentinelAttackResult as w, type AdapterCapability as x, type AdapterPositions as y, type AdapterTxResult as z };
@@ -23,6 +23,7 @@ interface BalanceResponse {
23
23
  debt: number;
24
24
  investment: number;
25
25
  investmentPnL: number;
26
+ pendingRewards: number;
26
27
  gasReserve: GasReserve;
27
28
  total: number;
28
29
  assets: Record<string, number>;
@@ -349,6 +350,21 @@ interface AutoInvestRunResult {
349
350
  reason: string;
350
351
  }>;
351
352
  }
353
+ interface PendingReward$1 {
354
+ protocol: string;
355
+ coinType: string;
356
+ symbol: string;
357
+ amount: number;
358
+ estimatedValueUsd: number;
359
+ }
360
+ interface ClaimRewardsResult {
361
+ success: boolean;
362
+ tx: string;
363
+ rewards: PendingReward$1[];
364
+ totalValueUsd: number;
365
+ gasCost: number;
366
+ gasMethod: GasMethod;
367
+ }
352
368
  type PositionSide = 'long' | 'short';
353
369
  interface PerpsPosition {
354
370
  market: string;
@@ -485,6 +501,15 @@ interface LendingAdapter {
485
501
  collectFee?: boolean;
486
502
  }): Promise<void>;
487
503
  addRepayToTx?(tx: Transaction, address: string, coin: TransactionObjectArgument, asset: string): Promise<void>;
504
+ getPendingRewards?(address: string): Promise<PendingReward[]>;
505
+ addClaimRewardsToTx?(tx: Transaction, address: string): Promise<PendingReward[]>;
506
+ }
507
+ interface PendingReward {
508
+ protocol: string;
509
+ coinType: string;
510
+ symbol: string;
511
+ amount: number;
512
+ estimatedValueUsd: number;
488
513
  }
489
514
  interface SwapAdapter {
490
515
  readonly id: string;
@@ -609,6 +634,8 @@ declare class NaviAdapter implements LendingAdapter {
609
634
  collectFee?: boolean;
610
635
  }): Promise<void>;
611
636
  addRepayToTx(tx: Transaction, address: string, coin: TransactionObjectArgument, asset: string): Promise<void>;
637
+ getPendingRewards(address: string): Promise<PendingReward[]>;
638
+ addClaimRewardsToTx(tx: Transaction, address: string): Promise<PendingReward[]>;
612
639
  }
613
640
 
614
641
  declare const descriptor$2: ProtocolDescriptor;
@@ -699,6 +726,9 @@ declare class SuilendAdapter implements LendingAdapter {
699
726
  currentHF: number;
700
727
  }>;
701
728
  private fetchAllCoins;
729
+ private isClaimableReward;
730
+ getPendingRewards(address: string): Promise<PendingReward[]>;
731
+ addClaimRewardsToTx(tx: Transaction, address: string): Promise<PendingReward[]>;
702
732
  }
703
733
 
704
734
  declare const descriptor: ProtocolDescriptor;
@@ -718,4 +748,4 @@ declare function attack(client: SuiJsonRpcClient, signer: Ed25519Keypair, sentin
718
748
  /** All registered protocol descriptors — used by the indexer for event classification */
719
749
  declare const allDescriptors: ProtocolDescriptor[];
720
750
 
721
- export { SuilendAdapter as $, type AutoInvestSchedule as A, type BalanceResponse as B, CetusAdapter as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type GasReserve as J, type HealthInfo as K, type LendingAdapter as L, type MaxWithdrawResult as M, NaviAdapter as N, type PerpsAdapter as O, type PortfolioResult as P, type PerpsPosition as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type PositionEntry as U, type PositionSide as V, type WithdrawResult as W, type ProtocolDescriptor as X, ProtocolRegistry as Y, type RebalanceStep as Z, type SentinelVerdict as _, type AutoInvestStatus as a, type SwapQuote as a0, type TradePositionsResult as a1, type TradeResult as a2, allDescriptors as a3, descriptor$2 as a4, getSentinelInfo as a5, listSentinels as a6, descriptor$3 as a7, requestAttack as a8, attack as a9, descriptor as aa, settleAttack as ab, submitPrompt as ac, descriptor$1 as ad, type SendResult as b, type TransactionRecord as c, type SwapAdapter as d, type SaveResult as e, type BorrowResult as f, type MaxBorrowResult as g, type SwapResult as h, type InvestResult as i, type InvestEarnResult as j, type StrategyBuyResult as k, type StrategySellResult as l, type StrategyRebalanceResult as m, type StrategyStatusResult as n, type AutoInvestRunResult as o, type InvestmentPosition as p, type PositionsResult as q, type RatesResult as r, type LendingRates as s, type RebalanceResult as t, type SentinelAgent as u, type SentinelAttackResult as v, type AdapterCapability as w, type AdapterPositions as x, type AdapterTxResult as y, type AssetRates as z };
751
+ export { type RebalanceStep as $, type AutoInvestSchedule as A, type BalanceResponse as B, type ClaimRewardsResult as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type AssetRates as J, CetusAdapter as K, type LendingAdapter as L, type MaxWithdrawResult as M, type GasReserve as N, type HealthInfo as O, type PendingReward$1 as P, NaviAdapter as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type PerpsAdapter as U, type PerpsPosition as V, type WithdrawResult as W, type PositionEntry as X, type PositionSide as Y, type ProtocolDescriptor as Z, ProtocolRegistry as _, type AutoInvestStatus as a, type SentinelVerdict as a0, SuilendAdapter as a1, type SwapQuote as a2, type TradePositionsResult as a3, type TradeResult as a4, allDescriptors as a5, descriptor$2 as a6, getSentinelInfo as a7, listSentinels as a8, descriptor$3 as a9, requestAttack as aa, attack as ab, descriptor as ac, settleAttack as ad, submitPrompt as ae, descriptor$1 as af, type SendResult as b, type TransactionRecord as c, type SwapAdapter as d, type SaveResult as e, type BorrowResult as f, type MaxBorrowResult as g, type SwapResult as h, type InvestResult as i, type InvestEarnResult as j, type StrategyBuyResult as k, type StrategySellResult as l, type StrategyRebalanceResult as m, type StrategyStatusResult as n, type AutoInvestRunResult as o, type PortfolioResult as p, type InvestmentPosition as q, type PositionsResult as r, type RatesResult as s, type LendingRates as t, type RebalanceResult as u, type SentinelAgent as v, type SentinelAttackResult as w, type AdapterCapability as x, type AdapterPositions as y, type AdapterTxResult as z };
package/dist/index.cjs CHANGED
@@ -495,6 +495,7 @@ async function queryBalance(client, address) {
495
495
  debt: 0,
496
496
  investment: 0,
497
497
  investmentPnL: 0,
498
+ pendingRewards: 0,
498
499
  gasReserve: {
499
500
  sui: suiAmount,
500
501
  usdEquiv
@@ -1178,6 +1179,173 @@ async function maxBorrowAmount(client, addressOrKeypair) {
1178
1179
  const maxAmount = Math.max(0, hf.supplied * ltv / MIN_HEALTH_FACTOR - hf.borrowed);
1179
1180
  return { maxAmount, healthFactorAfter: MIN_HEALTH_FACTOR, currentHF: hf.healthFactor };
1180
1181
  }
1182
+ var CERT_TYPE = "0x549e8b69270defbfafd4f94e17ec44cdbdd99820b33bda2278dea3b9a32d3f55::cert::CERT";
1183
+ var DEEP_TYPE = "0xdeeb7a4662eec9f2f3def03fb937a663dddaa2e215b8078a284d026b7946c270::deep::DEEP";
1184
+ var REWARD_FUNDS = {
1185
+ [CERT_TYPE]: "0x7093cf7549d5e5b35bfde2177223d1050f71655c7f676a5e610ee70eb4d93b5c",
1186
+ [DEEP_TYPE]: "0xc889d78b634f954979e80e622a2ae0fece824c0f6d9590044378a2563035f32f"
1187
+ };
1188
+ var REWARD_SYMBOLS = {
1189
+ [CERT_TYPE]: "vSUI",
1190
+ [DEEP_TYPE]: "DEEP"
1191
+ };
1192
+ var incentiveRulesCache = null;
1193
+ async function getIncentiveRules(client) {
1194
+ if (incentiveRulesCache && Date.now() - incentiveRulesCache.ts < CACHE_TTL) {
1195
+ return incentiveRulesCache.data;
1196
+ }
1197
+ const [pools, obj] = await Promise.all([
1198
+ getPools(),
1199
+ client.getObject({
1200
+ id: "0x62982dad27fb10bb314b3384d5de8d2ac2d72ab2dbeae5d801dbdb9efa816c80",
1201
+ options: { showContent: true }
1202
+ })
1203
+ ]);
1204
+ const rewardCoinMap = /* @__PURE__ */ new Map();
1205
+ for (const pool of pools) {
1206
+ const ct = (pool.suiCoinType || pool.coinType || "").toLowerCase();
1207
+ const suffix = ct.split("::").slice(1).join("::");
1208
+ const coins = pool.supplyIncentiveApyInfo?.rewardCoin;
1209
+ if (Array.isArray(coins) && coins.length > 0) {
1210
+ rewardCoinMap.set(suffix, coins[0]);
1211
+ }
1212
+ }
1213
+ const result = /* @__PURE__ */ new Map();
1214
+ if (obj.data?.content?.dataType !== "moveObject") {
1215
+ incentiveRulesCache = { data: result, ts: Date.now() };
1216
+ return result;
1217
+ }
1218
+ const fields = obj.data.content.fields;
1219
+ const poolsObj = fields.pools;
1220
+ const entries = poolsObj?.fields?.contents;
1221
+ if (!Array.isArray(entries)) {
1222
+ incentiveRulesCache = { data: result, ts: Date.now() };
1223
+ return result;
1224
+ }
1225
+ for (const entry of entries) {
1226
+ const ef = entry?.fields;
1227
+ if (!ef) continue;
1228
+ const key = String(ef.key ?? "");
1229
+ const value = ef.value;
1230
+ const rules = value?.fields?.rules;
1231
+ const ruleEntries = rules?.fields?.contents;
1232
+ if (!Array.isArray(ruleEntries)) continue;
1233
+ const ruleIds = ruleEntries.map((re) => {
1234
+ const rf = re?.fields;
1235
+ return String(rf?.key ?? "");
1236
+ }).filter(Boolean);
1237
+ const suffix = key.split("::").slice(1).join("::").toLowerCase();
1238
+ const full = key.toLowerCase();
1239
+ const rewardCoin = rewardCoinMap.get(suffix) ?? rewardCoinMap.get(full) ?? null;
1240
+ result.set(key, { ruleIds, rewardCoinType: rewardCoin });
1241
+ }
1242
+ incentiveRulesCache = { data: result, ts: Date.now() };
1243
+ return result;
1244
+ }
1245
+ function stripPrefix(coinType) {
1246
+ return coinType.replace(/^0x0*/, "");
1247
+ }
1248
+ async function getPendingRewards(client, address) {
1249
+ const [pools, states, rules] = await Promise.all([
1250
+ getPools(),
1251
+ getUserState(client, address),
1252
+ getIncentiveRules(client)
1253
+ ]);
1254
+ const rewards = [];
1255
+ const deposited = states.filter((s) => s.supplyBalance > 0n);
1256
+ if (deposited.length === 0) return rewards;
1257
+ const rewardTotals = /* @__PURE__ */ new Map();
1258
+ for (const state of deposited) {
1259
+ const pool = pools.find((p) => p.id === state.assetId);
1260
+ if (!pool) continue;
1261
+ const boostedApr = parseFloat(pool.supplyIncentiveApyInfo?.boostedApr ?? "0");
1262
+ if (boostedApr <= 0) continue;
1263
+ const rewardCoins = pool.supplyIncentiveApyInfo?.rewardCoin;
1264
+ if (!Array.isArray(rewardCoins) || rewardCoins.length === 0) continue;
1265
+ const rewardType = rewardCoins[0];
1266
+ const supplyBal = compoundBalance(state.supplyBalance, pool.currentSupplyIndex, pool);
1267
+ const price = pool.token?.price ?? 0;
1268
+ const depositUsd = supplyBal * price;
1269
+ const annualRewardUsd = depositUsd * (boostedApr / 100);
1270
+ const estimatedUsd = annualRewardUsd / 365;
1271
+ rewardTotals.set(rewardType, (rewardTotals.get(rewardType) ?? 0) + estimatedUsd);
1272
+ }
1273
+ for (const [coinType, dailyUsd] of rewardTotals) {
1274
+ if (dailyUsd < 1e-3) continue;
1275
+ rewards.push({
1276
+ protocol: "navi",
1277
+ coinType,
1278
+ symbol: REWARD_SYMBOLS[coinType] ?? coinType.split("::").pop() ?? "UNKNOWN",
1279
+ amount: 0,
1280
+ estimatedValueUsd: dailyUsd
1281
+ });
1282
+ }
1283
+ return rewards;
1284
+ }
1285
+ async function addClaimRewardsToTx(tx, client, address) {
1286
+ const [config, pools, states, rules] = await Promise.all([
1287
+ getConfig(),
1288
+ getPools(),
1289
+ getUserState(client, address),
1290
+ getIncentiveRules(client)
1291
+ ]);
1292
+ const deposited = states.filter((s) => s.supplyBalance > 0n);
1293
+ if (deposited.length === 0) return [];
1294
+ const claimGroups = /* @__PURE__ */ new Map();
1295
+ for (const state of deposited) {
1296
+ const pool = pools.find((p) => p.id === state.assetId);
1297
+ if (!pool) continue;
1298
+ const boostedApr = parseFloat(pool.supplyIncentiveApyInfo?.boostedApr ?? "0");
1299
+ if (boostedApr <= 0) continue;
1300
+ const rewardCoins = pool.supplyIncentiveApyInfo?.rewardCoin;
1301
+ if (!Array.isArray(rewardCoins) || rewardCoins.length === 0) continue;
1302
+ const rewardType = rewardCoins[0];
1303
+ const fundId = REWARD_FUNDS[rewardType];
1304
+ if (!fundId) continue;
1305
+ const coinType = pool.suiCoinType || pool.coinType || "";
1306
+ const strippedType = stripPrefix(coinType);
1307
+ const ruleData = Array.from(rules.entries()).find(
1308
+ ([key]) => stripPrefix(key) === strippedType || key.split("::").slice(1).join("::").toLowerCase() === coinType.split("::").slice(1).join("::").toLowerCase()
1309
+ );
1310
+ if (!ruleData || ruleData[1].ruleIds.length === 0) continue;
1311
+ const group = claimGroups.get(rewardType) ?? { assets: [], ruleIds: [] };
1312
+ for (const ruleId of ruleData[1].ruleIds) {
1313
+ group.assets.push(strippedType);
1314
+ group.ruleIds.push(ruleId);
1315
+ }
1316
+ claimGroups.set(rewardType, group);
1317
+ }
1318
+ const claimed = [];
1319
+ for (const [rewardType, { assets, ruleIds }] of claimGroups) {
1320
+ const fundId = REWARD_FUNDS[rewardType];
1321
+ const [balance] = tx.moveCall({
1322
+ target: `${config.package}::incentive_v3::claim_reward`,
1323
+ typeArguments: [rewardType],
1324
+ arguments: [
1325
+ tx.object(CLOCK),
1326
+ tx.object(config.incentiveV3),
1327
+ tx.object(config.storage),
1328
+ tx.object(fundId),
1329
+ tx.pure(bcs.bcs.vector(bcs.bcs.string()).serialize(assets)),
1330
+ tx.pure(bcs.bcs.vector(bcs.bcs.Address).serialize(ruleIds))
1331
+ ]
1332
+ });
1333
+ const [coin] = tx.moveCall({
1334
+ target: "0x2::coin::from_balance",
1335
+ typeArguments: [rewardType],
1336
+ arguments: [balance]
1337
+ });
1338
+ tx.transferObjects([coin], address);
1339
+ claimed.push({
1340
+ protocol: "navi",
1341
+ coinType: rewardType,
1342
+ symbol: REWARD_SYMBOLS[rewardType] ?? "UNKNOWN",
1343
+ amount: 0,
1344
+ estimatedValueUsd: 0
1345
+ });
1346
+ }
1347
+ return claimed;
1348
+ }
1181
1349
 
1182
1350
  // src/protocols/yieldTracker.ts
1183
1351
  async function getEarnings(client, keypair) {
@@ -1625,6 +1793,12 @@ var NaviAdapter = class {
1625
1793
  const normalized = normalizeAsset(asset);
1626
1794
  return addRepayToTx(tx, this.client, address, coin, { asset: normalized });
1627
1795
  }
1796
+ async getPendingRewards(address) {
1797
+ return getPendingRewards(this.client, address);
1798
+ }
1799
+ async addClaimRewardsToTx(tx, address) {
1800
+ return addClaimRewardsToTx(tx, this.client, address);
1801
+ }
1628
1802
  };
1629
1803
  var DEFAULT_SLIPPAGE_BPS = 300;
1630
1804
  function createAggregatorClient(client, signer) {
@@ -1908,6 +2082,26 @@ function computeRates(reserve) {
1908
2082
  const depositAprPct = utilizationPct / 100 * (borrowAprPct / 100) * (1 - reserve.spreadFeeBps / 1e4) * 100;
1909
2083
  return { borrowAprPct, depositAprPct };
1910
2084
  }
2085
+ var MS_PER_YEAR = 365.25 * 24 * 3600 * 1e3;
2086
+ function computeDepositRewardApr(reserve, allReserves) {
2087
+ if (reserve.depositTotalShares <= 0 || reserve.price <= 0) return 0;
2088
+ const totalDepositValue = reserve.depositTotalShares / 10 ** reserve.mintDecimals * reserve.price;
2089
+ if (totalDepositValue <= 0) return 0;
2090
+ const priceMap = /* @__PURE__ */ new Map();
2091
+ for (const r of allReserves) {
2092
+ if (r.price > 0) priceMap.set(r.coinType, { price: r.price, decimals: r.mintDecimals });
2093
+ }
2094
+ let rewardApr = 0;
2095
+ for (const rw of reserve.depositPoolRewards) {
2096
+ const info = priceMap.get(rw.coinType);
2097
+ if (!info || info.price <= 0) continue;
2098
+ const durationMs = rw.endTimeMs - rw.startTimeMs;
2099
+ if (durationMs <= 0) continue;
2100
+ const annualTokens = rw.totalRewards / 10 ** info.decimals * (MS_PER_YEAR / durationMs);
2101
+ rewardApr += annualTokens * info.price / totalDepositValue * 100;
2102
+ }
2103
+ return rewardApr;
2104
+ }
1911
2105
  function cTokenRatio(reserve) {
1912
2106
  if (reserve.ctokenSupply === 0) return 1;
1913
2107
  const totalSupply = reserve.availableAmount + reserve.borrowedAmountWad / WAD - reserve.unclaimedSpreadFeesWad / WAD;
@@ -1927,6 +2121,20 @@ function parseReserve(raw, index) {
1927
2121
  const r = f(raw);
1928
2122
  const coinTypeField = f(r.coin_type);
1929
2123
  const config = f(f(r.config)?.element);
2124
+ const dMgr = f(r.deposits_pool_reward_manager);
2125
+ const rawRewards = Array.isArray(dMgr?.pool_rewards) ? dMgr.pool_rewards : [];
2126
+ const now = Date.now();
2127
+ const depositPoolRewards = rawRewards.map((rw, idx) => {
2128
+ if (rw === null) return null;
2129
+ const rwf = f(rw);
2130
+ return {
2131
+ coinType: str(f(rwf.coin_type)?.name),
2132
+ totalRewards: num(rwf.total_rewards),
2133
+ startTimeMs: num(rwf.start_time_ms),
2134
+ endTimeMs: num(rwf.end_time_ms),
2135
+ rewardIndex: idx
2136
+ };
2137
+ }).filter((rw) => rw !== null && rw.endTimeMs > now && rw.totalRewards > 0);
1930
2138
  return {
1931
2139
  coinType: str(coinTypeField?.name),
1932
2140
  mintDecimals: num(r.mint_decimals),
@@ -1940,7 +2148,10 @@ function parseReserve(raw, index) {
1940
2148
  spreadFeeBps: num(config?.spread_fee_bps),
1941
2149
  interestRateUtils: Array.isArray(config?.interest_rate_utils) ? config.interest_rate_utils.map(num) : [],
1942
2150
  interestRateAprs: Array.isArray(config?.interest_rate_aprs) ? config.interest_rate_aprs.map(num) : [],
1943
- arrayIndex: index
2151
+ arrayIndex: index,
2152
+ price: num(f(r.price)?.value) / WAD,
2153
+ depositTotalShares: num(dMgr?.total_shares),
2154
+ depositPoolRewards
1944
2155
  };
1945
2156
  }
1946
2157
  function parseObligation(raw) {
@@ -2085,7 +2296,8 @@ var SuilendAdapter = class {
2085
2296
  const reserve = this.findReserve(reserves, asset);
2086
2297
  if (!reserve) throw new T2000Error("ASSET_NOT_SUPPORTED", `Suilend does not support ${asset}`);
2087
2298
  const { borrowAprPct, depositAprPct } = computeRates(reserve);
2088
- return { asset, saveApy: depositAprPct, borrowApy: borrowAprPct };
2299
+ const rewardApr = computeDepositRewardApr(reserve, reserves);
2300
+ return { asset, saveApy: depositAprPct + rewardApr, borrowApy: borrowAprPct };
2089
2301
  }
2090
2302
  async getPositions(address) {
2091
2303
  const supplies = [];
@@ -2102,7 +2314,8 @@ var SuilendAdapter = class {
2102
2314
  const ratio = cTokenRatio(reserve);
2103
2315
  const amount = dep.ctokenAmount * ratio / 10 ** reserve.mintDecimals;
2104
2316
  const { depositAprPct } = computeRates(reserve);
2105
- supplies.push({ asset: this.resolveSymbol(dep.coinType), amount, apy: depositAprPct });
2317
+ const rewardApr = computeDepositRewardApr(reserve, reserves);
2318
+ supplies.push({ asset: this.resolveSymbol(dep.coinType), amount, apy: depositAprPct + rewardApr });
2106
2319
  }
2107
2320
  for (const bor of obligation.borrows) {
2108
2321
  const reserve = reserves[bor.reserveIdx];
@@ -2472,6 +2685,109 @@ var SuilendAdapter = class {
2472
2685
  }
2473
2686
  return all;
2474
2687
  }
2688
+ // -- Claim Rewards --------------------------------------------------------
2689
+ isClaimableReward(coinType) {
2690
+ const ct = coinType.toLowerCase();
2691
+ return ct.includes("spring_sui") || ct.includes("deep::deep") || ct.includes("cert::cert");
2692
+ }
2693
+ async getPendingRewards(address) {
2694
+ const caps = await this.fetchObligationCaps(address);
2695
+ if (caps.length === 0) return [];
2696
+ const [reserves, obligation] = await Promise.all([
2697
+ this.loadReserves(true),
2698
+ this.fetchObligation(caps[0].obligationId)
2699
+ ]);
2700
+ const rewards = [];
2701
+ const rewardEstimates = /* @__PURE__ */ new Map();
2702
+ for (const dep of obligation.deposits) {
2703
+ const reserve = reserves[dep.reserveIdx];
2704
+ if (!reserve) continue;
2705
+ const ratio = cTokenRatio(reserve);
2706
+ const amount = dep.ctokenAmount * ratio / 10 ** reserve.mintDecimals;
2707
+ const price = reserve.price;
2708
+ const depositUsd = amount * price;
2709
+ for (const rw of reserve.depositPoolRewards) {
2710
+ if (!this.isClaimableReward(rw.coinType)) continue;
2711
+ const rewardReserve = reserves.find((r) => {
2712
+ try {
2713
+ return utils.normalizeStructTag(r.coinType) === utils.normalizeStructTag(rw.coinType);
2714
+ } catch {
2715
+ return false;
2716
+ }
2717
+ });
2718
+ const rewardPrice = rewardReserve?.price ?? 0;
2719
+ const rewardDecimals = rewardReserve?.mintDecimals ?? 9;
2720
+ const durationMs = rw.endTimeMs - rw.startTimeMs;
2721
+ if (durationMs <= 0) continue;
2722
+ const annualTokens = rw.totalRewards / 10 ** rewardDecimals * (MS_PER_YEAR / durationMs);
2723
+ const totalDepositValue = reserve.depositTotalShares / 10 ** reserve.mintDecimals * price;
2724
+ if (totalDepositValue <= 0) continue;
2725
+ const userShare = depositUsd / totalDepositValue;
2726
+ const dailyRewardUsd = annualTokens * rewardPrice / 365 * userShare;
2727
+ rewardEstimates.set(rw.coinType, (rewardEstimates.get(rw.coinType) ?? 0) + dailyRewardUsd);
2728
+ }
2729
+ }
2730
+ for (const [coinType, dailyUsd] of rewardEstimates) {
2731
+ if (dailyUsd < 1e-3) continue;
2732
+ const symbol = coinType.includes("spring_sui") ? "SPRING_SUI" : coinType.includes("deep::") ? "DEEP" : coinType.split("::").pop() ?? "UNKNOWN";
2733
+ rewards.push({
2734
+ protocol: "suilend",
2735
+ coinType,
2736
+ symbol,
2737
+ amount: 0,
2738
+ estimatedValueUsd: dailyUsd
2739
+ });
2740
+ }
2741
+ return rewards;
2742
+ }
2743
+ async addClaimRewardsToTx(tx, address) {
2744
+ const caps = await this.fetchObligationCaps(address);
2745
+ if (caps.length === 0) return [];
2746
+ const [pkg, reserves, obligation] = await Promise.all([
2747
+ this.resolvePackage(),
2748
+ this.loadReserves(true),
2749
+ this.fetchObligation(caps[0].obligationId)
2750
+ ]);
2751
+ const claimsByToken = /* @__PURE__ */ new Map();
2752
+ const claimed = [];
2753
+ for (const dep of obligation.deposits) {
2754
+ const reserve = reserves[dep.reserveIdx];
2755
+ if (!reserve) continue;
2756
+ for (const rw of reserve.depositPoolRewards) {
2757
+ if (!this.isClaimableReward(rw.coinType)) continue;
2758
+ const [coin] = tx.moveCall({
2759
+ target: `${pkg}::lending_market::claim_rewards`,
2760
+ typeArguments: [LENDING_MARKET_TYPE, rw.coinType],
2761
+ arguments: [
2762
+ tx.object(LENDING_MARKET_ID),
2763
+ tx.object(caps[0].id),
2764
+ tx.object(CLOCK2),
2765
+ tx.pure.u64(reserve.arrayIndex),
2766
+ tx.pure.u64(rw.rewardIndex),
2767
+ tx.pure.bool(true)
2768
+ ]
2769
+ });
2770
+ const existing = claimsByToken.get(rw.coinType) ?? [];
2771
+ existing.push(coin);
2772
+ claimsByToken.set(rw.coinType, existing);
2773
+ }
2774
+ }
2775
+ for (const [coinType, coins] of claimsByToken) {
2776
+ if (coins.length > 1) {
2777
+ tx.mergeCoins(coins[0], coins.slice(1));
2778
+ }
2779
+ tx.transferObjects([coins[0]], address);
2780
+ const symbol = coinType.includes("spring_sui") ? "SPRING_SUI" : coinType.includes("deep::") ? "DEEP" : coinType.split("::").pop() ?? "UNKNOWN";
2781
+ claimed.push({
2782
+ protocol: "suilend",
2783
+ coinType,
2784
+ symbol,
2785
+ amount: 0,
2786
+ estimatedValueUsd: 0
2787
+ });
2788
+ }
2789
+ return claimed;
2790
+ }
2475
2791
  };
2476
2792
  function hasLeadingZeroBits(hash, bits) {
2477
2793
  const fullBytes = Math.floor(bits / 8);
@@ -3597,6 +3913,12 @@ To access invested funds: t2000 invest sell ${params.amount} ${asset}`,
3597
3913
  bal.investment = 0;
3598
3914
  bal.investmentPnL = 0;
3599
3915
  }
3916
+ try {
3917
+ const pendingRewards = await this.getPendingRewards();
3918
+ bal.pendingRewards = pendingRewards.reduce((s, r) => s + r.estimatedValueUsd, 0);
3919
+ } catch {
3920
+ bal.pendingRewards = 0;
3921
+ }
3600
3922
  bal.total = bal.available + bal.savings - bal.debt + bal.investment + bal.gasReserve.usdEquiv;
3601
3923
  return bal;
3602
3924
  }
@@ -4546,6 +4868,47 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
4546
4868
  gasMethod: gasResult.gasMethod
4547
4869
  };
4548
4870
  }
4871
+ // -- Claim Rewards --
4872
+ async getPendingRewards() {
4873
+ const adapters = this.registry.listLending();
4874
+ const results = await Promise.allSettled(
4875
+ adapters.filter((a) => a.getPendingRewards).map((a) => a.getPendingRewards(this._address))
4876
+ );
4877
+ const all = [];
4878
+ for (const r of results) {
4879
+ if (r.status === "fulfilled") all.push(...r.value);
4880
+ }
4881
+ return all;
4882
+ }
4883
+ async claimRewards() {
4884
+ this.enforcer.assertNotLocked();
4885
+ const adapters = this.registry.listLending().filter((a) => a.addClaimRewardsToTx);
4886
+ if (adapters.length === 0) {
4887
+ return { success: true, tx: "", rewards: [], totalValueUsd: 0, gasCost: 0, gasMethod: "none" };
4888
+ }
4889
+ const tx = new transactions.Transaction();
4890
+ tx.setSender(this._address);
4891
+ const allRewards = [];
4892
+ for (const adapter of adapters) {
4893
+ try {
4894
+ const claimed = await adapter.addClaimRewardsToTx(tx, this._address);
4895
+ allRewards.push(...claimed);
4896
+ } catch {
4897
+ }
4898
+ }
4899
+ if (allRewards.length === 0) {
4900
+ return { success: true, tx: "", rewards: [], totalValueUsd: 0, gasCost: 0, gasMethod: "none" };
4901
+ }
4902
+ const gasResult = await executeWithGas(this.client, this.keypair, async () => tx);
4903
+ return {
4904
+ success: true,
4905
+ tx: gasResult.digest,
4906
+ rewards: allRewards,
4907
+ totalValueUsd: allRewards.reduce((s, r) => s + r.estimatedValueUsd, 0),
4908
+ gasCost: gasResult.gasCostSui,
4909
+ gasMethod: gasResult.gasMethod
4910
+ };
4911
+ }
4549
4912
  // -- Strategies --
4550
4913
  async investStrategy(params) {
4551
4914
  this.enforcer.assertNotLocked();