@t2000/sdk 0.9.2 → 0.9.5
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 +10 -3
- package/dist/adapters/index.cjs +268 -10
- 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 +268 -10
- package/dist/adapters/index.js.map +1 -1
- package/dist/{index-C7W686z2.d.cts → index-CrLRqDFL.d.cts} +34 -0
- package/dist/{index-C7W686z2.d.ts → index-CrLRqDFL.d.ts} +34 -0
- package/dist/index.cjs +591 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +591 -141
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -232,7 +232,7 @@ Every operation (send, save, borrow, repay, withdraw) routes through a 3-step ga
|
|
|
232
232
|
|
|
233
233
|
Every transaction result includes a `gasMethod` field (`'self-funded'` | `'auto-topup'` | `'sponsored'`) indicating which strategy was used.
|
|
234
234
|
|
|
235
|
-
**Architecture:** Each protocol operation (NAVI, Suilend, Cetus, send) exposes
|
|
235
|
+
**Architecture:** Each protocol operation (NAVI, Suilend, Cetus, send) exposes both `buildXxxTx()` (standalone transaction) and `addXxxToTx()` (composable PTB) functions. Multi-step operations (save with auto-convert, withdraw with auto-swap, rebalance) compose multiple protocol calls into a single atomic PTB. `executeWithGas()` handles execution with the gas fallback chain. If any step within a PTB fails, the entire transaction reverts — no funds left in intermediate states.
|
|
236
236
|
|
|
237
237
|
## Configuration
|
|
238
238
|
|
|
@@ -275,7 +275,7 @@ Common error codes: `INSUFFICIENT_BALANCE` · `INVALID_ADDRESS` · `INVALID_AMOU
|
|
|
275
275
|
## Testing
|
|
276
276
|
|
|
277
277
|
```bash
|
|
278
|
-
# Run all SDK unit tests (
|
|
278
|
+
# Run all SDK unit tests (367 tests)
|
|
279
279
|
pnpm --filter @t2000/sdk test
|
|
280
280
|
```
|
|
281
281
|
|
|
@@ -291,6 +291,13 @@ pnpm --filter @t2000/sdk test
|
|
|
291
291
|
| `send.test.ts` | Send transaction building and validation |
|
|
292
292
|
| `manager.test.ts` | Gas resolution chain (self-fund, auto-topup, sponsored fallback) |
|
|
293
293
|
| `autoTopUp.test.ts` | Auto-topup threshold logic and swap execution |
|
|
294
|
+
| `compliance.test.ts` | Adapter contract compliance (49 checks across all adapters) |
|
|
295
|
+
| `registry.test.ts` | Best rates, multi-protocol routing, quote aggregation |
|
|
296
|
+
| `cetus.test.ts` | Cetus swap adapter (metadata, quotes, transaction building) |
|
|
297
|
+
| `suilend.test.ts` | Suilend adapter (rates, positions, obligation lifecycle) |
|
|
298
|
+
| `t2000.integration.test.ts` | End-to-end flows (save, withdraw, borrow, repay, rebalance, auto-swap) |
|
|
299
|
+
| `protocolFee.test.ts` | Protocol fee calculation and collection |
|
|
300
|
+
| `sentinel.test.ts` | Sentinel attack flow, listing, fee parsing |
|
|
294
301
|
| `serialization.test.ts` | Transaction JSON serialization roundtrip |
|
|
295
302
|
|
|
296
303
|
## Protocol Fees
|
|
@@ -299,7 +306,7 @@ pnpm --filter @t2000/sdk test
|
|
|
299
306
|
|-----------|-----|-------|
|
|
300
307
|
| Save (deposit) | 0.10% | Protocol fee on deposit |
|
|
301
308
|
| Borrow | 0.05% | Protocol fee on loan |
|
|
302
|
-
| Swap | **Free** |
|
|
309
|
+
| Swap (internal) | **Free** | Cetus pool fees only; used internally by rebalance/auto-convert |
|
|
303
310
|
| Withdraw | Free | |
|
|
304
311
|
| Repay | Free | |
|
|
305
312
|
| Send | Free | |
|
package/dist/adapters/index.cjs
CHANGED
|
@@ -392,7 +392,7 @@ function compoundBalance(rawBalance, currentIndex) {
|
|
|
392
392
|
const result = (rawBalance * BigInt(currentIndex) + half) / scale;
|
|
393
393
|
return Number(result) / 10 ** NAVI_BALANCE_DECIMALS;
|
|
394
394
|
}
|
|
395
|
-
async function getUserState(client, address) {
|
|
395
|
+
async function getUserState(client, address, includeZero = false) {
|
|
396
396
|
const config = await getConfig();
|
|
397
397
|
const tx = new transactions.Transaction();
|
|
398
398
|
tx.moveCall({
|
|
@@ -405,11 +405,13 @@ async function getUserState(client, address) {
|
|
|
405
405
|
});
|
|
406
406
|
const decoded = decodeDevInspect(result, bcs.bcs.vector(UserStateInfo));
|
|
407
407
|
if (!decoded) return [];
|
|
408
|
-
|
|
408
|
+
const mapped = decoded.map((s) => ({
|
|
409
409
|
assetId: s.asset_id,
|
|
410
410
|
supplyBalance: toBigInt(s.supply_balance),
|
|
411
411
|
borrowBalance: toBigInt(s.borrow_balance)
|
|
412
|
-
}))
|
|
412
|
+
}));
|
|
413
|
+
if (includeZero) return mapped;
|
|
414
|
+
return mapped.filter((s) => s.supplyBalance !== 0n || s.borrowBalance !== 0n);
|
|
413
415
|
}
|
|
414
416
|
async function fetchCoins(client, owner, coinType) {
|
|
415
417
|
const all = [];
|
|
@@ -466,20 +468,20 @@ async function buildSaveTx(client, address, amount, options = {}) {
|
|
|
466
468
|
async function buildWithdrawTx(client, address, amount, options = {}) {
|
|
467
469
|
const asset = options.asset ?? "USDC";
|
|
468
470
|
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
469
|
-
const [config, pool, pools,
|
|
471
|
+
const [config, pool, pools, allStates] = await Promise.all([
|
|
470
472
|
getConfig(),
|
|
471
473
|
getPool(asset),
|
|
472
474
|
getPools(),
|
|
473
|
-
getUserState(client, address)
|
|
475
|
+
getUserState(client, address, true)
|
|
474
476
|
]);
|
|
475
|
-
const assetState =
|
|
477
|
+
const assetState = allStates.find((s) => s.assetId === pool.id);
|
|
476
478
|
const deposited = assetState ? compoundBalance(assetState.supplyBalance, pool.currentSupplyIndex) : 0;
|
|
477
479
|
const effectiveAmount = Math.min(amount, Math.max(0, deposited - WITHDRAW_DUST_BUFFER));
|
|
478
480
|
if (effectiveAmount <= 0) throw new T2000Error("NO_COLLATERAL", `Nothing to withdraw for ${assetInfo.displayName} on NAVI`);
|
|
479
481
|
const rawAmount = Number(stableToRaw(effectiveAmount, assetInfo.decimals));
|
|
480
482
|
const tx = new transactions.Transaction();
|
|
481
483
|
tx.setSender(address);
|
|
482
|
-
addOracleUpdatesForPositions(tx, config, pools,
|
|
484
|
+
addOracleUpdatesForPositions(tx, config, pools, allStates, pool);
|
|
483
485
|
const [balance] = tx.moveCall({
|
|
484
486
|
target: `${config.package}::incentive_v3::withdraw_v2`,
|
|
485
487
|
arguments: [
|
|
@@ -503,6 +505,94 @@ async function buildWithdrawTx(client, address, amount, options = {}) {
|
|
|
503
505
|
tx.transferObjects([coin], address);
|
|
504
506
|
return { tx, effectiveAmount };
|
|
505
507
|
}
|
|
508
|
+
async function addWithdrawToTx(tx, client, address, amount, options = {}) {
|
|
509
|
+
const asset = options.asset ?? "USDC";
|
|
510
|
+
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
511
|
+
const [config, pool, pools, allStates] = await Promise.all([
|
|
512
|
+
getConfig(),
|
|
513
|
+
getPool(asset),
|
|
514
|
+
getPools(),
|
|
515
|
+
getUserState(client, address, true)
|
|
516
|
+
]);
|
|
517
|
+
const assetState = allStates.find((s) => s.assetId === pool.id);
|
|
518
|
+
const deposited = assetState ? compoundBalance(assetState.supplyBalance, pool.currentSupplyIndex) : 0;
|
|
519
|
+
const effectiveAmount = Math.min(amount, Math.max(0, deposited - WITHDRAW_DUST_BUFFER));
|
|
520
|
+
if (effectiveAmount <= 0) throw new T2000Error("NO_COLLATERAL", `Nothing to withdraw for ${assetInfo.displayName} on NAVI`);
|
|
521
|
+
const rawAmount = Number(stableToRaw(effectiveAmount, assetInfo.decimals));
|
|
522
|
+
addOracleUpdatesForPositions(tx, config, pools, allStates, pool);
|
|
523
|
+
const [balance] = tx.moveCall({
|
|
524
|
+
target: `${config.package}::incentive_v3::withdraw_v2`,
|
|
525
|
+
arguments: [
|
|
526
|
+
tx.object(CLOCK),
|
|
527
|
+
tx.object(config.oracle.priceOracle),
|
|
528
|
+
tx.object(config.storage),
|
|
529
|
+
tx.object(pool.contract.pool),
|
|
530
|
+
tx.pure.u8(pool.id),
|
|
531
|
+
tx.pure.u64(rawAmount),
|
|
532
|
+
tx.object(config.incentiveV2),
|
|
533
|
+
tx.object(config.incentiveV3),
|
|
534
|
+
tx.object(SUI_SYSTEM_STATE)
|
|
535
|
+
],
|
|
536
|
+
typeArguments: [pool.suiCoinType]
|
|
537
|
+
});
|
|
538
|
+
const [coin] = tx.moveCall({
|
|
539
|
+
target: "0x2::coin::from_balance",
|
|
540
|
+
arguments: [balance],
|
|
541
|
+
typeArguments: [pool.suiCoinType]
|
|
542
|
+
});
|
|
543
|
+
return { coin, effectiveAmount };
|
|
544
|
+
}
|
|
545
|
+
async function addSaveToTx(tx, _client, _address, coin, options = {}) {
|
|
546
|
+
const asset = options.asset ?? "USDC";
|
|
547
|
+
const [config, pool] = await Promise.all([getConfig(), getPool(asset)]);
|
|
548
|
+
if (options.collectFee) {
|
|
549
|
+
addCollectFeeToTx(tx, coin, "save");
|
|
550
|
+
}
|
|
551
|
+
const [coinValue] = tx.moveCall({
|
|
552
|
+
target: "0x2::coin::value",
|
|
553
|
+
typeArguments: [pool.suiCoinType],
|
|
554
|
+
arguments: [coin]
|
|
555
|
+
});
|
|
556
|
+
tx.moveCall({
|
|
557
|
+
target: `${config.package}::incentive_v3::entry_deposit`,
|
|
558
|
+
arguments: [
|
|
559
|
+
tx.object(CLOCK),
|
|
560
|
+
tx.object(config.storage),
|
|
561
|
+
tx.object(pool.contract.pool),
|
|
562
|
+
tx.pure.u8(pool.id),
|
|
563
|
+
coin,
|
|
564
|
+
coinValue,
|
|
565
|
+
tx.object(config.incentiveV2),
|
|
566
|
+
tx.object(config.incentiveV3)
|
|
567
|
+
],
|
|
568
|
+
typeArguments: [pool.suiCoinType]
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
async function addRepayToTx(tx, client, _address, coin, options = {}) {
|
|
572
|
+
const asset = options.asset ?? "USDC";
|
|
573
|
+
const [config, pool] = await Promise.all([getConfig(), getPool(asset)]);
|
|
574
|
+
addOracleUpdate(tx, config, pool);
|
|
575
|
+
const [coinValue] = tx.moveCall({
|
|
576
|
+
target: "0x2::coin::value",
|
|
577
|
+
typeArguments: [pool.suiCoinType],
|
|
578
|
+
arguments: [coin]
|
|
579
|
+
});
|
|
580
|
+
tx.moveCall({
|
|
581
|
+
target: `${config.package}::incentive_v3::entry_repay`,
|
|
582
|
+
arguments: [
|
|
583
|
+
tx.object(CLOCK),
|
|
584
|
+
tx.object(config.oracle.priceOracle),
|
|
585
|
+
tx.object(config.storage),
|
|
586
|
+
tx.object(pool.contract.pool),
|
|
587
|
+
tx.pure.u8(pool.id),
|
|
588
|
+
coin,
|
|
589
|
+
coinValue,
|
|
590
|
+
tx.object(config.incentiveV2),
|
|
591
|
+
tx.object(config.incentiveV3)
|
|
592
|
+
],
|
|
593
|
+
typeArguments: [pool.suiCoinType]
|
|
594
|
+
});
|
|
595
|
+
}
|
|
506
596
|
async function buildBorrowTx(client, address, amount, options = {}) {
|
|
507
597
|
if (!amount || amount <= 0 || !Number.isFinite(amount)) {
|
|
508
598
|
throw new T2000Error("INVALID_AMOUNT", "Borrow amount must be a positive number");
|
|
@@ -510,15 +600,15 @@ async function buildBorrowTx(client, address, amount, options = {}) {
|
|
|
510
600
|
const asset = options.asset ?? "USDC";
|
|
511
601
|
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
512
602
|
const rawAmount = Number(stableToRaw(amount, assetInfo.decimals));
|
|
513
|
-
const [config, pool, pools,
|
|
603
|
+
const [config, pool, pools, allStates] = await Promise.all([
|
|
514
604
|
getConfig(),
|
|
515
605
|
getPool(asset),
|
|
516
606
|
getPools(),
|
|
517
|
-
getUserState(client, address)
|
|
607
|
+
getUserState(client, address, true)
|
|
518
608
|
]);
|
|
519
609
|
const tx = new transactions.Transaction();
|
|
520
610
|
tx.setSender(address);
|
|
521
|
-
addOracleUpdatesForPositions(tx, config, pools,
|
|
611
|
+
addOracleUpdatesForPositions(tx, config, pools, allStates, pool);
|
|
522
612
|
const [balance] = tx.moveCall({
|
|
523
613
|
target: `${config.package}::incentive_v3::borrow_v2`,
|
|
524
614
|
arguments: [
|
|
@@ -794,6 +884,18 @@ var NaviAdapter = class {
|
|
|
794
884
|
async maxBorrow(address, _asset) {
|
|
795
885
|
return maxBorrowAmount(this.client, address);
|
|
796
886
|
}
|
|
887
|
+
async addWithdrawToTx(tx, address, amount, asset) {
|
|
888
|
+
const stableAsset = normalizeAsset(asset);
|
|
889
|
+
return addWithdrawToTx(tx, this.client, address, amount, { asset: stableAsset });
|
|
890
|
+
}
|
|
891
|
+
async addSaveToTx(tx, address, coin, asset, options) {
|
|
892
|
+
const stableAsset = normalizeAsset(asset);
|
|
893
|
+
return addSaveToTx(tx, this.client, address, coin, { ...options, asset: stableAsset });
|
|
894
|
+
}
|
|
895
|
+
async addRepayToTx(tx, address, coin, asset) {
|
|
896
|
+
const stableAsset = normalizeAsset(asset);
|
|
897
|
+
return addRepayToTx(tx, this.client, address, coin, { asset: stableAsset });
|
|
898
|
+
}
|
|
797
899
|
};
|
|
798
900
|
var DEFAULT_SLIPPAGE_BPS = 300;
|
|
799
901
|
function createAggregatorClient(client, signer) {
|
|
@@ -838,6 +940,41 @@ async function buildSwapTx(params) {
|
|
|
838
940
|
toDecimals: toInfo.decimals
|
|
839
941
|
};
|
|
840
942
|
}
|
|
943
|
+
async function addSwapToTx(params) {
|
|
944
|
+
const { tx, client, address, inputCoin, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
|
|
945
|
+
const fromInfo = SUPPORTED_ASSETS[fromAsset];
|
|
946
|
+
const toInfo = SUPPORTED_ASSETS[toAsset];
|
|
947
|
+
if (!fromInfo || !toInfo) {
|
|
948
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap pair ${fromAsset}/${toAsset} is not supported`);
|
|
949
|
+
}
|
|
950
|
+
const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
|
|
951
|
+
const aggClient = createAggregatorClient(client, address);
|
|
952
|
+
const result = await aggClient.findRouters({
|
|
953
|
+
from: fromInfo.type,
|
|
954
|
+
target: toInfo.type,
|
|
955
|
+
amount: rawAmount,
|
|
956
|
+
byAmountIn: true
|
|
957
|
+
});
|
|
958
|
+
if (!result || result.insufficientLiquidity) {
|
|
959
|
+
throw new T2000Error(
|
|
960
|
+
"ASSET_NOT_SUPPORTED",
|
|
961
|
+
`No swap route found for ${fromAsset} \u2192 ${toAsset}`
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
const slippage = maxSlippageBps / 1e4;
|
|
965
|
+
const outputCoin = await aggClient.routerSwap({
|
|
966
|
+
router: result,
|
|
967
|
+
txb: tx,
|
|
968
|
+
inputCoin,
|
|
969
|
+
slippage
|
|
970
|
+
});
|
|
971
|
+
const estimatedOut = Number(result.amountOut.toString());
|
|
972
|
+
return {
|
|
973
|
+
outputCoin,
|
|
974
|
+
estimatedOut,
|
|
975
|
+
toDecimals: toInfo.decimals
|
|
976
|
+
};
|
|
977
|
+
}
|
|
841
978
|
async function getPoolPrice(client) {
|
|
842
979
|
try {
|
|
843
980
|
const pool = await client.getObject({
|
|
@@ -948,6 +1085,18 @@ var CetusAdapter = class {
|
|
|
948
1085
|
async getPoolPrice() {
|
|
949
1086
|
return getPoolPrice(this.client);
|
|
950
1087
|
}
|
|
1088
|
+
async addSwapToTx(tx, address, inputCoin, from, to, amount, maxSlippageBps) {
|
|
1089
|
+
return addSwapToTx({
|
|
1090
|
+
tx,
|
|
1091
|
+
client: this.client,
|
|
1092
|
+
address,
|
|
1093
|
+
inputCoin,
|
|
1094
|
+
fromAsset: from,
|
|
1095
|
+
toAsset: to,
|
|
1096
|
+
amount,
|
|
1097
|
+
maxSlippageBps
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
951
1100
|
};
|
|
952
1101
|
var WAD = 1e18;
|
|
953
1102
|
var MIN_HEALTH_FACTOR2 = 1.5;
|
|
@@ -1343,6 +1492,95 @@ var SuilendAdapter = class {
|
|
|
1343
1492
|
tx.transferObjects([coin], address);
|
|
1344
1493
|
return { tx, effectiveAmount };
|
|
1345
1494
|
}
|
|
1495
|
+
async addWithdrawToTx(tx, address, amount, asset) {
|
|
1496
|
+
const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
|
|
1497
|
+
const assetInfo = SUPPORTED_ASSETS[assetKey];
|
|
1498
|
+
const [pkg, reserves] = await Promise.all([this.resolvePackage(), this.loadReserves(true)]);
|
|
1499
|
+
const reserve = this.findReserve(reserves, assetKey);
|
|
1500
|
+
if (!reserve) throw new T2000Error("ASSET_NOT_SUPPORTED", `${assetInfo.displayName} reserve not found on Suilend`);
|
|
1501
|
+
const caps = await this.fetchObligationCaps(address);
|
|
1502
|
+
if (caps.length === 0) throw new T2000Error("NO_COLLATERAL", "No Suilend position found");
|
|
1503
|
+
const positions = await this.getPositions(address);
|
|
1504
|
+
const deposited = positions.supplies.find((s) => s.asset === assetKey)?.amount ?? 0;
|
|
1505
|
+
const effectiveAmount = Math.min(amount, deposited);
|
|
1506
|
+
if (effectiveAmount <= 0) throw new T2000Error("NO_COLLATERAL", `Nothing to withdraw for ${assetInfo.displayName} on Suilend`);
|
|
1507
|
+
const ratio = cTokenRatio(reserve);
|
|
1508
|
+
const ctokenAmount = Math.ceil(effectiveAmount * 10 ** reserve.mintDecimals / ratio);
|
|
1509
|
+
const [ctokens] = tx.moveCall({
|
|
1510
|
+
target: `${pkg}::lending_market::withdraw_ctokens`,
|
|
1511
|
+
typeArguments: [LENDING_MARKET_TYPE, assetInfo.type],
|
|
1512
|
+
arguments: [
|
|
1513
|
+
tx.object(LENDING_MARKET_ID),
|
|
1514
|
+
tx.pure.u64(reserve.arrayIndex),
|
|
1515
|
+
tx.object(caps[0].id),
|
|
1516
|
+
tx.object(CLOCK2),
|
|
1517
|
+
tx.pure.u64(ctokenAmount)
|
|
1518
|
+
]
|
|
1519
|
+
});
|
|
1520
|
+
const exemptionType = `${SUILEND_PACKAGE}::lending_market::RateLimiterExemption<${LENDING_MARKET_TYPE}, ${assetInfo.type}>`;
|
|
1521
|
+
const [none] = tx.moveCall({
|
|
1522
|
+
target: "0x1::option::none",
|
|
1523
|
+
typeArguments: [exemptionType]
|
|
1524
|
+
});
|
|
1525
|
+
const [coin] = tx.moveCall({
|
|
1526
|
+
target: `${pkg}::lending_market::redeem_ctokens_and_withdraw_liquidity`,
|
|
1527
|
+
typeArguments: [LENDING_MARKET_TYPE, assetInfo.type],
|
|
1528
|
+
arguments: [
|
|
1529
|
+
tx.object(LENDING_MARKET_ID),
|
|
1530
|
+
tx.pure.u64(reserve.arrayIndex),
|
|
1531
|
+
tx.object(CLOCK2),
|
|
1532
|
+
ctokens,
|
|
1533
|
+
none
|
|
1534
|
+
]
|
|
1535
|
+
});
|
|
1536
|
+
return { coin, effectiveAmount };
|
|
1537
|
+
}
|
|
1538
|
+
async addSaveToTx(tx, address, coin, asset, options) {
|
|
1539
|
+
const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
|
|
1540
|
+
const assetInfo = SUPPORTED_ASSETS[assetKey];
|
|
1541
|
+
const [pkg, reserves] = await Promise.all([this.resolvePackage(), this.loadReserves()]);
|
|
1542
|
+
const reserve = this.findReserve(reserves, assetKey);
|
|
1543
|
+
if (!reserve) throw new T2000Error("ASSET_NOT_SUPPORTED", `${assetInfo.displayName} reserve not found on Suilend`);
|
|
1544
|
+
const caps = await this.fetchObligationCaps(address);
|
|
1545
|
+
let capRef;
|
|
1546
|
+
if (caps.length === 0) {
|
|
1547
|
+
const [newCap] = tx.moveCall({
|
|
1548
|
+
target: `${pkg}::lending_market::create_obligation`,
|
|
1549
|
+
typeArguments: [LENDING_MARKET_TYPE],
|
|
1550
|
+
arguments: [tx.object(LENDING_MARKET_ID)]
|
|
1551
|
+
});
|
|
1552
|
+
capRef = newCap;
|
|
1553
|
+
} else {
|
|
1554
|
+
capRef = caps[0].id;
|
|
1555
|
+
}
|
|
1556
|
+
if (options?.collectFee) {
|
|
1557
|
+
addCollectFeeToTx(tx, coin, "save");
|
|
1558
|
+
}
|
|
1559
|
+
const [ctokens] = tx.moveCall({
|
|
1560
|
+
target: `${pkg}::lending_market::deposit_liquidity_and_mint_ctokens`,
|
|
1561
|
+
typeArguments: [LENDING_MARKET_TYPE, assetInfo.type],
|
|
1562
|
+
arguments: [
|
|
1563
|
+
tx.object(LENDING_MARKET_ID),
|
|
1564
|
+
tx.pure.u64(reserve.arrayIndex),
|
|
1565
|
+
tx.object(CLOCK2),
|
|
1566
|
+
coin
|
|
1567
|
+
]
|
|
1568
|
+
});
|
|
1569
|
+
tx.moveCall({
|
|
1570
|
+
target: `${pkg}::lending_market::deposit_ctokens_into_obligation`,
|
|
1571
|
+
typeArguments: [LENDING_MARKET_TYPE, assetInfo.type],
|
|
1572
|
+
arguments: [
|
|
1573
|
+
tx.object(LENDING_MARKET_ID),
|
|
1574
|
+
tx.pure.u64(reserve.arrayIndex),
|
|
1575
|
+
typeof capRef === "string" ? tx.object(capRef) : capRef,
|
|
1576
|
+
tx.object(CLOCK2),
|
|
1577
|
+
ctokens
|
|
1578
|
+
]
|
|
1579
|
+
});
|
|
1580
|
+
if (typeof capRef !== "string") {
|
|
1581
|
+
tx.transferObjects([capRef], address);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1346
1584
|
async buildBorrowTx(address, amount, asset, options) {
|
|
1347
1585
|
const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
|
|
1348
1586
|
const assetInfo = SUPPORTED_ASSETS[assetKey];
|
|
@@ -1402,6 +1640,26 @@ var SuilendAdapter = class {
|
|
|
1402
1640
|
});
|
|
1403
1641
|
return { tx };
|
|
1404
1642
|
}
|
|
1643
|
+
async addRepayToTx(tx, address, coin, asset) {
|
|
1644
|
+
const assetKey = asset in SUPPORTED_ASSETS ? asset : "USDC";
|
|
1645
|
+
const assetInfo = SUPPORTED_ASSETS[assetKey];
|
|
1646
|
+
const [pkg, reserves] = await Promise.all([this.resolvePackage(), this.loadReserves()]);
|
|
1647
|
+
const reserve = this.findReserve(reserves, assetKey);
|
|
1648
|
+
if (!reserve) throw new T2000Error("ASSET_NOT_SUPPORTED", `${assetInfo.displayName} reserve not found on Suilend`);
|
|
1649
|
+
const caps = await this.fetchObligationCaps(address);
|
|
1650
|
+
if (caps.length === 0) throw new T2000Error("NO_COLLATERAL", "No Suilend obligation found");
|
|
1651
|
+
tx.moveCall({
|
|
1652
|
+
target: `${pkg}::lending_market::repay`,
|
|
1653
|
+
typeArguments: [LENDING_MARKET_TYPE, assetInfo.type],
|
|
1654
|
+
arguments: [
|
|
1655
|
+
tx.object(LENDING_MARKET_ID),
|
|
1656
|
+
tx.pure.u64(reserve.arrayIndex),
|
|
1657
|
+
tx.object(caps[0].id),
|
|
1658
|
+
tx.object(CLOCK2),
|
|
1659
|
+
coin
|
|
1660
|
+
]
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1405
1663
|
async maxWithdraw(address, _asset) {
|
|
1406
1664
|
const health = await this.getHealth(address);
|
|
1407
1665
|
let maxAmount;
|