@suilend/sdk 13.5.0 → 13.6.0-next.g4bcc630
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/README.md +18 -0
- package/package.json +1 -1
- package/strategies.d.ts +1 -1
- package/strategies.js +29 -13
- package/utils/events.d.ts +13 -12
- package/utils/simulate.d.ts +1 -0
- package/utils/simulate.js +21 -6
package/README.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
A TypeScript SDK for interacting with the Suilend program published on npm as [`@suilend/sdk`](https://www.npmjs.com/package/@suilend/sdk).
|
|
4
4
|
|
|
5
|
+
### Pyth access
|
|
6
|
+
|
|
7
|
+
Public Hermes requires a Pyth API key, so `initializeSuilend` and `initializeSuilendAll` need yours. Without it, every price read fails with 401. The Suilend API's Pyth routes, `/proxy/latest-prices` and `/proxy/price-update-data`, spend our key and only answer Suilend's own frontends, so a third-party browser page cannot use them.
|
|
8
|
+
|
|
9
|
+
On a server, pass the key in `pythConnectionConfig`:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { initializeSuilend } from "@suilend/sdk";
|
|
13
|
+
|
|
14
|
+
const data = await initializeSuilend(suiGrpcClient, suilendClient, undefined, undefined, {
|
|
15
|
+
pythConnectionConfig: { accessToken: process.env.PYTH_KEY! },
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
In a browser, never ship the key in your bundle. Implement a `PriceFeedSource` that reads from your own backend, which holds the key, and pass it as `pythConnection`.
|
|
20
|
+
|
|
21
|
+
`/proxy/price` and `/proxy/prices` stay open to every origin, so `getPrices` from `@suilend/sui-core`, which `initializeSuilend` uses by default, keeps working from any site.
|
|
22
|
+
|
|
5
23
|
---
|
|
6
24
|
|
|
7
25
|
Got a suggestion, running into issues, or have a question? Join our [#dev-support](https://discord.com/channels/1202984617087598622/1238023733403193385) channel on Discord.
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@suilend/sdk","version":"13.
|
|
1
|
+
{"name":"@suilend/sdk","version":"13.6.0-next.g4bcc630","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":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"7.7.0","@cetusprotocol/aggregator-sdk":"1.7.3","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.3","uuid":"14.0.2"},"devDependencies":{"@mysten/bcs":"2.1.2","@mysten/sui":"2.35.0","@suilend/sui-core":"2.2.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.25","@types/node":"26.6.4","fast-check":"4.10.2","prettier":"3.9.9","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.2","vitest":"5.0.3"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.31.3 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
|
package/strategies.d.ts
CHANGED
|
@@ -358,7 +358,7 @@ export declare const strategyDepositAndLoopToExposureTx: (reserveMap: Record<str
|
|
|
358
358
|
}>;
|
|
359
359
|
export declare const strategyWithdrawTx: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, cetusSdk: CetusSdk, cetusPartnerId: string, _address: string, strategyOwnerCapId: TransactionObjectInput, obligationId: string, _deposits: StrategyDeposit[], _borrowedAmount: BigNumber, withdraw: StrategyWithdraw, transaction: Transaction, dryRunTransaction: (transaction: Transaction, setGasBudget?: boolean) => Promise<SuiClientTypes.SimulateTransactionResult<{
|
|
360
360
|
commandResults: true;
|
|
361
|
-
}>>, returnWithdrawnCoin?: boolean) => Promise<{
|
|
361
|
+
}>>, returnWithdrawnCoin?: boolean, rewardsMap?: RewardsMap) => Promise<{
|
|
362
362
|
deposits: StrategyDeposit[];
|
|
363
363
|
borrowedAmount: BigNumber;
|
|
364
364
|
transaction: Transaction;
|
package/strategies.js
CHANGED
|
@@ -2248,11 +2248,34 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2248
2248
|
}
|
|
2249
2249
|
return { deposits, borrowedAmount, transaction };
|
|
2250
2250
|
};
|
|
2251
|
+
const hasStrategyRewardsToClaim = (rewardsMap) => Object.values(rewardsMap).some(({ amount }) => amount.gt(0));
|
|
2252
|
+
const isStrategyPositionClosed = (reserveMap, deposits) => deposits.every((d) => {
|
|
2253
|
+
const reserve = reserveMap[d.coinType];
|
|
2254
|
+
return (reserve !== undefined &&
|
|
2255
|
+
d.depositedAmount
|
|
2256
|
+
.decimalPlaces(reserve.token.decimals, BigNumber.ROUND_DOWN)
|
|
2257
|
+
.lte(0));
|
|
2258
|
+
});
|
|
2259
|
+
const tryClaimStrategyRewardsToUser = async (strategyType, rewardsMap, strategyOwnerCapId, address, transaction, dryRunTransaction) => {
|
|
2260
|
+
const transactionBeforeClaim = Transaction.from(transaction);
|
|
2261
|
+
try {
|
|
2262
|
+
const mergedCoinsMap = strategyClaimRewardsAndMergeCoins(strategyType, rewardsMap, strategyOwnerCapId, transaction);
|
|
2263
|
+
for (const coin of Object.values(mergedCoinsMap)) {
|
|
2264
|
+
transaction.transferObjects([coin], address);
|
|
2265
|
+
}
|
|
2266
|
+
await dryRunTransaction(transaction);
|
|
2267
|
+
return transaction;
|
|
2268
|
+
}
|
|
2269
|
+
catch (err) {
|
|
2270
|
+
console.error(err);
|
|
2271
|
+
return transactionBeforeClaim;
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2251
2274
|
export const strategyWithdrawTx = async (
|
|
2252
2275
|
// AppContext
|
|
2253
2276
|
reserveMap,
|
|
2254
2277
|
// Strategy
|
|
2255
|
-
lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, withdraw, transaction, dryRunTransaction, returnWithdrawnCoin) => {
|
|
2278
|
+
lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, withdraw, transaction, dryRunTransaction, returnWithdrawnCoin, rewardsMap = {}) => {
|
|
2256
2279
|
const strategyInfo = STRATEGY_TYPE_INFO_MAP[strategyType];
|
|
2257
2280
|
const lst = strategyInfo.depositLstCoinType !== undefined
|
|
2258
2281
|
? lstMap[strategyInfo.depositLstCoinType]
|
|
@@ -2420,6 +2443,9 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2420
2443
|
}
|
|
2421
2444
|
}
|
|
2422
2445
|
}
|
|
2446
|
+
if (hasStrategyRewardsToClaim(rewardsMap) &&
|
|
2447
|
+
isStrategyPositionClosed(reserveMap, deposits))
|
|
2448
|
+
transaction = await tryClaimStrategyRewardsToUser(strategyType, rewardsMap, strategyOwnerCapId, _address, transaction, dryRunTransaction);
|
|
2423
2449
|
return { deposits, borrowedAmount, transaction };
|
|
2424
2450
|
};
|
|
2425
2451
|
export const strategyMaxWithdrawTx = async (
|
|
@@ -2435,7 +2461,7 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2435
2461
|
const depositReserves = getStrategyDepositReserves(reserveMap, strategyType);
|
|
2436
2462
|
const borrowReserve = getStrategyBorrowReserve(reserveMap, strategyType);
|
|
2437
2463
|
const defaultCurrencyReserve = getStrategyDefaultCurrencyReserve(reserveMap, strategyType);
|
|
2438
|
-
const hasClaimableRewards =
|
|
2464
|
+
const hasClaimableRewards = hasStrategyRewardsToClaim(rewardsMap);
|
|
2439
2465
|
console.log(`[strategyMaxWithdraw] args |`, JSON.stringify({
|
|
2440
2466
|
_address,
|
|
2441
2467
|
strategyOwnerCapId,
|
|
@@ -2666,17 +2692,7 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2666
2692
|
// Claiming is a bonus, so a failure must not block the withdraw — but the calls
|
|
2667
2693
|
// are appended as they are built, so a throw part-way leaves half a claim behind
|
|
2668
2694
|
// that can abort the whole transaction. Roll back to before the claim instead.
|
|
2669
|
-
|
|
2670
|
-
try {
|
|
2671
|
-
const mergedCoinsMap = strategyClaimRewardsAndMergeCoins(strategyType, rewardsMap, strategyOwnerCapId, transaction);
|
|
2672
|
-
for (const coin of Object.values(mergedCoinsMap)) {
|
|
2673
|
-
transaction.transferObjects([coin], _address);
|
|
2674
|
-
}
|
|
2675
|
-
}
|
|
2676
|
-
catch (err) {
|
|
2677
|
-
console.error(err);
|
|
2678
|
-
transaction = transactionBeforeClaim;
|
|
2679
|
-
}
|
|
2695
|
+
transaction = await tryClaimStrategyRewardsToUser(strategyType, rewardsMap, strategyOwnerCapId, _address, transaction, dryRunTransaction);
|
|
2680
2696
|
}
|
|
2681
2697
|
if (flashLoan !== undefined || repaidByAdjustRepay || borrowedAmount.gt(0)) {
|
|
2682
2698
|
try {
|
package/utils/events.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ type Decimal = {
|
|
|
8
8
|
};
|
|
9
9
|
type address = string;
|
|
10
10
|
type u64 = string;
|
|
11
|
+
type u256 = string;
|
|
11
12
|
export declare enum SuilendEventType {
|
|
12
13
|
InterestUpdateEvent = "InterestUpdateEvent",
|
|
13
14
|
ReserveAssetDataEvent = "ReserveAssetDataEvent",
|
|
@@ -73,20 +74,20 @@ export declare class ReserveAssetDataEventV2 extends TypedParamsSuiEvent<{
|
|
|
73
74
|
lending_market_id: address;
|
|
74
75
|
coin_type: TypeName;
|
|
75
76
|
reserve_id: address;
|
|
76
|
-
available_amount:
|
|
77
|
-
supply_amount:
|
|
78
|
-
borrowed_amount:
|
|
79
|
-
available_amount_usd_estimate:
|
|
80
|
-
supply_amount_usd_estimate:
|
|
81
|
-
borrowed_amount_usd_estimate:
|
|
82
|
-
borrow_apr:
|
|
83
|
-
supply_apr:
|
|
77
|
+
available_amount: u256;
|
|
78
|
+
supply_amount: u256;
|
|
79
|
+
borrowed_amount: u256;
|
|
80
|
+
available_amount_usd_estimate: u256;
|
|
81
|
+
supply_amount_usd_estimate: u256;
|
|
82
|
+
borrowed_amount_usd_estimate: u256;
|
|
83
|
+
borrow_apr: u256;
|
|
84
|
+
supply_apr: u256;
|
|
84
85
|
ctoken_supply: u64;
|
|
85
|
-
cumulative_borrow_rate:
|
|
86
|
-
price:
|
|
87
|
-
smoothed_price:
|
|
86
|
+
cumulative_borrow_rate: u256;
|
|
87
|
+
price: u256;
|
|
88
|
+
smoothed_price: u256;
|
|
88
89
|
price_last_update_timestamp_s: u64;
|
|
89
|
-
conf:
|
|
90
|
+
conf: u256;
|
|
90
91
|
}> {
|
|
91
92
|
}
|
|
92
93
|
export declare class MintEvent extends TypedParamsSuiEvent<{
|
package/utils/simulate.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export interface FeedPrice {
|
|
|
39
39
|
conf?: number;
|
|
40
40
|
}
|
|
41
41
|
export declare const feedConfidenceOf: (reserve: Reserve<string>) => BigNumber | undefined;
|
|
42
|
+
export declare const withUnclampedPrices: (reserve: Reserve<string>, price: Decimal, smoothedPrice: Decimal) => Reserve<string>;
|
|
42
43
|
/**
|
|
43
44
|
* A source of latest prices. Structural on purpose: any object with this method
|
|
44
45
|
* qualifies, so a caller can inject its own client (authenticated, failover,
|
package/utils/simulate.js
CHANGED
|
@@ -120,6 +120,19 @@ export const updatePoolRewardsManager = (manager, nowMs) => {
|
|
|
120
120
|
return updatedManager;
|
|
121
121
|
};
|
|
122
122
|
export const feedConfidenceOf = (reserve) => reserve.feedConfidence;
|
|
123
|
+
export const withUnclampedPrices = (reserve, price, smoothedPrice) => Object.assign(Object.create(Object.getPrototypeOf(reserve)), reserve, {
|
|
124
|
+
unclampedPrices: { price, smoothedPrice },
|
|
125
|
+
});
|
|
126
|
+
/** Mirrors reserve.move's `price_range`. */
|
|
127
|
+
const priceRange = (reserve) => {
|
|
128
|
+
const unclamped = reserve.unclampedPrices;
|
|
129
|
+
const prices = [
|
|
130
|
+
reserve.price,
|
|
131
|
+
reserve.smoothedPrice,
|
|
132
|
+
...(unclamped ? [unclamped.price, unclamped.smoothedPrice] : []),
|
|
133
|
+
].map(decimalToBigNumber);
|
|
134
|
+
return { low: BigNumber.min(...prices), high: BigNumber.max(...prices) };
|
|
135
|
+
};
|
|
123
136
|
// Re-exported, not redefined: it was part of this module's public surface before
|
|
124
137
|
// it moved to ./feedId, and every consumer that normalizes a feed id to compare
|
|
125
138
|
// it against `unpricedCoinTypes` imports it from here.
|
|
@@ -161,6 +174,7 @@ const withFeedPrice = (reserve, priceFeed) => {
|
|
|
161
174
|
newReserve.priceLastUpdateTimestampS = BigInt(priceFeed.publishTimeS);
|
|
162
175
|
newReserve.feedConfidence =
|
|
163
176
|
priceFeed.conf === undefined ? undefined : new BigNumber(priceFeed.conf);
|
|
177
|
+
newReserve.unclampedPrices = undefined;
|
|
164
178
|
return newReserve;
|
|
165
179
|
};
|
|
166
180
|
export const reservePriceFeedIds = (reserves) => Array.from(new Set(reserves.map(reserveFeedId)));
|
|
@@ -416,9 +430,11 @@ confidenceByCoinType) {
|
|
|
416
430
|
.multipliedBy(decimalToBigNumber(reserve.price))
|
|
417
431
|
.dividedBy(new BigNumber(10 ** reserve.mintDecimals));
|
|
418
432
|
const confidence = resolveConfidence(normalizedConfidence?.get(normalizeStructTag(unrefreshedBorrow.coinType)), reserve);
|
|
433
|
+
const { low, high } = priceRange(reserve);
|
|
419
434
|
const upperBoundPrice = confidence !== undefined
|
|
420
|
-
? applyConfidenceMargin(
|
|
421
|
-
|
|
435
|
+
? applyConfidenceMargin(low, high, confidence.conf, confidence.kBps)
|
|
436
|
+
.upperBound
|
|
437
|
+
: high;
|
|
422
438
|
// `exactBorrowedAmount` (this borrow's own amount), not
|
|
423
439
|
// `reserve.borrowedAmount`: the reserve field is the whole reserve's
|
|
424
440
|
// debt, so using it would scale every borrower's upper bound by total
|
|
@@ -500,11 +516,10 @@ export const getCTokenMarketValueLowerBound = (reserve, depositedCTokenAmount,
|
|
|
500
516
|
// min(price, smoothedPrice) lower bound.
|
|
501
517
|
conf, kBps) => {
|
|
502
518
|
const liquidityAmount = depositedCTokenAmount.multipliedBy(cTokenRatio(reserve));
|
|
503
|
-
const
|
|
504
|
-
const smoothedPrice = reserve.smoothedPrice;
|
|
519
|
+
const { low, high } = priceRange(reserve);
|
|
505
520
|
const lowerBoundPrice = conf !== undefined && kBps !== undefined
|
|
506
|
-
? applyConfidenceMargin(
|
|
507
|
-
:
|
|
521
|
+
? applyConfidenceMargin(low, high, conf, kBps).lowerBound
|
|
522
|
+
: low;
|
|
508
523
|
return lowerBoundPrice
|
|
509
524
|
.multipliedBy(liquidityAmount)
|
|
510
525
|
.dividedBy(new BigNumber(10 ** reserve.mintDecimals));
|