defi-dash-sdk 0.1.3 → 0.1.4
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/LICENSE +7 -0
- package/README.md +199 -97
- package/dist/__tests__/deleverageEstimate.unit.test.d.ts +10 -0
- package/dist/__tests__/deleverageEstimate.unit.test.d.ts.map +1 -0
- package/dist/__tests__/deleverageEstimate.unit.test.js +116 -0
- package/dist/__tests__/deleverageEstimate.unit.test.js.map +1 -0
- package/dist/__tests__/findBestLeverageRoute.test.d.ts +12 -0
- package/dist/__tests__/findBestLeverageRoute.test.d.ts.map +1 -0
- package/dist/__tests__/findBestLeverageRoute.test.js +177 -0
- package/dist/__tests__/findBestLeverageRoute.test.js.map +1 -0
- package/dist/__tests__/getAssetApy.test.d.ts +2 -0
- package/dist/__tests__/getAssetApy.test.d.ts.map +1 -0
- package/dist/__tests__/getAssetApy.test.js +133 -0
- package/dist/__tests__/getAssetApy.test.js.map +1 -0
- package/dist/__tests__/getAssetRiskParams.test.d.ts +11 -0
- package/dist/__tests__/getAssetRiskParams.test.d.ts.map +1 -0
- package/dist/__tests__/getAssetRiskParams.test.js +183 -0
- package/dist/__tests__/getAssetRiskParams.test.js.map +1 -0
- package/dist/__tests__/internal.getAssetApy.test.d.ts +2 -0
- package/dist/__tests__/internal.getAssetApy.test.d.ts.map +1 -0
- package/dist/__tests__/internal.getAssetApy.test.js +140 -0
- package/dist/__tests__/internal.getAssetApy.test.js.map +1 -0
- package/dist/__tests__/internal.getAssetRiskParams.test.d.ts +22 -0
- package/dist/__tests__/internal.getAssetRiskParams.test.d.ts.map +1 -0
- package/dist/__tests__/internal.getAssetRiskParams.test.js +194 -0
- package/dist/__tests__/internal.getAssetRiskParams.test.js.map +1 -0
- package/dist/__tests__/internal.getPosition.test.d.ts +22 -0
- package/dist/__tests__/internal.getPosition.test.d.ts.map +1 -0
- package/dist/__tests__/internal.getPosition.test.js +153 -0
- package/dist/__tests__/internal.getPosition.test.js.map +1 -0
- package/dist/__tests__/leveragePreview.unit.test.d.ts +10 -0
- package/dist/__tests__/leveragePreview.unit.test.d.ts.map +1 -0
- package/dist/__tests__/leveragePreview.unit.test.js +168 -0
- package/dist/__tests__/leveragePreview.unit.test.js.map +1 -0
- package/dist/__tests__/leverageRoute.unit.test.d.ts +10 -0
- package/dist/__tests__/leverageRoute.unit.test.d.ts.map +1 -0
- package/dist/__tests__/leverageRoute.unit.test.js +159 -0
- package/dist/__tests__/leverageRoute.unit.test.js.map +1 -0
- package/dist/__tests__/previewLeverage.test.d.ts +13 -0
- package/dist/__tests__/previewLeverage.test.d.ts.map +1 -0
- package/dist/__tests__/previewLeverage.test.js +217 -0
- package/dist/__tests__/previewLeverage.test.js.map +1 -0
- package/dist/__tests__/sdk.bestRoute.test.d.ts +22 -0
- package/dist/__tests__/sdk.bestRoute.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.bestRoute.test.js +186 -0
- package/dist/__tests__/sdk.bestRoute.test.js.map +1 -0
- package/dist/__tests__/sdk.bestRoute.unit.test.d.ts +16 -0
- package/dist/__tests__/sdk.bestRoute.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.bestRoute.unit.test.js +165 -0
- package/dist/__tests__/sdk.bestRoute.unit.test.js.map +1 -0
- package/dist/__tests__/sdk.deleverage.test.d.ts +21 -0
- package/dist/__tests__/sdk.deleverage.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.deleverage.test.js +130 -0
- package/dist/__tests__/sdk.deleverage.test.js.map +1 -0
- package/dist/__tests__/sdk.deleverage.unit.test.d.ts +21 -0
- package/dist/__tests__/sdk.deleverage.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.deleverage.unit.test.js +141 -0
- package/dist/__tests__/sdk.deleverage.unit.test.js.map +1 -0
- package/dist/__tests__/sdk.leverage.test.d.ts +19 -0
- package/dist/__tests__/sdk.leverage.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.leverage.test.js +188 -0
- package/dist/__tests__/sdk.leverage.test.js.map +1 -0
- package/dist/__tests__/sdk.portfolio.test.d.ts +17 -0
- package/dist/__tests__/sdk.portfolio.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.portfolio.test.js +162 -0
- package/dist/__tests__/sdk.portfolio.test.js.map +1 -0
- package/dist/__tests__/sdk.position.test.d.ts +16 -0
- package/dist/__tests__/sdk.position.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.position.test.js +193 -0
- package/dist/__tests__/sdk.position.test.js.map +1 -0
- package/dist/__tests__/sdk.preview.test.d.ts +23 -0
- package/dist/__tests__/sdk.preview.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.preview.test.js +226 -0
- package/dist/__tests__/sdk.preview.test.js.map +1 -0
- package/dist/__tests__/sdk.preview.unit.test.d.ts +18 -0
- package/dist/__tests__/sdk.preview.unit.test.d.ts.map +1 -0
- package/dist/__tests__/sdk.preview.unit.test.js +175 -0
- package/dist/__tests__/sdk.preview.unit.test.js.map +1 -0
- package/dist/__tests__/utils.normalizeCoinType.test.d.ts +7 -0
- package/dist/__tests__/utils.normalizeCoinType.test.d.ts.map +1 -0
- package/dist/__tests__/utils.normalizeCoinType.test.js +42 -0
- package/dist/__tests__/utils.normalizeCoinType.test.js.map +1 -0
- package/dist/index.d.ts +19 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -40
- package/dist/index.js.map +1 -1
- package/dist/protocols/base-adapter.d.ts +42 -75
- package/dist/protocols/base-adapter.d.ts.map +1 -1
- package/dist/protocols/base-adapter.js +34 -77
- package/dist/protocols/base-adapter.js.map +1 -1
- package/dist/protocols/navi/adapter.d.ts +22 -2
- package/dist/protocols/navi/adapter.d.ts.map +1 -1
- package/dist/protocols/navi/adapter.js +145 -11
- package/dist/protocols/navi/adapter.js.map +1 -1
- package/dist/protocols/scallop/adapter.d.ts +26 -6
- package/dist/protocols/scallop/adapter.d.ts.map +1 -1
- package/dist/protocols/scallop/adapter.js +206 -43
- package/dist/protocols/scallop/adapter.js.map +1 -1
- package/dist/protocols/scallop/flash-loan.d.ts +18 -3
- package/dist/protocols/scallop/flash-loan.d.ts.map +1 -1
- package/dist/protocols/scallop/flash-loan.js +79 -20
- package/dist/protocols/scallop/flash-loan.js.map +1 -1
- package/dist/protocols/scallop/types.d.ts.map +1 -1
- package/dist/protocols/scallop/types.js +4 -1
- package/dist/protocols/scallop/types.js.map +1 -1
- package/dist/protocols/suilend/adapter.d.ts +19 -1
- package/dist/protocols/suilend/adapter.d.ts.map +1 -1
- package/dist/protocols/suilend/adapter.js +196 -51
- package/dist/protocols/suilend/adapter.js.map +1 -1
- package/dist/sdk.d.ts +107 -148
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +171 -523
- package/dist/sdk.js.map +1 -1
- package/dist/strategies/common.d.ts +42 -0
- package/dist/strategies/common.d.ts.map +1 -0
- package/dist/strategies/common.js +81 -0
- package/dist/strategies/common.js.map +1 -0
- package/dist/strategies/deleverage.d.ts.map +1 -1
- package/dist/strategies/deleverage.js +44 -62
- package/dist/strategies/deleverage.js.map +1 -1
- package/dist/strategies/index.d.ts +9 -1
- package/dist/strategies/index.d.ts.map +1 -1
- package/dist/strategies/index.js +11 -2
- package/dist/strategies/index.js.map +1 -1
- package/dist/strategies/leverage-preview.d.ts +27 -0
- package/dist/strategies/leverage-preview.d.ts.map +1 -0
- package/dist/strategies/leverage-preview.js +120 -0
- package/dist/strategies/leverage-preview.js.map +1 -0
- package/dist/strategies/leverage-route.d.ts +29 -0
- package/dist/strategies/leverage-route.d.ts.map +1 -0
- package/dist/strategies/leverage-route.js +112 -0
- package/dist/strategies/leverage-route.js.map +1 -0
- package/dist/strategies/leverage.d.ts +4 -44
- package/dist/strategies/leverage.d.ts.map +1 -1
- package/dist/strategies/leverage.js +43 -112
- package/dist/strategies/leverage.js.map +1 -1
- package/dist/strategies/scallop-leverage.d.ts +34 -0
- package/dist/strategies/scallop-leverage.d.ts.map +1 -0
- package/dist/strategies/scallop-leverage.js +143 -0
- package/dist/strategies/scallop-leverage.js.map +1 -0
- package/dist/types/config.d.ts +0 -7
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/constants.d.ts +8 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/constants.js +9 -1
- package/dist/types/constants.js.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/position.d.ts +7 -3
- package/dist/types/position.d.ts.map +1 -1
- package/dist/types/protocol.d.ts +101 -0
- package/dist/types/protocol.d.ts.map +1 -1
- package/dist/types/strategy.d.ts +82 -38
- package/dist/types/strategy.d.ts.map +1 -1
- package/dist/utils/calculations.d.ts +10 -137
- package/dist/utils/calculations.d.ts.map +1 -1
- package/dist/utils/calculations.js +11 -162
- package/dist/utils/calculations.js.map +1 -1
- package/dist/utils/coin.d.ts +16 -7
- package/dist/utils/coin.d.ts.map +1 -1
- package/dist/utils/coin.js +32 -25
- package/dist/utils/coin.js.map +1 -1
- package/dist/utils/errors.d.ts +0 -18
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +2 -40
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/execution.d.ts +38 -0
- package/dist/utils/execution.d.ts.map +1 -0
- package/dist/utils/execution.js +110 -0
- package/dist/utils/execution.js.map +1 -0
- package/dist/utils/gas.d.ts +0 -37
- package/dist/utils/gas.d.ts.map +1 -1
- package/dist/utils/gas.js +2 -60
- package/dist/utils/gas.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +15 -45
package/dist/sdk.js
CHANGED
|
@@ -7,41 +7,40 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.DefiDashSDK = void 0;
|
|
10
|
-
const transactions_1 = require("@mysten/sui/transactions");
|
|
11
10
|
const sdk_ts_1 = require("@7kprotocol/sdk-ts");
|
|
12
11
|
const types_1 = require("./types");
|
|
13
|
-
const sui_scallop_sdk_1 = require("@scallop-io/sui-scallop-sdk");
|
|
14
12
|
const adapter_1 = require("./protocols/suilend/adapter");
|
|
15
13
|
const adapter_2 = require("./protocols/navi/adapter");
|
|
16
14
|
const adapter_3 = require("./protocols/scallop/adapter");
|
|
17
15
|
const flash_loan_1 = require("./protocols/scallop/flash-loan");
|
|
18
16
|
const utils_1 = require("./utils");
|
|
19
|
-
const gas_1 = require("./utils/gas");
|
|
20
17
|
const errors_1 = require("./utils/errors");
|
|
18
|
+
const execution_1 = require("./utils/execution");
|
|
21
19
|
const leverage_1 = require("./strategies/leverage");
|
|
22
20
|
const deleverage_1 = require("./strategies/deleverage");
|
|
23
|
-
const
|
|
21
|
+
const leverage_preview_1 = require("./strategies/leverage-preview");
|
|
22
|
+
const leverage_route_1 = require("./strategies/leverage-route");
|
|
24
23
|
/**
|
|
25
24
|
* DeFi Dash SDK - Main entry point
|
|
26
25
|
*
|
|
27
|
-
* @example Node.js usage:
|
|
26
|
+
* @example Node.js usage (build + execute):
|
|
28
27
|
* ```typescript
|
|
29
|
-
* const sdk =
|
|
30
|
-
*
|
|
31
|
-
* const
|
|
28
|
+
* const sdk = await DefiDashSDK.create(suiClient, keypair);
|
|
29
|
+
*
|
|
30
|
+
* const tx = new Transaction();
|
|
31
|
+
* tx.setSender(address);
|
|
32
|
+
* await sdk.buildLeverageTransaction(tx, { protocol, depositAsset, ... });
|
|
33
|
+
* const result = await sdk.execute(tx); // or sdk.dryRun(tx)
|
|
32
34
|
* ```
|
|
33
35
|
*
|
|
34
36
|
* @example Browser usage:
|
|
35
37
|
* ```typescript
|
|
36
|
-
* const sdk =
|
|
37
|
-
* await sdk.initialize(suiClient, userAddress); // No keypair needed
|
|
38
|
+
* const sdk = await DefiDashSDK.create(suiClient, userAddress);
|
|
38
39
|
*
|
|
39
40
|
* const tx = new Transaction();
|
|
40
41
|
* tx.setSender(userAddress);
|
|
41
42
|
* await sdk.buildLeverageTransaction(tx, { protocol, depositAsset, ... });
|
|
42
|
-
*
|
|
43
|
-
* // Sign with wallet adapter
|
|
44
|
-
* await signAndExecute({ transaction: tx });
|
|
43
|
+
* await signAndExecute({ transaction: tx }); // wallet adapter
|
|
45
44
|
* ```
|
|
46
45
|
*/
|
|
47
46
|
class DefiDashSDK {
|
|
@@ -51,25 +50,34 @@ class DefiDashSDK {
|
|
|
51
50
|
this.options = options;
|
|
52
51
|
}
|
|
53
52
|
/**
|
|
54
|
-
*
|
|
53
|
+
* Create and initialize the SDK in one step (recommended).
|
|
55
54
|
*
|
|
56
55
|
* @param suiClient - Sui client instance
|
|
57
56
|
* @param keypairOrAddress - Ed25519Keypair (Node.js) or user address string (Browser)
|
|
57
|
+
* @param options - SDK options
|
|
58
58
|
*
|
|
59
59
|
* @example Node.js
|
|
60
60
|
* ```typescript
|
|
61
|
-
* await
|
|
61
|
+
* const sdk = await DefiDashSDK.create(suiClient, keypair);
|
|
62
62
|
* ```
|
|
63
63
|
*
|
|
64
64
|
* @example Browser
|
|
65
65
|
* ```typescript
|
|
66
|
-
* await
|
|
66
|
+
* const sdk = await DefiDashSDK.create(suiClient, account.address);
|
|
67
67
|
* ```
|
|
68
68
|
*/
|
|
69
|
+
static async create(suiClient, keypairOrAddress, options = {}) {
|
|
70
|
+
const sdk = new DefiDashSDK(options);
|
|
71
|
+
await sdk.initialize(suiClient, keypairOrAddress);
|
|
72
|
+
return sdk;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Initialize the SDK (internal — use `DefiDashSDK.create()`)
|
|
76
|
+
*/
|
|
69
77
|
async initialize(suiClient, keypairOrAddress) {
|
|
70
78
|
this.suiClient = suiClient;
|
|
71
79
|
// Detect if keypair or address
|
|
72
|
-
if (typeof keypairOrAddress ===
|
|
80
|
+
if (typeof keypairOrAddress === 'string') {
|
|
73
81
|
// Browser mode: address only
|
|
74
82
|
this._userAddress = keypairOrAddress;
|
|
75
83
|
}
|
|
@@ -119,21 +127,9 @@ class DefiDashSDK {
|
|
|
119
127
|
}
|
|
120
128
|
return this._userAddress;
|
|
121
129
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Resolve asset symbol to coin type
|
|
124
|
-
*/
|
|
130
|
+
/** Resolve asset symbol to coin type */
|
|
125
131
|
resolveCoinType(asset) {
|
|
126
|
-
|
|
127
|
-
if (asset.includes("::")) {
|
|
128
|
-
return (0, utils_1.normalizeCoinType)(asset);
|
|
129
|
-
}
|
|
130
|
-
// Look up by symbol
|
|
131
|
-
const upperSymbol = asset.toUpperCase();
|
|
132
|
-
const coinType = types_1.COIN_TYPES[upperSymbol];
|
|
133
|
-
if (coinType) {
|
|
134
|
-
return (0, utils_1.normalizeCoinType)(coinType);
|
|
135
|
-
}
|
|
136
|
-
throw new errors_1.UnknownAssetError(asset);
|
|
132
|
+
return (0, utils_1.resolveCoinType)(asset);
|
|
137
133
|
}
|
|
138
134
|
// ============================================================================
|
|
139
135
|
// Browser-Compatible Transaction Builder Methods
|
|
@@ -166,11 +162,13 @@ class DefiDashSDK {
|
|
|
166
162
|
async buildLeverageTransaction(tx, params) {
|
|
167
163
|
this.ensureInitialized();
|
|
168
164
|
// Validate that exactly one of depositAmount or depositValueUsd is provided
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
const hasAmount = params.depositAmount != null;
|
|
166
|
+
const hasValueUsd = params.depositValueUsd != null;
|
|
167
|
+
if (!hasAmount && !hasValueUsd) {
|
|
168
|
+
throw new errors_1.InvalidParameterError('Either depositAmount or depositValueUsd must be provided');
|
|
171
169
|
}
|
|
172
|
-
if (
|
|
173
|
-
throw new errors_1.InvalidParameterError(
|
|
170
|
+
if (hasAmount && hasValueUsd) {
|
|
171
|
+
throw new errors_1.InvalidParameterError('Cannot provide both depositAmount and depositValueUsd. Choose one.');
|
|
174
172
|
}
|
|
175
173
|
const protocol = this.getProtocol(params.protocol);
|
|
176
174
|
// Clear adapter state for new transaction (Scallop tracks unstaked obligations)
|
|
@@ -178,11 +176,21 @@ class DefiDashSDK {
|
|
|
178
176
|
protocol.clearPendingState?.();
|
|
179
177
|
}
|
|
180
178
|
const coinType = this.resolveCoinType(params.depositAsset);
|
|
181
|
-
const
|
|
182
|
-
|
|
179
|
+
const decimals = (0, utils_1.getDecimals)(coinType);
|
|
180
|
+
// Validate multiplier against protocol limits
|
|
181
|
+
if (params.multiplier <= 1) {
|
|
182
|
+
throw new errors_1.InvalidParameterError(`Multiplier must be greater than 1 (got ${params.multiplier})`);
|
|
183
|
+
}
|
|
184
|
+
const riskParams = await protocol.getAssetRiskParams(coinType);
|
|
185
|
+
if (params.multiplier > riskParams.maxMultiplier) {
|
|
186
|
+
throw new errors_1.InvalidParameterError(`Multiplier ${params.multiplier}x exceeds protocol max ${riskParams.maxMultiplier.toFixed(2)}x (LTV ${(riskParams.ltv * 100).toFixed(0)}%)`);
|
|
187
|
+
}
|
|
183
188
|
// Convert depositValueUsd to depositAmount if needed
|
|
184
189
|
let depositAmountStr;
|
|
185
|
-
if (
|
|
190
|
+
if (hasValueUsd) {
|
|
191
|
+
if (params.depositValueUsd <= 0) {
|
|
192
|
+
throw new errors_1.InvalidParameterError('depositValueUsd must be positive');
|
|
193
|
+
}
|
|
186
194
|
const price = await (0, sdk_ts_1.getTokenPrice)(coinType);
|
|
187
195
|
const amountInToken = params.depositValueUsd / price;
|
|
188
196
|
depositAmountStr = amountInToken.toFixed(decimals);
|
|
@@ -191,6 +199,9 @@ class DefiDashSDK {
|
|
|
191
199
|
depositAmountStr = params.depositAmount;
|
|
192
200
|
}
|
|
193
201
|
const depositAmount = (0, utils_1.parseUnits)(depositAmountStr, decimals);
|
|
202
|
+
if (depositAmount <= 0n) {
|
|
203
|
+
throw new errors_1.InvalidParameterError('depositAmount must be positive');
|
|
204
|
+
}
|
|
194
205
|
await (0, leverage_1.buildLeverageTransaction)(tx, {
|
|
195
206
|
protocol,
|
|
196
207
|
flashLoanClient: this.flashLoanClient,
|
|
@@ -249,193 +260,66 @@ class DefiDashSDK {
|
|
|
249
260
|
});
|
|
250
261
|
}
|
|
251
262
|
// ============================================================================
|
|
252
|
-
//
|
|
263
|
+
// Position Methods
|
|
253
264
|
// ============================================================================
|
|
254
265
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* Opens a leveraged position by:
|
|
258
|
-
* 1. Taking a flash loan
|
|
259
|
-
* 2. Swapping borrowed USDC for deposit asset
|
|
260
|
-
* 3. Depositing total collateral (user deposit + swapped amount)
|
|
261
|
-
* 4. Borrowing USDC to repay flash loan
|
|
262
|
-
*
|
|
263
|
-
* @param params - Leverage parameters
|
|
264
|
-
* @param params.protocol - Lending protocol to use (Suilend, Scallop, or Navi)
|
|
265
|
-
* @param params.depositAsset - Asset symbol (e.g., "SUI", "LBTC") or full coin type
|
|
266
|
-
* @param params.depositAmount - Amount to deposit (required if depositValueUsd not provided)
|
|
267
|
-
* @param params.depositValueUsd - USD value to deposit (required if depositAmount not provided)
|
|
268
|
-
* @param params.multiplier - Leverage multiplier (e.g., 2.0 for 2x leverage)
|
|
269
|
-
* @param params.dryRun - If true, simulates transaction and returns gas estimate without executing
|
|
266
|
+
* Get position for a single protocol
|
|
270
267
|
*
|
|
271
|
-
* @
|
|
272
|
-
*
|
|
273
|
-
* @throws {SDKNotInitializedError} If SDK not initialized
|
|
274
|
-
* @throws {KeypairRequiredError} If keypair not provided (Node.js mode required)
|
|
275
|
-
* @throws {InvalidParameterError} If both or neither depositAmount and depositValueUsd provided
|
|
276
|
-
* @throws {UnknownAssetError} If asset symbol not recognized
|
|
268
|
+
* @param protocol - Lending protocol to query
|
|
269
|
+
* @returns Position info or null if no active position
|
|
277
270
|
*
|
|
278
271
|
* @example
|
|
279
272
|
* ```typescript
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* multiplier: 2.0,
|
|
286
|
-
* dryRun: true
|
|
287
|
-
* });
|
|
288
|
-
*
|
|
289
|
-
* // Leverage with USD value
|
|
290
|
-
* const result = await sdk.leverage({
|
|
291
|
-
* protocol: LendingProtocol.Scallop,
|
|
292
|
-
* depositAsset: 'SUI',
|
|
293
|
-
* depositValueUsd: 100, // $100 worth of SUI
|
|
294
|
-
* multiplier: 3.0,
|
|
295
|
-
* dryRun: false
|
|
296
|
-
* });
|
|
273
|
+
* const position = await sdk.getPosition(LendingProtocol.Navi);
|
|
274
|
+
* if (position) {
|
|
275
|
+
* console.log(`Collateral: ${position.collateral.symbol} $${position.collateral.valueUsd}`);
|
|
276
|
+
* console.log(`Debt: ${position.debt.symbol} $${position.debt.valueUsd}`);
|
|
277
|
+
* }
|
|
297
278
|
* ```
|
|
298
|
-
*
|
|
299
|
-
* @remarks
|
|
300
|
-
* - Requires SDK to be initialized with keypair (Node.js mode)
|
|
301
|
-
* - For browser usage, use {@link buildLeverageTransaction} instead
|
|
302
|
-
* - Scallop protocol uses optimized native SDK for oracle updates
|
|
303
|
-
* - Gas is automatically optimized via dry run (20% buffer added)
|
|
304
279
|
*/
|
|
305
|
-
async
|
|
280
|
+
async getPosition(protocol) {
|
|
306
281
|
this.ensureInitialized();
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
success: false,
|
|
310
|
-
error: "Keypair required for execution. Use buildLeverageTransaction for browser.",
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
// Scallop uses its own SDK builder for oracle updates
|
|
314
|
-
if (params.protocol === types_1.LendingProtocol.Scallop) {
|
|
315
|
-
return this.executeScallopLeverage(params);
|
|
316
|
-
}
|
|
317
|
-
const tx = new transactions_1.Transaction();
|
|
318
|
-
tx.setSender(this.userAddress);
|
|
319
|
-
try {
|
|
320
|
-
await this.buildLeverageTransaction(tx, params);
|
|
321
|
-
if (params.dryRun) {
|
|
322
|
-
return this.dryRunWithGasOptimization(tx);
|
|
323
|
-
}
|
|
324
|
-
// execute() runs dryrun first and optimizes gas budget
|
|
325
|
-
return this.execute(tx);
|
|
326
|
-
}
|
|
327
|
-
catch (error) {
|
|
328
|
-
return {
|
|
329
|
-
success: false,
|
|
330
|
-
error: error.message || String(error),
|
|
331
|
-
};
|
|
332
|
-
}
|
|
282
|
+
return this.getProtocol(protocol).getPosition(this.userAddress);
|
|
333
283
|
}
|
|
334
284
|
/**
|
|
335
|
-
*
|
|
285
|
+
* Get all open positions across all supported protocols
|
|
336
286
|
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* 2. Withdrawing collateral
|
|
340
|
-
* 3. Swapping portion of collateral to USDC
|
|
341
|
-
* 4. Repaying flash loan
|
|
342
|
-
* 5. Keeping remaining collateral
|
|
343
|
-
*
|
|
344
|
-
* @param params - Deleverage parameters
|
|
345
|
-
* @param params.protocol - Lending protocol where position exists
|
|
346
|
-
* @param params.dryRun - If true, simulates transaction without executing
|
|
287
|
+
* Queries Suilend, Navi, and Scallop in parallel and returns
|
|
288
|
+
* only the protocols that have an active position (collateral > 0 or debt > 0).
|
|
347
289
|
*
|
|
348
|
-
* @returns
|
|
349
|
-
*
|
|
350
|
-
* @throws {SDKNotInitializedError} If SDK not initialized
|
|
351
|
-
* @throws {KeypairRequiredError} If keypair not provided (Node.js mode required)
|
|
352
|
-
* @throws {PositionNotFoundError} If no position exists on the protocol
|
|
353
|
-
* @throws {NoDebtError} If position has no debt (use withdraw instead)
|
|
290
|
+
* @returns Array of open positions with protocol identifier
|
|
354
291
|
*
|
|
355
292
|
* @example
|
|
356
293
|
* ```typescript
|
|
357
|
-
*
|
|
358
|
-
* const preview = await sdk.deleverage({
|
|
359
|
-
* protocol: LendingProtocol.Suilend,
|
|
360
|
-
* dryRun: true
|
|
361
|
-
* });
|
|
362
|
-
*
|
|
363
|
-
* if (preview.success) {
|
|
364
|
-
* console.log(`Estimated gas: ${preview.gasUsed}`);
|
|
365
|
-
*
|
|
366
|
-
* // Execute for real
|
|
367
|
-
* const result = await sdk.deleverage({
|
|
368
|
-
* protocol: LendingProtocol.Suilend,
|
|
369
|
-
* dryRun: false
|
|
370
|
-
* });
|
|
294
|
+
* const positions = await sdk.getOpenPositions();
|
|
371
295
|
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
296
|
+
* for (const { protocol, position } of positions) {
|
|
297
|
+
* console.log(`${protocol}: ${position.collateral.symbol} $${position.collateral.valueUsd.toFixed(2)}`);
|
|
298
|
+
* if (position.debt.amount > 0n) {
|
|
299
|
+
* console.log(` Debt: ${position.debt.symbol} $${position.debt.valueUsd.toFixed(2)}`);
|
|
374
300
|
* }
|
|
301
|
+
* console.log(` Net: $${position.netValueUsd.toFixed(2)}`);
|
|
375
302
|
* }
|
|
376
303
|
* ```
|
|
377
|
-
*
|
|
378
|
-
* @remarks
|
|
379
|
-
* - Requires SDK to be initialized with keypair (Node.js mode)
|
|
380
|
-
* - For browser usage, use {@link buildDeleverageTransaction} instead
|
|
381
|
-
* - Automatically fetches current position and calculates optimal swap amounts
|
|
382
|
-
* - Gas is automatically optimized via dry run (20% buffer added)
|
|
383
304
|
*/
|
|
384
|
-
async
|
|
305
|
+
async getOpenPositions() {
|
|
385
306
|
this.ensureInitialized();
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
await this.buildDeleverageTransaction(tx, params);
|
|
396
|
-
if (params.dryRun) {
|
|
397
|
-
return this.dryRunWithGasOptimization(tx);
|
|
307
|
+
const allProtocols = [
|
|
308
|
+
types_1.LendingProtocol.Suilend,
|
|
309
|
+
types_1.LendingProtocol.Navi,
|
|
310
|
+
types_1.LendingProtocol.Scallop,
|
|
311
|
+
];
|
|
312
|
+
const results = await Promise.all(allProtocols.map(async (p) => {
|
|
313
|
+
try {
|
|
314
|
+
const position = await this.getProtocol(p).getPosition(this.userAddress);
|
|
315
|
+
return position ? { protocol: p, position } : null;
|
|
398
316
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
error: error.message || String(error),
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
// ============================================================================
|
|
410
|
-
// Position Methods
|
|
411
|
-
// ============================================================================
|
|
412
|
-
/**
|
|
413
|
-
* Get current lending position on a specific protocol
|
|
414
|
-
*
|
|
415
|
-
* @param protocol - The lending protocol to query
|
|
416
|
-
*
|
|
417
|
-
* @returns Position information including collateral, debt, and metrics, or null if no position exists
|
|
418
|
-
*
|
|
419
|
-
* @throws {SDKNotInitializedError} If SDK not initialized
|
|
420
|
-
* @throws {UnsupportedProtocolError} If protocol not supported
|
|
421
|
-
*
|
|
422
|
-
* @example
|
|
423
|
-
* ```typescript
|
|
424
|
-
* const position = await sdk.getPosition(LendingProtocol.Suilend);
|
|
425
|
-
*
|
|
426
|
-
* if (position) {
|
|
427
|
-
* console.log(`Collateral: ${position.collateral.amount} ${position.collateral.symbol}`);
|
|
428
|
-
* console.log(`Debt: ${position.debt.amount} ${position.debt.symbol}`);
|
|
429
|
-
* console.log(`Health Factor: ${position.healthFactor}`);
|
|
430
|
-
* console.log(`Net Value: $${position.netValueUsd}`);
|
|
431
|
-
* } else {
|
|
432
|
-
* console.log('No position found');
|
|
433
|
-
* }
|
|
434
|
-
* ```
|
|
435
|
-
*/
|
|
436
|
-
async getPosition(protocol) {
|
|
437
|
-
this.ensureInitialized();
|
|
438
|
-
return this.getProtocol(protocol).getPosition(this.userAddress);
|
|
317
|
+
catch (e) {
|
|
318
|
+
console.warn(`[DefiDashSDK] Failed to fetch position for ${p}:`, e);
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}));
|
|
322
|
+
return results.filter((r) => r !== null);
|
|
439
323
|
}
|
|
440
324
|
// ============================================================================
|
|
441
325
|
// Aggregation Methods
|
|
@@ -488,7 +372,7 @@ class DefiDashSDK {
|
|
|
488
372
|
}
|
|
489
373
|
}
|
|
490
374
|
catch (e) {
|
|
491
|
-
|
|
375
|
+
console.warn(`[DefiDashSDK] Failed to fetch portfolio for ${p}:`, e);
|
|
492
376
|
}
|
|
493
377
|
// Return resilient default
|
|
494
378
|
return {
|
|
@@ -513,6 +397,7 @@ class DefiDashSDK {
|
|
|
513
397
|
* Useful for showing users what their leveraged position will look like.
|
|
514
398
|
*
|
|
515
399
|
* @param params - Preview parameters
|
|
400
|
+
* @param params.protocol - Lending protocol to use (suilend, navi, scallop)
|
|
516
401
|
* @param params.depositAsset - Asset symbol (e.g., "SUI", "LBTC") or full coin type
|
|
517
402
|
* @param params.depositAmount - Amount to deposit (required if depositValueUsd not provided)
|
|
518
403
|
* @param params.depositValueUsd - USD value to deposit (required if depositAmount not provided)
|
|
@@ -521,12 +406,14 @@ class DefiDashSDK {
|
|
|
521
406
|
* @returns Preview containing position metrics, flash loan details, and risk parameters
|
|
522
407
|
*
|
|
523
408
|
* @throws {InvalidParameterError} If both or neither depositAmount and depositValueUsd provided
|
|
409
|
+
* @throws {InvalidParameterError} If multiplier exceeds protocol's max multiplier
|
|
524
410
|
* @throws {UnknownAssetError} If asset symbol not recognized
|
|
525
411
|
*
|
|
526
412
|
* @example
|
|
527
413
|
* ```typescript
|
|
528
414
|
* // Preview with fixed amount
|
|
529
415
|
* const preview = await sdk.previewLeverage({
|
|
416
|
+
* protocol: 'suilend',
|
|
530
417
|
* depositAsset: 'LBTC',
|
|
531
418
|
* depositAmount: '0.001',
|
|
532
419
|
* multiplier: 2.0
|
|
@@ -536,11 +423,13 @@ class DefiDashSDK {
|
|
|
536
423
|
* console.log(`Flash Loan: ${preview.flashLoanUsdc / 1e6} USDC`);
|
|
537
424
|
* console.log(`Total Position: $${preview.totalPositionUsd}`);
|
|
538
425
|
* console.log(`Position LTV: ${preview.ltvPercent.toFixed(1)}%`);
|
|
426
|
+
* console.log(`Max Multiplier: ${preview.maxMultiplier.toFixed(2)}x`);
|
|
539
427
|
* console.log(`Liquidation Price: $${preview.liquidationPrice}`);
|
|
540
428
|
* console.log(`Price Drop Buffer: ${preview.priceDropBuffer.toFixed(1)}%`);
|
|
541
429
|
*
|
|
542
430
|
* // Preview with USD value
|
|
543
431
|
* const preview2 = await sdk.previewLeverage({
|
|
432
|
+
* protocol: 'scallop',
|
|
544
433
|
* depositAsset: 'SUI',
|
|
545
434
|
* depositValueUsd: 100, // $100 worth
|
|
546
435
|
* multiplier: 3.0
|
|
@@ -548,37 +437,59 @@ class DefiDashSDK {
|
|
|
548
437
|
* ```
|
|
549
438
|
*
|
|
550
439
|
* @remarks
|
|
551
|
-
* -
|
|
440
|
+
* - Queries protocol-specific LTV to calculate accurate max multiplier
|
|
441
|
+
* - Max multiplier = 1 / (1 - LTV), e.g., 65% LTV → 2.857x max
|
|
552
442
|
* - Fetches current market prices from 7k Protocol
|
|
553
443
|
* - Calculations are estimates; actual execution may differ slightly
|
|
554
444
|
* - Higher multipliers increase both returns and liquidation risk
|
|
555
445
|
*/
|
|
556
446
|
async previewLeverage(params) {
|
|
557
|
-
|
|
558
|
-
if (!params.depositAmount && !params.depositValueUsd) {
|
|
559
|
-
throw new errors_1.InvalidParameterError("Either depositAmount or depositValueUsd must be provided");
|
|
560
|
-
}
|
|
561
|
-
if (params.depositAmount && params.depositValueUsd) {
|
|
562
|
-
throw new errors_1.InvalidParameterError("Cannot provide both depositAmount and depositValueUsd. Choose one.");
|
|
563
|
-
}
|
|
447
|
+
const protocol = this.getProtocol(params.protocol);
|
|
564
448
|
const coinType = this.resolveCoinType(params.depositAsset);
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
if (params.depositValueUsd) {
|
|
570
|
-
const price = await (0, sdk_ts_1.getTokenPrice)(coinType);
|
|
571
|
-
const amountInToken = params.depositValueUsd / price;
|
|
572
|
-
depositAmountStr = amountInToken.toFixed(decimals);
|
|
573
|
-
}
|
|
574
|
-
else {
|
|
575
|
-
depositAmountStr = params.depositAmount;
|
|
576
|
-
}
|
|
577
|
-
const depositAmount = (0, utils_1.parseUnits)(depositAmountStr, decimals);
|
|
578
|
-
return (0, leverage_1.calculateLeveragePreview)({
|
|
579
|
-
depositCoinType: coinType,
|
|
580
|
-
depositAmount,
|
|
449
|
+
return (0, leverage_preview_1.previewLeverage)({
|
|
450
|
+
coinType,
|
|
451
|
+
depositAmount: params.depositAmount,
|
|
452
|
+
depositValueUsd: params.depositValueUsd,
|
|
581
453
|
multiplier: params.multiplier,
|
|
454
|
+
}, {
|
|
455
|
+
protocol,
|
|
456
|
+
swapClient: this.swapClient,
|
|
457
|
+
suiClient: this.suiClient,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
// ============================================================================
|
|
461
|
+
// Route Finding
|
|
462
|
+
// ============================================================================
|
|
463
|
+
/**
|
|
464
|
+
* Find the best leverage route across all initialized protocols for a given asset.
|
|
465
|
+
*
|
|
466
|
+
* Returns two recommendations:
|
|
467
|
+
* 1. **bestMaxMultiplier** — the protocol offering the highest possible leverage
|
|
468
|
+
* 2. **bestApy** — the protocol with the highest net APY at a safe multiplier
|
|
469
|
+
*
|
|
470
|
+
* The safe multiplier = min(maxMultiplier across protocols) - LEVERAGE_MULTIPLIER_BUFFER
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```typescript
|
|
474
|
+
* const route = await sdk.findBestLeverageRoute({
|
|
475
|
+
* depositAsset: 'SUI',
|
|
476
|
+
* depositValueUsd: 100,
|
|
477
|
+
* });
|
|
478
|
+
*
|
|
479
|
+
* console.log(route.bestMaxMultiplier.protocol); // e.g. 'scallop'
|
|
480
|
+
* console.log(route.bestApy.protocol); // e.g. 'suilend'
|
|
481
|
+
* console.log(route.safeMultiplier); // e.g. 2.83
|
|
482
|
+
* ```
|
|
483
|
+
*/
|
|
484
|
+
async findBestLeverageRoute(params) {
|
|
485
|
+
this.ensureInitialized();
|
|
486
|
+
return (0, leverage_route_1.findBestLeverageRoute)(params, {
|
|
487
|
+
protocols: this.protocols,
|
|
488
|
+
previewFn: (protocol, previewParams) => this.previewLeverage({
|
|
489
|
+
protocol,
|
|
490
|
+
...previewParams,
|
|
491
|
+
}),
|
|
492
|
+
resolveCoinType: (asset) => this.resolveCoinType(asset),
|
|
582
493
|
});
|
|
583
494
|
}
|
|
584
495
|
// ============================================================================
|
|
@@ -605,34 +516,31 @@ class DefiDashSDK {
|
|
|
605
516
|
return this.userAddress;
|
|
606
517
|
}
|
|
607
518
|
// ============================================================================
|
|
608
|
-
//
|
|
519
|
+
// Execution Methods
|
|
609
520
|
// ============================================================================
|
|
610
521
|
/**
|
|
611
|
-
* Dry run with gas optimization
|
|
522
|
+
* Dry run a transaction with gas optimization
|
|
523
|
+
*
|
|
524
|
+
* Simulates the transaction and returns estimated gas usage.
|
|
525
|
+
* Does NOT execute the transaction on-chain.
|
|
612
526
|
*
|
|
613
|
-
*
|
|
527
|
+
* @param tx - Built transaction to simulate
|
|
528
|
+
* @returns Strategy result with gas estimate
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```typescript
|
|
532
|
+
* const tx = new Transaction();
|
|
533
|
+
* tx.setSender(address);
|
|
534
|
+
* await sdk.buildLeverageTransaction(tx, params);
|
|
535
|
+
* const result = await sdk.dryRun(tx);
|
|
536
|
+
* console.log(`Estimated gas: ${result.gasUsed}`);
|
|
537
|
+
* ```
|
|
614
538
|
*/
|
|
615
|
-
async
|
|
616
|
-
|
|
617
|
-
tx.setGasBudget(gas_1.DRYRUN_GAS_BUDGET);
|
|
618
|
-
const result = await this.suiClient.dryRunTransactionBlock({
|
|
619
|
-
transactionBlock: await tx.build({ client: this.suiClient }),
|
|
620
|
-
});
|
|
621
|
-
if (result.effects.status.status === "success") {
|
|
622
|
-
const actualGas = (0, gas_1.calculateActualGas)(result.effects.gasUsed);
|
|
623
|
-
const optimizedBudget = (0, gas_1.calculateOptimizedBudget)(actualGas);
|
|
624
|
-
return {
|
|
625
|
-
success: true,
|
|
626
|
-
gasUsed: optimizedBudget,
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
return {
|
|
630
|
-
success: false,
|
|
631
|
-
error: result.effects.status.error || "Dry run failed",
|
|
632
|
-
};
|
|
539
|
+
async dryRun(tx) {
|
|
540
|
+
return (0, execution_1.dryRunTransaction)(this.suiClient, tx);
|
|
633
541
|
}
|
|
634
542
|
/**
|
|
635
|
-
* Execute transaction with gas optimization
|
|
543
|
+
* Execute a transaction with gas optimization (Node.js only)
|
|
636
544
|
*
|
|
637
545
|
* Flow:
|
|
638
546
|
* 1. Dryrun with small fixed budget to get actual gas usage
|
|
@@ -640,285 +548,25 @@ class DefiDashSDK {
|
|
|
640
548
|
* 3. Check user has enough balance
|
|
641
549
|
* 4. Execute with optimized budget
|
|
642
550
|
*
|
|
643
|
-
*
|
|
551
|
+
* @param tx - Built transaction to execute
|
|
552
|
+
* @returns Strategy result with transaction digest and gas used
|
|
553
|
+
*
|
|
554
|
+
* @throws {KeypairRequiredError} If keypair not provided
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
* ```typescript
|
|
558
|
+
* const tx = new Transaction();
|
|
559
|
+
* tx.setSender(address);
|
|
560
|
+
* await sdk.buildLeverageTransaction(tx, params);
|
|
561
|
+
* const result = await sdk.execute(tx);
|
|
562
|
+
* console.log(`TX: ${result.txDigest}`);
|
|
563
|
+
* ```
|
|
644
564
|
*/
|
|
645
565
|
async execute(tx) {
|
|
646
566
|
if (!this.keypair) {
|
|
647
567
|
throw new errors_1.KeypairRequiredError();
|
|
648
568
|
}
|
|
649
|
-
|
|
650
|
-
const balance = await this.suiClient.getBalance({
|
|
651
|
-
owner: this.userAddress,
|
|
652
|
-
});
|
|
653
|
-
const userBalance = BigInt(balance.totalBalance);
|
|
654
|
-
// Step 2: Set dryrun budget (use available balance or default, whichever is lower)
|
|
655
|
-
const dryrunBudget = userBalance < BigInt(gas_1.DRYRUN_GAS_BUDGET)
|
|
656
|
-
? userBalance
|
|
657
|
-
: BigInt(gas_1.DRYRUN_GAS_BUDGET);
|
|
658
|
-
if (dryrunBudget < 10000000n) {
|
|
659
|
-
// Min 0.01 SUI for dryrun
|
|
660
|
-
return {
|
|
661
|
-
success: false,
|
|
662
|
-
error: `Insufficient balance for gas. Have: ${Number(userBalance) / 1e9} SUI`,
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
tx.setGasBudget(dryrunBudget);
|
|
666
|
-
const dryRunResult = await this.suiClient.dryRunTransactionBlock({
|
|
667
|
-
transactionBlock: await tx.build({ client: this.suiClient }),
|
|
668
|
-
});
|
|
669
|
-
if (dryRunResult.effects.status.status !== "success") {
|
|
670
|
-
return {
|
|
671
|
-
success: false,
|
|
672
|
-
error: `Dry run failed: ${dryRunResult.effects.status.error}`,
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
// Step 3: Calculate optimized gas budget (actual + 20% buffer)
|
|
676
|
-
const actualGas = (0, gas_1.calculateActualGas)(dryRunResult.effects.gasUsed);
|
|
677
|
-
const optimizedBudget = (0, gas_1.calculateOptimizedBudget)(actualGas);
|
|
678
|
-
// Step 4: Check if user has enough balance for actual execution
|
|
679
|
-
if (userBalance < optimizedBudget) {
|
|
680
|
-
return {
|
|
681
|
-
success: false,
|
|
682
|
-
error: `Insufficient balance for gas. Need: ${Number(optimizedBudget) / 1e9} SUI, Have: ${Number(userBalance) / 1e9} SUI`,
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
// Step 4: Execute with optimized gas budget
|
|
686
|
-
tx.setGasBudget(optimizedBudget);
|
|
687
|
-
const result = await this.suiClient.signAndExecuteTransaction({
|
|
688
|
-
signer: this.keypair,
|
|
689
|
-
transaction: tx,
|
|
690
|
-
options: {
|
|
691
|
-
showEffects: true,
|
|
692
|
-
},
|
|
693
|
-
});
|
|
694
|
-
if (result.effects?.status.status === "success") {
|
|
695
|
-
return {
|
|
696
|
-
success: true,
|
|
697
|
-
txDigest: result.digest,
|
|
698
|
-
gasUsed: BigInt(result.effects.gasUsed.computationCost),
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
return {
|
|
702
|
-
success: false,
|
|
703
|
-
txDigest: result.digest,
|
|
704
|
-
error: result.effects?.status.error || "Execution failed",
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
// ============================================================================
|
|
708
|
-
// Scallop-Specific Methods (uses Scallop SDK builder for oracle updates)
|
|
709
|
-
// ============================================================================
|
|
710
|
-
/**
|
|
711
|
-
* Execute Scallop leverage using native Scallop SDK builder
|
|
712
|
-
*
|
|
713
|
-
* Scallop requires oracle price updates via their SDK's updateAssetPricesQuick.
|
|
714
|
-
* This method uses the Scallop SDK builder internally.
|
|
715
|
-
*/
|
|
716
|
-
async executeScallopLeverage(params) {
|
|
717
|
-
if (!this.keypair) {
|
|
718
|
-
return { success: false, error: "Keypair required" };
|
|
719
|
-
}
|
|
720
|
-
try {
|
|
721
|
-
// Resolve coin type and amounts
|
|
722
|
-
const coinType = this.resolveCoinType(params.depositAsset);
|
|
723
|
-
const reserve = (0, constants_1.getReserveByCoinType)(coinType);
|
|
724
|
-
const decimals = reserve?.decimals || 9;
|
|
725
|
-
const symbol = coinType.split("::").pop()?.toUpperCase() || "SUI";
|
|
726
|
-
const isSui = coinType.endsWith("::sui::SUI");
|
|
727
|
-
// Get coin name for Scallop (e.g., "sui", "usdc")
|
|
728
|
-
const coinName = this.getScallopCoinName(coinType);
|
|
729
|
-
// Calculate deposit amount
|
|
730
|
-
let depositAmountStr;
|
|
731
|
-
if (params.depositValueUsd) {
|
|
732
|
-
const price = await (0, sdk_ts_1.getTokenPrice)(coinType);
|
|
733
|
-
depositAmountStr = (params.depositValueUsd / price).toFixed(decimals);
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
depositAmountStr = params.depositAmount;
|
|
737
|
-
}
|
|
738
|
-
const depositAmountRaw = (0, utils_1.parseUnits)(depositAmountStr, decimals);
|
|
739
|
-
const depositAmountHuman = parseFloat(depositAmountStr);
|
|
740
|
-
// Calculate flash loan amount
|
|
741
|
-
const depositPrice = await (0, sdk_ts_1.getTokenPrice)(coinType);
|
|
742
|
-
const initialEquityUsd = depositAmountHuman * depositPrice;
|
|
743
|
-
const flashLoanUsd = initialEquityUsd * (params.multiplier - 1);
|
|
744
|
-
const flashLoanUsdc = BigInt(Math.ceil(flashLoanUsd * 1e6 * 1.02));
|
|
745
|
-
const flashLoanFee = flash_loan_1.ScallopFlashLoanClient.calculateFee(flashLoanUsdc);
|
|
746
|
-
const repaymentAmount = flashLoanUsdc + flashLoanFee;
|
|
747
|
-
const borrowFeeBuffer = 1.003;
|
|
748
|
-
const borrowAmount = BigInt(Math.ceil(Number(repaymentAmount) * borrowFeeBuffer));
|
|
749
|
-
// Initialize Scallop SDK with secret key
|
|
750
|
-
// Scallop SDK requires the original secret key string (not extracted from keypair)
|
|
751
|
-
if (!this.options.secretKey) {
|
|
752
|
-
return {
|
|
753
|
-
success: false,
|
|
754
|
-
error: "Scallop operations require secretKey in SDK options. Pass { secretKey: 'suiprivkey...' } to DefiDashSDK constructor.",
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
const scallop = new sui_scallop_sdk_1.Scallop({
|
|
758
|
-
secretKey: this.options.secretKey,
|
|
759
|
-
networkType: "mainnet",
|
|
760
|
-
});
|
|
761
|
-
await scallop.init();
|
|
762
|
-
const builder = await scallop.createScallopBuilder();
|
|
763
|
-
const client = await scallop.createScallopClient();
|
|
764
|
-
const tx = builder.createTxBlock();
|
|
765
|
-
tx.setSender(this.userAddress);
|
|
766
|
-
// Check for existing obligation
|
|
767
|
-
const existingObligations = await client.getObligations();
|
|
768
|
-
const hasExistingObligation = existingObligations.length > 0;
|
|
769
|
-
let existingObligationId = null;
|
|
770
|
-
let existingObligationKeyId = null;
|
|
771
|
-
let isCurrentlyLocked = false;
|
|
772
|
-
if (hasExistingObligation) {
|
|
773
|
-
existingObligationId = existingObligations[0].id;
|
|
774
|
-
existingObligationKeyId = existingObligations[0].keyId;
|
|
775
|
-
isCurrentlyLocked = existingObligations[0].locked;
|
|
776
|
-
}
|
|
777
|
-
// Get swap quote
|
|
778
|
-
const swapQuotes = await this.swapClient.quote({
|
|
779
|
-
amountIn: flashLoanUsdc.toString(),
|
|
780
|
-
coinTypeIn: types_1.COIN_TYPES.USDC,
|
|
781
|
-
coinTypeOut: coinType,
|
|
782
|
-
});
|
|
783
|
-
if (swapQuotes.length === 0) {
|
|
784
|
-
return { success: false, error: `No swap quotes for USDC → ${symbol}` };
|
|
785
|
-
}
|
|
786
|
-
const bestQuote = swapQuotes.sort((a, b) => Number(b.amountOut) - Number(a.amountOut))[0];
|
|
787
|
-
// Build transaction using Scallop SDK
|
|
788
|
-
// Step 1: Flash loan USDC
|
|
789
|
-
const [loanCoin, receipt] = await tx.borrowFlashLoan(Number(flashLoanUsdc), "usdc");
|
|
790
|
-
// Step 2: Swap USDC → deposit asset
|
|
791
|
-
const swappedAsset = await this.swapClient.swap({
|
|
792
|
-
quote: bestQuote,
|
|
793
|
-
signer: this.userAddress,
|
|
794
|
-
coinIn: loanCoin,
|
|
795
|
-
tx: tx.txBlock,
|
|
796
|
-
}, 100);
|
|
797
|
-
// Step 3: Prepare deposit coin
|
|
798
|
-
let depositCoin;
|
|
799
|
-
if (isSui) {
|
|
800
|
-
const [userDeposit] = tx.splitSUIFromGas([Number(depositAmountRaw)]);
|
|
801
|
-
tx.mergeCoins(userDeposit, [swappedAsset]);
|
|
802
|
-
depositCoin = userDeposit;
|
|
803
|
-
}
|
|
804
|
-
else {
|
|
805
|
-
const userCoins = await this.suiClient.getCoins({
|
|
806
|
-
owner: this.userAddress,
|
|
807
|
-
coinType,
|
|
808
|
-
});
|
|
809
|
-
if (userCoins.data.length === 0) {
|
|
810
|
-
return { success: false, error: `No ${symbol} coins in wallet` };
|
|
811
|
-
}
|
|
812
|
-
const primaryCoin = tx.txBlock.object(userCoins.data[0].coinObjectId);
|
|
813
|
-
if (userCoins.data.length > 1) {
|
|
814
|
-
const otherCoins = userCoins.data
|
|
815
|
-
.slice(1)
|
|
816
|
-
.map((c) => tx.txBlock.object(c.coinObjectId));
|
|
817
|
-
tx.mergeCoins(primaryCoin, otherCoins);
|
|
818
|
-
}
|
|
819
|
-
const [userContribution] = tx.splitCoins(primaryCoin, [
|
|
820
|
-
Number(depositAmountRaw),
|
|
821
|
-
]);
|
|
822
|
-
tx.mergeCoins(userContribution, [swappedAsset]);
|
|
823
|
-
depositCoin = userContribution;
|
|
824
|
-
}
|
|
825
|
-
// Step 4: Handle obligation
|
|
826
|
-
let obligation;
|
|
827
|
-
let obligationKey;
|
|
828
|
-
let obligationHotPotato;
|
|
829
|
-
let isNewObligation = false;
|
|
830
|
-
if (hasExistingObligation && existingObligationId && existingObligationKeyId) {
|
|
831
|
-
obligation = tx.txBlock.object(existingObligationId);
|
|
832
|
-
obligationKey = tx.txBlock.object(existingObligationKeyId);
|
|
833
|
-
if (isCurrentlyLocked) {
|
|
834
|
-
tx.unstakeObligation(obligation, obligationKey);
|
|
835
|
-
}
|
|
836
|
-
tx.addCollateral(obligation, depositCoin, coinName);
|
|
837
|
-
}
|
|
838
|
-
else {
|
|
839
|
-
[obligation, obligationKey, obligationHotPotato] = tx.openObligation();
|
|
840
|
-
tx.addCollateral(obligation, depositCoin, coinName);
|
|
841
|
-
isNewObligation = true;
|
|
842
|
-
}
|
|
843
|
-
// Step 5: Update oracles (critical for Scallop!)
|
|
844
|
-
await tx.updateAssetPricesQuick([coinName, "usdc"]);
|
|
845
|
-
// Step 6: Borrow USDC
|
|
846
|
-
const borrowedUsdc = tx.borrow(obligation, obligationKey, Number(borrowAmount), "usdc");
|
|
847
|
-
// Step 7: Repay flash loan
|
|
848
|
-
await tx.repayFlashLoan(borrowedUsdc, receipt, "usdc");
|
|
849
|
-
// Step 8: Finalize
|
|
850
|
-
if (isNewObligation) {
|
|
851
|
-
tx.returnObligation(obligation, obligationHotPotato);
|
|
852
|
-
tx.stakeObligation(obligation, obligationKey);
|
|
853
|
-
tx.transferObjects([obligationKey], this.userAddress);
|
|
854
|
-
}
|
|
855
|
-
else {
|
|
856
|
-
tx.stakeObligation(obligation, obligationKey);
|
|
857
|
-
}
|
|
858
|
-
// Execute
|
|
859
|
-
if (params.dryRun) {
|
|
860
|
-
tx.txBlock.setGasBudget(gas_1.DRYRUN_GAS_BUDGET);
|
|
861
|
-
const dryRunResult = await this.suiClient.dryRunTransactionBlock({
|
|
862
|
-
transactionBlock: await tx.txBlock.build({ client: this.suiClient }),
|
|
863
|
-
});
|
|
864
|
-
if (dryRunResult.effects.status.status === "success") {
|
|
865
|
-
const actualGas = (0, gas_1.calculateActualGas)(dryRunResult.effects.gasUsed);
|
|
866
|
-
const optimizedBudget = (0, gas_1.calculateOptimizedBudget)(actualGas);
|
|
867
|
-
return { success: true, gasUsed: optimizedBudget };
|
|
868
|
-
}
|
|
869
|
-
return {
|
|
870
|
-
success: false,
|
|
871
|
-
error: dryRunResult.effects.status.error || "Dry run failed",
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
// For real execution, do dryrun first to optimize gas
|
|
875
|
-
tx.txBlock.setGasBudget(gas_1.DRYRUN_GAS_BUDGET);
|
|
876
|
-
const dryRunResult = await this.suiClient.dryRunTransactionBlock({
|
|
877
|
-
transactionBlock: await tx.txBlock.build({ client: this.suiClient }),
|
|
878
|
-
});
|
|
879
|
-
if (dryRunResult.effects.status.status !== "success") {
|
|
880
|
-
return {
|
|
881
|
-
success: false,
|
|
882
|
-
error: `Dry run failed: ${dryRunResult.effects.status.error}`,
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
const actualGas = (0, gas_1.calculateActualGas)(dryRunResult.effects.gasUsed);
|
|
886
|
-
const optimizedBudget = (0, gas_1.calculateOptimizedBudget)(actualGas);
|
|
887
|
-
tx.txBlock.setGasBudget(optimizedBudget);
|
|
888
|
-
const result = await builder.signAndSendTxBlock(tx);
|
|
889
|
-
// Fetch transaction details to get actual gas used
|
|
890
|
-
const txDetails = await this.suiClient.waitForTransaction({
|
|
891
|
-
digest: result.digest,
|
|
892
|
-
options: { showEffects: true },
|
|
893
|
-
});
|
|
894
|
-
const gasUsed = txDetails.effects?.gasUsed
|
|
895
|
-
? BigInt(txDetails.effects.gasUsed.computationCost)
|
|
896
|
-
: actualGas;
|
|
897
|
-
return {
|
|
898
|
-
success: true,
|
|
899
|
-
txDigest: result.digest,
|
|
900
|
-
gasUsed,
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
catch (error) {
|
|
904
|
-
return {
|
|
905
|
-
success: false,
|
|
906
|
-
error: error.message || String(error),
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Get Scallop coin name from coin type
|
|
912
|
-
*/
|
|
913
|
-
getScallopCoinName(coinType) {
|
|
914
|
-
const normalized = (0, utils_1.normalizeCoinType)(coinType);
|
|
915
|
-
const COIN_NAME_MAP = {
|
|
916
|
-
"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI": "sui",
|
|
917
|
-
"0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC": "usdc",
|
|
918
|
-
"0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN": "wusdc",
|
|
919
|
-
"0xc060006111016b8a020ad5b33834984a437aaa7d3c74c18e09a95d48aceab08c::coin::COIN": "wusdt",
|
|
920
|
-
};
|
|
921
|
-
return COIN_NAME_MAP[normalized] || normalized.split("::").pop()?.toLowerCase() || "sui";
|
|
569
|
+
return (0, execution_1.executeTransaction)(this.suiClient, this.keypair, this.userAddress, tx);
|
|
922
570
|
}
|
|
923
571
|
}
|
|
924
572
|
exports.DefiDashSDK = DefiDashSDK;
|