@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.
- package/dist/adapters/index.cjs +318 -3
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +318 -3
- package/dist/adapters/index.js.map +1 -1
- package/dist/{index-D4cFY__D.d.cts → index-xQEri-Eu.d.cts} +31 -1
- package/dist/{index-D4cFY__D.d.ts → index-xQEri-Eu.d.ts} +31 -1
- package/dist/index.cjs +366 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +366 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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();
|