@t2000/sdk 0.2.7 → 0.4.0
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 +21 -0
- package/dist/adapters/index.cjs +856 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.cts +3 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.js +851 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/index-DYQv9Wxo.d.cts +393 -0
- package/dist/index-DYQv9Wxo.d.ts +393 -0
- package/dist/index.cjs +773 -192
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -173
- package/dist/index.d.ts +24 -173
- package/dist/index.js +771 -195
- package/dist/index.js.map +1 -1
- package/package.json +26 -13
package/dist/index.cjs
CHANGED
|
@@ -11,8 +11,8 @@ var path = require('path');
|
|
|
11
11
|
var os = require('os');
|
|
12
12
|
var transactions = require('@mysten/sui/transactions');
|
|
13
13
|
var lending = require('@naviprotocol/lending');
|
|
14
|
-
var suiClmmSdk = require('@cetusprotocol/sui-clmm-sdk');
|
|
15
14
|
var bcs = require('@mysten/sui/bcs');
|
|
15
|
+
var aggregatorSdk = require('@cetusprotocol/aggregator-sdk');
|
|
16
16
|
|
|
17
17
|
// src/t2000.ts
|
|
18
18
|
|
|
@@ -40,9 +40,9 @@ var SUPPORTED_ASSETS = {
|
|
|
40
40
|
symbol: "SUI"
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
-
process.env.T2000_PACKAGE_ID ?? "
|
|
44
|
-
process.env.T2000_CONFIG_ID ?? "
|
|
45
|
-
process.env.T2000_TREASURY_ID ?? "
|
|
43
|
+
var T2000_PACKAGE_ID = process.env.T2000_PACKAGE_ID ?? "0xab92e9f1fe549ad3d6a52924a73181b45791e76120b975138fac9ec9b75db9f3";
|
|
44
|
+
var T2000_CONFIG_ID = process.env.T2000_CONFIG_ID ?? "0x408add9aa9322f93cfd87523d8f603006eb8713894f4c460283c58a6888dae8a";
|
|
45
|
+
var T2000_TREASURY_ID = process.env.T2000_TREASURY_ID ?? "0x3bb501b8300125dca59019247941a42af6b292a150ce3cfcce9449456be2ec91";
|
|
46
46
|
var DEFAULT_NETWORK = "mainnet";
|
|
47
47
|
var DEFAULT_RPC_URL = "https://fullnode.mainnet.sui.io:443";
|
|
48
48
|
var DEFAULT_KEY_PATH = "~/.t2000/wallet.key";
|
|
@@ -102,7 +102,8 @@ function mapMoveAbortCode(code) {
|
|
|
102
102
|
6: "Not authorized",
|
|
103
103
|
7: "Package version mismatch \u2014 upgrade required",
|
|
104
104
|
8: "Timelock is active \u2014 wait for expiry",
|
|
105
|
-
9: "No pending change to execute"
|
|
105
|
+
9: "No pending change to execute",
|
|
106
|
+
10: "Already at current version"
|
|
106
107
|
};
|
|
107
108
|
return abortMessages[code] ?? `Move abort code: ${code}`;
|
|
108
109
|
}
|
|
@@ -373,6 +374,61 @@ function inferAction(txBlock) {
|
|
|
373
374
|
if (kind === "ProgrammableTransaction") return "transaction";
|
|
374
375
|
return kind ?? "unknown";
|
|
375
376
|
}
|
|
377
|
+
|
|
378
|
+
// src/protocols/protocolFee.ts
|
|
379
|
+
var FEE_RATES = {
|
|
380
|
+
save: SAVE_FEE_BPS,
|
|
381
|
+
swap: SWAP_FEE_BPS,
|
|
382
|
+
borrow: BORROW_FEE_BPS
|
|
383
|
+
};
|
|
384
|
+
var OP_CODES = {
|
|
385
|
+
save: 0,
|
|
386
|
+
swap: 1,
|
|
387
|
+
borrow: 2
|
|
388
|
+
};
|
|
389
|
+
function calculateFee(operation, amount) {
|
|
390
|
+
const bps = FEE_RATES[operation];
|
|
391
|
+
const feeAmount = amount * Number(bps) / Number(BPS_DENOMINATOR);
|
|
392
|
+
const rawAmount = usdcToRaw(feeAmount);
|
|
393
|
+
return {
|
|
394
|
+
amount: feeAmount,
|
|
395
|
+
asset: "USDC",
|
|
396
|
+
rate: Number(bps) / Number(BPS_DENOMINATOR),
|
|
397
|
+
rawAmount
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function addCollectFeeToTx(tx, paymentCoin, operation) {
|
|
401
|
+
const bps = FEE_RATES[operation];
|
|
402
|
+
if (bps <= 0n) return;
|
|
403
|
+
tx.moveCall({
|
|
404
|
+
target: `${T2000_PACKAGE_ID}::treasury::collect_fee`,
|
|
405
|
+
typeArguments: [SUPPORTED_ASSETS.USDC.type],
|
|
406
|
+
arguments: [
|
|
407
|
+
tx.object(T2000_TREASURY_ID),
|
|
408
|
+
tx.object(T2000_CONFIG_ID),
|
|
409
|
+
paymentCoin,
|
|
410
|
+
tx.pure.u8(OP_CODES[operation])
|
|
411
|
+
]
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
async function reportFee(agentAddress, operation, feeAmount, feeRate, txDigest) {
|
|
415
|
+
try {
|
|
416
|
+
await fetch(`${API_BASE_URL}/api/fees`, {
|
|
417
|
+
method: "POST",
|
|
418
|
+
headers: { "Content-Type": "application/json" },
|
|
419
|
+
body: JSON.stringify({
|
|
420
|
+
agentAddress,
|
|
421
|
+
operation,
|
|
422
|
+
feeAmount: feeAmount.toString(),
|
|
423
|
+
feeRate: feeRate.toString(),
|
|
424
|
+
txDigest
|
|
425
|
+
})
|
|
426
|
+
});
|
|
427
|
+
} catch {
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/protocols/navi.ts
|
|
376
432
|
var ENV = { env: "prod" };
|
|
377
433
|
var USDC_TYPE = SUPPORTED_ASSETS.USDC.type;
|
|
378
434
|
var RATE_DECIMALS = 27;
|
|
@@ -415,7 +471,7 @@ async function updateOracle(tx, client, address) {
|
|
|
415
471
|
} catch {
|
|
416
472
|
}
|
|
417
473
|
}
|
|
418
|
-
async function buildSaveTx(client, address, amount) {
|
|
474
|
+
async function buildSaveTx(client, address, amount, options = {}) {
|
|
419
475
|
const rawAmount = Number(usdcToRaw(amount));
|
|
420
476
|
const coins = await lending.getCoins(address, { coinType: USDC_TYPE, client });
|
|
421
477
|
if (!coins || coins.length === 0) {
|
|
@@ -424,6 +480,9 @@ async function buildSaveTx(client, address, amount) {
|
|
|
424
480
|
const tx = new transactions.Transaction();
|
|
425
481
|
tx.setSender(address);
|
|
426
482
|
const coinObj = lending.mergeCoinsPTB(tx, coins, { balance: rawAmount });
|
|
483
|
+
if (options.collectFee) {
|
|
484
|
+
addCollectFeeToTx(tx, coinObj, "save");
|
|
485
|
+
}
|
|
427
486
|
await lending.depositCoinPTB(tx, USDC_TYPE, coinObj, ENV);
|
|
428
487
|
return tx;
|
|
429
488
|
}
|
|
@@ -441,12 +500,15 @@ async function buildWithdrawTx(client, address, amount) {
|
|
|
441
500
|
tx.transferObjects([withdrawnCoin], address);
|
|
442
501
|
return { tx, effectiveAmount };
|
|
443
502
|
}
|
|
444
|
-
async function buildBorrowTx(client, address, amount) {
|
|
503
|
+
async function buildBorrowTx(client, address, amount, options = {}) {
|
|
445
504
|
const rawAmount = Number(usdcToRaw(amount));
|
|
446
505
|
const tx = new transactions.Transaction();
|
|
447
506
|
tx.setSender(address);
|
|
448
507
|
await updateOracle(tx, client, address);
|
|
449
508
|
const borrowedCoin = await lending.borrowCoinPTB(tx, USDC_TYPE, rawAmount, ENV);
|
|
509
|
+
if (options.collectFee) {
|
|
510
|
+
addCollectFeeToTx(tx, borrowedCoin, "borrow");
|
|
511
|
+
}
|
|
450
512
|
tx.transferObjects([borrowedCoin], address);
|
|
451
513
|
return tx;
|
|
452
514
|
}
|
|
@@ -462,8 +524,8 @@ async function buildRepayTx(client, address, amount) {
|
|
|
462
524
|
await lending.repayCoinPTB(tx, USDC_TYPE, coinObj, { ...ENV, amount: rawAmount });
|
|
463
525
|
return tx;
|
|
464
526
|
}
|
|
465
|
-
async function getHealthFactor(client,
|
|
466
|
-
const address =
|
|
527
|
+
async function getHealthFactor(client, addressOrKeypair) {
|
|
528
|
+
const address = typeof addressOrKeypair === "string" ? addressOrKeypair : addressOrKeypair.getPublicKey().toSuiAddress();
|
|
467
529
|
const [healthFactor, state, pool] = await Promise.all([
|
|
468
530
|
lending.getHealthFactor(address, clientOpt(client, true)),
|
|
469
531
|
lending.getLendingState(address, clientOpt(client, true)),
|
|
@@ -495,8 +557,8 @@ async function getRates(client) {
|
|
|
495
557
|
return { USDC: { saveApy: 4, borrowApy: 6 } };
|
|
496
558
|
}
|
|
497
559
|
}
|
|
498
|
-
async function getPositions(client,
|
|
499
|
-
const address =
|
|
560
|
+
async function getPositions(client, addressOrKeypair) {
|
|
561
|
+
const address = typeof addressOrKeypair === "string" ? addressOrKeypair : addressOrKeypair.getPublicKey().toSuiAddress();
|
|
500
562
|
const state = await lending.getLendingState(address, clientOpt(client, true));
|
|
501
563
|
const positions = [];
|
|
502
564
|
for (const pos of state) {
|
|
@@ -524,8 +586,8 @@ async function getPositions(client, keypair) {
|
|
|
524
586
|
}
|
|
525
587
|
return { positions };
|
|
526
588
|
}
|
|
527
|
-
async function maxWithdrawAmount(client,
|
|
528
|
-
const hf = await getHealthFactor(client,
|
|
589
|
+
async function maxWithdrawAmount(client, addressOrKeypair) {
|
|
590
|
+
const hf = await getHealthFactor(client, addressOrKeypair);
|
|
529
591
|
const ltv = hf.liquidationThreshold > 0 ? hf.liquidationThreshold : 0.75;
|
|
530
592
|
let maxAmount;
|
|
531
593
|
if (hf.borrowed === 0) {
|
|
@@ -541,8 +603,8 @@ async function maxWithdrawAmount(client, keypair) {
|
|
|
541
603
|
currentHF: hf.healthFactor
|
|
542
604
|
};
|
|
543
605
|
}
|
|
544
|
-
async function maxBorrowAmount(client,
|
|
545
|
-
const hf = await getHealthFactor(client,
|
|
606
|
+
async function maxBorrowAmount(client, addressOrKeypair) {
|
|
607
|
+
const hf = await getHealthFactor(client, addressOrKeypair);
|
|
546
608
|
const ltv = hf.liquidationThreshold > 0 ? hf.liquidationThreshold : 0.75;
|
|
547
609
|
const maxAmount = Math.max(0, hf.supplied * ltv / MIN_HEALTH_FACTOR - hf.borrowed);
|
|
548
610
|
return {
|
|
@@ -551,142 +613,6 @@ async function maxBorrowAmount(client, keypair) {
|
|
|
551
613
|
currentHF: hf.healthFactor
|
|
552
614
|
};
|
|
553
615
|
}
|
|
554
|
-
var DEFAULT_SLIPPAGE_BPS = 300;
|
|
555
|
-
function isA2B(from) {
|
|
556
|
-
return from === "USDC";
|
|
557
|
-
}
|
|
558
|
-
var _cetusSDK = null;
|
|
559
|
-
function getCetusSDK() {
|
|
560
|
-
if (!_cetusSDK) {
|
|
561
|
-
_cetusSDK = suiClmmSdk.CetusClmmSDK.createSDK({ env: "mainnet" });
|
|
562
|
-
}
|
|
563
|
-
return _cetusSDK;
|
|
564
|
-
}
|
|
565
|
-
async function buildSwapTx(params) {
|
|
566
|
-
const { client, address, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
|
|
567
|
-
const a2b = isA2B(fromAsset);
|
|
568
|
-
const fromInfo = SUPPORTED_ASSETS[fromAsset];
|
|
569
|
-
const toInfo = SUPPORTED_ASSETS[toAsset];
|
|
570
|
-
const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
|
|
571
|
-
const sdk = getCetusSDK();
|
|
572
|
-
sdk.setSenderAddress(address);
|
|
573
|
-
const pool = await sdk.Pool.getPool(CETUS_USDC_SUI_POOL);
|
|
574
|
-
const preSwapResult = await sdk.Swap.preSwap({
|
|
575
|
-
pool,
|
|
576
|
-
current_sqrt_price: pool.current_sqrt_price,
|
|
577
|
-
coin_type_a: pool.coin_type_a,
|
|
578
|
-
coin_type_b: pool.coin_type_b,
|
|
579
|
-
decimals_a: 6,
|
|
580
|
-
decimals_b: 9,
|
|
581
|
-
a2b,
|
|
582
|
-
by_amount_in: true,
|
|
583
|
-
amount: rawAmount.toString()
|
|
584
|
-
});
|
|
585
|
-
const estimatedOut = Number(preSwapResult.estimated_amount_out);
|
|
586
|
-
const slippageFactor = (1e4 - maxSlippageBps) / 1e4;
|
|
587
|
-
const amountLimit = Math.floor(estimatedOut * slippageFactor);
|
|
588
|
-
const swapPayload = await sdk.Swap.createSwapPayload({
|
|
589
|
-
pool_id: pool.id,
|
|
590
|
-
coin_type_a: pool.coin_type_a,
|
|
591
|
-
coin_type_b: pool.coin_type_b,
|
|
592
|
-
a2b,
|
|
593
|
-
by_amount_in: true,
|
|
594
|
-
amount: preSwapResult.amount.toString(),
|
|
595
|
-
amount_limit: amountLimit.toString()
|
|
596
|
-
});
|
|
597
|
-
return {
|
|
598
|
-
tx: swapPayload,
|
|
599
|
-
estimatedOut,
|
|
600
|
-
toDecimals: toInfo.decimals
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
async function getPoolPrice(client) {
|
|
604
|
-
try {
|
|
605
|
-
const pool = await client.getObject({
|
|
606
|
-
id: CETUS_USDC_SUI_POOL,
|
|
607
|
-
options: { showContent: true }
|
|
608
|
-
});
|
|
609
|
-
if (pool.data?.content?.dataType === "moveObject") {
|
|
610
|
-
const fields = pool.data.content.fields;
|
|
611
|
-
const currentSqrtPrice = BigInt(String(fields.current_sqrt_price ?? "0"));
|
|
612
|
-
if (currentSqrtPrice > 0n) {
|
|
613
|
-
const Q64 = 2n ** 64n;
|
|
614
|
-
const sqrtPriceFloat = Number(currentSqrtPrice) / Number(Q64);
|
|
615
|
-
const rawPrice = sqrtPriceFloat * sqrtPriceFloat;
|
|
616
|
-
const suiPriceUsd = 1e3 / rawPrice;
|
|
617
|
-
if (suiPriceUsd > 0.01 && suiPriceUsd < 1e3) return suiPriceUsd;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
} catch {
|
|
621
|
-
}
|
|
622
|
-
return 3.5;
|
|
623
|
-
}
|
|
624
|
-
async function getSwapQuote(client, fromAsset, toAsset, amount) {
|
|
625
|
-
const a2b = isA2B(fromAsset);
|
|
626
|
-
const fromInfo = SUPPORTED_ASSETS[fromAsset];
|
|
627
|
-
const toInfo = SUPPORTED_ASSETS[toAsset];
|
|
628
|
-
const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
|
|
629
|
-
const poolPrice = await getPoolPrice(client);
|
|
630
|
-
try {
|
|
631
|
-
const sdk = getCetusSDK();
|
|
632
|
-
const pool = await sdk.Pool.getPool(CETUS_USDC_SUI_POOL);
|
|
633
|
-
const preSwapResult = await sdk.Swap.preSwap({
|
|
634
|
-
pool,
|
|
635
|
-
current_sqrt_price: pool.current_sqrt_price,
|
|
636
|
-
coin_type_a: pool.coin_type_a,
|
|
637
|
-
coin_type_b: pool.coin_type_b,
|
|
638
|
-
decimals_a: 6,
|
|
639
|
-
decimals_b: 9,
|
|
640
|
-
a2b,
|
|
641
|
-
by_amount_in: true,
|
|
642
|
-
amount: rawAmount.toString()
|
|
643
|
-
});
|
|
644
|
-
const expectedOutput = Number(preSwapResult.estimated_amount_out) / 10 ** toInfo.decimals;
|
|
645
|
-
return { expectedOutput, priceImpact: 0, poolPrice };
|
|
646
|
-
} catch {
|
|
647
|
-
let expectedOutput;
|
|
648
|
-
if (fromAsset === "USDC") {
|
|
649
|
-
expectedOutput = amount / poolPrice;
|
|
650
|
-
} else {
|
|
651
|
-
expectedOutput = amount * poolPrice;
|
|
652
|
-
}
|
|
653
|
-
return { expectedOutput, priceImpact: 0, poolPrice };
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// src/protocols/protocolFee.ts
|
|
658
|
-
var FEE_RATES = {
|
|
659
|
-
save: SAVE_FEE_BPS,
|
|
660
|
-
swap: SWAP_FEE_BPS,
|
|
661
|
-
borrow: BORROW_FEE_BPS
|
|
662
|
-
};
|
|
663
|
-
function calculateFee(operation, amount) {
|
|
664
|
-
const bps = FEE_RATES[operation];
|
|
665
|
-
const feeAmount = amount * Number(bps) / Number(BPS_DENOMINATOR);
|
|
666
|
-
const rawAmount = usdcToRaw(feeAmount);
|
|
667
|
-
return {
|
|
668
|
-
amount: feeAmount,
|
|
669
|
-
asset: "USDC",
|
|
670
|
-
rate: Number(bps) / Number(BPS_DENOMINATOR),
|
|
671
|
-
rawAmount
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
async function reportFee(agentAddress, operation, feeAmount, feeRate, txDigest) {
|
|
675
|
-
try {
|
|
676
|
-
await fetch(`${API_BASE_URL}/api/fees`, {
|
|
677
|
-
method: "POST",
|
|
678
|
-
headers: { "Content-Type": "application/json" },
|
|
679
|
-
body: JSON.stringify({
|
|
680
|
-
agentAddress,
|
|
681
|
-
operation,
|
|
682
|
-
feeAmount: feeAmount.toString(),
|
|
683
|
-
feeRate: feeRate.toString(),
|
|
684
|
-
txDigest
|
|
685
|
-
})
|
|
686
|
-
});
|
|
687
|
-
} catch {
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
616
|
|
|
691
617
|
// src/protocols/yieldTracker.ts
|
|
692
618
|
async function getEarnings(client, keypair) {
|
|
@@ -898,6 +824,298 @@ async function attack(client, signer, sentinelId, prompt, feeMist) {
|
|
|
898
824
|
feePaid: Number(fee) / Number(MIST_PER_SUI)
|
|
899
825
|
};
|
|
900
826
|
}
|
|
827
|
+
|
|
828
|
+
// src/adapters/registry.ts
|
|
829
|
+
var ProtocolRegistry = class {
|
|
830
|
+
lending = /* @__PURE__ */ new Map();
|
|
831
|
+
swap = /* @__PURE__ */ new Map();
|
|
832
|
+
registerLending(adapter) {
|
|
833
|
+
this.lending.set(adapter.id, adapter);
|
|
834
|
+
}
|
|
835
|
+
registerSwap(adapter) {
|
|
836
|
+
this.swap.set(adapter.id, adapter);
|
|
837
|
+
}
|
|
838
|
+
async bestSaveRate(asset) {
|
|
839
|
+
const candidates = [];
|
|
840
|
+
for (const adapter of this.lending.values()) {
|
|
841
|
+
if (!adapter.supportedAssets.includes(asset)) continue;
|
|
842
|
+
if (!adapter.capabilities.includes("save")) continue;
|
|
843
|
+
try {
|
|
844
|
+
const rate = await adapter.getRates(asset);
|
|
845
|
+
candidates.push({ adapter, rate });
|
|
846
|
+
} catch {
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (candidates.length === 0) {
|
|
850
|
+
throw new Error(`No lending adapter supports saving ${asset}`);
|
|
851
|
+
}
|
|
852
|
+
candidates.sort((a, b) => b.rate.saveApy - a.rate.saveApy);
|
|
853
|
+
return candidates[0];
|
|
854
|
+
}
|
|
855
|
+
async bestBorrowRate(asset, opts) {
|
|
856
|
+
const candidates = [];
|
|
857
|
+
for (const adapter of this.lending.values()) {
|
|
858
|
+
if (!adapter.supportedAssets.includes(asset)) continue;
|
|
859
|
+
if (!adapter.capabilities.includes("borrow")) continue;
|
|
860
|
+
if (opts?.requireSameAssetBorrow && !adapter.supportsSameAssetBorrow) continue;
|
|
861
|
+
try {
|
|
862
|
+
const rate = await adapter.getRates(asset);
|
|
863
|
+
candidates.push({ adapter, rate });
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (candidates.length === 0) {
|
|
868
|
+
throw new Error(`No lending adapter supports borrowing ${asset}`);
|
|
869
|
+
}
|
|
870
|
+
candidates.sort((a, b) => a.rate.borrowApy - b.rate.borrowApy);
|
|
871
|
+
return candidates[0];
|
|
872
|
+
}
|
|
873
|
+
async bestSwapQuote(from, to, amount) {
|
|
874
|
+
const candidates = [];
|
|
875
|
+
for (const adapter of this.swap.values()) {
|
|
876
|
+
const pairs = adapter.getSupportedPairs();
|
|
877
|
+
if (!pairs.some((p) => p.from === from && p.to === to)) continue;
|
|
878
|
+
try {
|
|
879
|
+
const quote = await adapter.getQuote(from, to, amount);
|
|
880
|
+
candidates.push({ adapter, quote });
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (candidates.length === 0) {
|
|
885
|
+
throw new Error(`No swap adapter supports ${from} \u2192 ${to}`);
|
|
886
|
+
}
|
|
887
|
+
candidates.sort((a, b) => b.quote.expectedOutput - a.quote.expectedOutput);
|
|
888
|
+
return candidates[0];
|
|
889
|
+
}
|
|
890
|
+
async allRates(asset) {
|
|
891
|
+
const results = [];
|
|
892
|
+
for (const adapter of this.lending.values()) {
|
|
893
|
+
if (!adapter.supportedAssets.includes(asset)) continue;
|
|
894
|
+
try {
|
|
895
|
+
const rates = await adapter.getRates(asset);
|
|
896
|
+
results.push({ protocol: adapter.name, protocolId: adapter.id, rates });
|
|
897
|
+
} catch {
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return results;
|
|
901
|
+
}
|
|
902
|
+
async allPositions(address) {
|
|
903
|
+
const results = [];
|
|
904
|
+
for (const adapter of this.lending.values()) {
|
|
905
|
+
try {
|
|
906
|
+
const positions = await adapter.getPositions(address);
|
|
907
|
+
if (positions.supplies.length > 0 || positions.borrows.length > 0) {
|
|
908
|
+
results.push({ protocol: adapter.name, protocolId: adapter.id, positions });
|
|
909
|
+
}
|
|
910
|
+
} catch {
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return results;
|
|
914
|
+
}
|
|
915
|
+
getLending(id) {
|
|
916
|
+
return this.lending.get(id);
|
|
917
|
+
}
|
|
918
|
+
getSwap(id) {
|
|
919
|
+
return this.swap.get(id);
|
|
920
|
+
}
|
|
921
|
+
listLending() {
|
|
922
|
+
return [...this.lending.values()];
|
|
923
|
+
}
|
|
924
|
+
listSwap() {
|
|
925
|
+
return [...this.swap.values()];
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
// src/adapters/navi.ts
|
|
930
|
+
var NaviAdapter = class {
|
|
931
|
+
id = "navi";
|
|
932
|
+
name = "NAVI Protocol";
|
|
933
|
+
version = "1.0.0";
|
|
934
|
+
capabilities = ["save", "withdraw", "borrow", "repay"];
|
|
935
|
+
supportedAssets = ["USDC"];
|
|
936
|
+
supportsSameAssetBorrow = true;
|
|
937
|
+
client;
|
|
938
|
+
async init(client) {
|
|
939
|
+
this.client = client;
|
|
940
|
+
}
|
|
941
|
+
initSync(client) {
|
|
942
|
+
this.client = client;
|
|
943
|
+
}
|
|
944
|
+
async getRates(asset) {
|
|
945
|
+
const rates = await getRates(this.client);
|
|
946
|
+
const key = asset.toUpperCase();
|
|
947
|
+
const r = rates[key];
|
|
948
|
+
if (!r) throw new Error(`NAVI does not support ${asset}`);
|
|
949
|
+
return { asset, saveApy: r.saveApy, borrowApy: r.borrowApy };
|
|
950
|
+
}
|
|
951
|
+
async getPositions(address) {
|
|
952
|
+
const result = await getPositions(this.client, address);
|
|
953
|
+
return {
|
|
954
|
+
supplies: result.positions.filter((p) => p.type === "save").map((p) => ({ asset: p.asset, amount: p.amount, apy: p.apy })),
|
|
955
|
+
borrows: result.positions.filter((p) => p.type === "borrow").map((p) => ({ asset: p.asset, amount: p.amount, apy: p.apy }))
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
async getHealth(address) {
|
|
959
|
+
return getHealthFactor(this.client, address);
|
|
960
|
+
}
|
|
961
|
+
async buildSaveTx(address, amount, _asset, options) {
|
|
962
|
+
const tx = await buildSaveTx(this.client, address, amount, options);
|
|
963
|
+
return { tx };
|
|
964
|
+
}
|
|
965
|
+
async buildWithdrawTx(address, amount, _asset) {
|
|
966
|
+
const result = await buildWithdrawTx(this.client, address, amount);
|
|
967
|
+
return { tx: result.tx, effectiveAmount: result.effectiveAmount };
|
|
968
|
+
}
|
|
969
|
+
async buildBorrowTx(address, amount, _asset, options) {
|
|
970
|
+
const tx = await buildBorrowTx(this.client, address, amount, options);
|
|
971
|
+
return { tx };
|
|
972
|
+
}
|
|
973
|
+
async buildRepayTx(address, amount, _asset) {
|
|
974
|
+
const tx = await buildRepayTx(this.client, address, amount);
|
|
975
|
+
return { tx };
|
|
976
|
+
}
|
|
977
|
+
async maxWithdraw(address, _asset) {
|
|
978
|
+
return maxWithdrawAmount(this.client, address);
|
|
979
|
+
}
|
|
980
|
+
async maxBorrow(address, _asset) {
|
|
981
|
+
return maxBorrowAmount(this.client, address);
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
var DEFAULT_SLIPPAGE_BPS = 300;
|
|
985
|
+
function createAggregatorClient(client, signer) {
|
|
986
|
+
return new aggregatorSdk.AggregatorClient({
|
|
987
|
+
client,
|
|
988
|
+
signer,
|
|
989
|
+
env: aggregatorSdk.Env.Mainnet
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
async function buildSwapTx(params) {
|
|
993
|
+
const { client, address, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
|
|
994
|
+
const fromInfo = SUPPORTED_ASSETS[fromAsset];
|
|
995
|
+
const toInfo = SUPPORTED_ASSETS[toAsset];
|
|
996
|
+
const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
|
|
997
|
+
const aggClient = createAggregatorClient(client, address);
|
|
998
|
+
const result = await aggClient.findRouters({
|
|
999
|
+
from: fromInfo.type,
|
|
1000
|
+
target: toInfo.type,
|
|
1001
|
+
amount: rawAmount,
|
|
1002
|
+
byAmountIn: true
|
|
1003
|
+
});
|
|
1004
|
+
if (!result || result.insufficientLiquidity) {
|
|
1005
|
+
throw new T2000Error(
|
|
1006
|
+
"ASSET_NOT_SUPPORTED",
|
|
1007
|
+
`No swap route found for ${fromAsset} \u2192 ${toAsset}`
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
const tx = new transactions.Transaction();
|
|
1011
|
+
const slippage = maxSlippageBps / 1e4;
|
|
1012
|
+
await aggClient.fastRouterSwap({
|
|
1013
|
+
router: result,
|
|
1014
|
+
txb: tx,
|
|
1015
|
+
slippage
|
|
1016
|
+
});
|
|
1017
|
+
const estimatedOut = Number(result.amountOut.toString());
|
|
1018
|
+
return {
|
|
1019
|
+
tx,
|
|
1020
|
+
estimatedOut,
|
|
1021
|
+
toDecimals: toInfo.decimals
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
async function getPoolPrice(client) {
|
|
1025
|
+
try {
|
|
1026
|
+
const pool = await client.getObject({
|
|
1027
|
+
id: CETUS_USDC_SUI_POOL,
|
|
1028
|
+
options: { showContent: true }
|
|
1029
|
+
});
|
|
1030
|
+
if (pool.data?.content?.dataType === "moveObject") {
|
|
1031
|
+
const fields = pool.data.content.fields;
|
|
1032
|
+
const currentSqrtPrice = BigInt(String(fields.current_sqrt_price ?? "0"));
|
|
1033
|
+
if (currentSqrtPrice > 0n) {
|
|
1034
|
+
const Q64 = 2n ** 64n;
|
|
1035
|
+
const sqrtPriceFloat = Number(currentSqrtPrice) / Number(Q64);
|
|
1036
|
+
const rawPrice = sqrtPriceFloat * sqrtPriceFloat;
|
|
1037
|
+
const suiPriceUsd = 1e3 / rawPrice;
|
|
1038
|
+
if (suiPriceUsd > 0.01 && suiPriceUsd < 1e3) return suiPriceUsd;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
} catch {
|
|
1042
|
+
}
|
|
1043
|
+
return 3.5;
|
|
1044
|
+
}
|
|
1045
|
+
async function getSwapQuote(client, fromAsset, toAsset, amount) {
|
|
1046
|
+
const fromInfo = SUPPORTED_ASSETS[fromAsset];
|
|
1047
|
+
const toInfo = SUPPORTED_ASSETS[toAsset];
|
|
1048
|
+
const rawAmount = BigInt(Math.floor(amount * 10 ** fromInfo.decimals));
|
|
1049
|
+
const poolPrice = await getPoolPrice(client);
|
|
1050
|
+
try {
|
|
1051
|
+
const aggClient = createAggregatorClient(client);
|
|
1052
|
+
const result = await aggClient.findRouters({
|
|
1053
|
+
from: fromInfo.type,
|
|
1054
|
+
target: toInfo.type,
|
|
1055
|
+
amount: rawAmount,
|
|
1056
|
+
byAmountIn: true
|
|
1057
|
+
});
|
|
1058
|
+
if (!result || result.insufficientLiquidity) {
|
|
1059
|
+
return fallbackQuote(fromAsset, amount, poolPrice);
|
|
1060
|
+
}
|
|
1061
|
+
const expectedOutput = Number(result.amountOut.toString()) / 10 ** toInfo.decimals;
|
|
1062
|
+
const priceImpact = result.deviationRatio ?? 0;
|
|
1063
|
+
return { expectedOutput, priceImpact, poolPrice };
|
|
1064
|
+
} catch {
|
|
1065
|
+
return fallbackQuote(fromAsset, amount, poolPrice);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
function fallbackQuote(fromAsset, amount, poolPrice) {
|
|
1069
|
+
const expectedOutput = fromAsset === "USDC" ? amount / poolPrice : amount * poolPrice;
|
|
1070
|
+
return { expectedOutput, priceImpact: 0, poolPrice };
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// src/adapters/cetus.ts
|
|
1074
|
+
var CetusAdapter = class {
|
|
1075
|
+
id = "cetus";
|
|
1076
|
+
name = "Cetus";
|
|
1077
|
+
version = "1.0.0";
|
|
1078
|
+
capabilities = ["swap"];
|
|
1079
|
+
client;
|
|
1080
|
+
async init(client) {
|
|
1081
|
+
this.client = client;
|
|
1082
|
+
}
|
|
1083
|
+
initSync(client) {
|
|
1084
|
+
this.client = client;
|
|
1085
|
+
}
|
|
1086
|
+
async getQuote(from, to, amount) {
|
|
1087
|
+
return getSwapQuote(
|
|
1088
|
+
this.client,
|
|
1089
|
+
from.toUpperCase(),
|
|
1090
|
+
to.toUpperCase(),
|
|
1091
|
+
amount
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
async buildSwapTx(address, from, to, amount, maxSlippageBps) {
|
|
1095
|
+
const result = await buildSwapTx({
|
|
1096
|
+
client: this.client,
|
|
1097
|
+
address,
|
|
1098
|
+
fromAsset: from.toUpperCase(),
|
|
1099
|
+
toAsset: to.toUpperCase(),
|
|
1100
|
+
amount,
|
|
1101
|
+
maxSlippageBps
|
|
1102
|
+
});
|
|
1103
|
+
return {
|
|
1104
|
+
tx: result.tx,
|
|
1105
|
+
estimatedOut: result.estimatedOut,
|
|
1106
|
+
toDecimals: result.toDecimals
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
getSupportedPairs() {
|
|
1110
|
+
return [
|
|
1111
|
+
{ from: "USDC", to: "SUI" },
|
|
1112
|
+
{ from: "SUI", to: "USDC" }
|
|
1113
|
+
];
|
|
1114
|
+
}
|
|
1115
|
+
async getPoolPrice() {
|
|
1116
|
+
return getPoolPrice(this.client);
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
901
1119
|
function hasLeadingZeroBits(hash, bits) {
|
|
902
1120
|
const fullBytes = Math.floor(bits / 8);
|
|
903
1121
|
const remainingBits = bits % 8;
|
|
@@ -1130,11 +1348,23 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1130
1348
|
keypair;
|
|
1131
1349
|
client;
|
|
1132
1350
|
_address;
|
|
1133
|
-
|
|
1351
|
+
registry;
|
|
1352
|
+
constructor(keypair, client, registry) {
|
|
1134
1353
|
super();
|
|
1135
1354
|
this.keypair = keypair;
|
|
1136
1355
|
this.client = client;
|
|
1137
1356
|
this._address = getAddress(keypair);
|
|
1357
|
+
this.registry = registry ?? _T2000.createDefaultRegistry(client);
|
|
1358
|
+
}
|
|
1359
|
+
static createDefaultRegistry(client) {
|
|
1360
|
+
const registry = new ProtocolRegistry();
|
|
1361
|
+
const naviAdapter = new NaviAdapter();
|
|
1362
|
+
naviAdapter.initSync(client);
|
|
1363
|
+
registry.registerLending(naviAdapter);
|
|
1364
|
+
const cetusAdapter = new CetusAdapter();
|
|
1365
|
+
cetusAdapter.initSync(client);
|
|
1366
|
+
registry.registerSwap(cetusAdapter);
|
|
1367
|
+
return registry;
|
|
1138
1368
|
}
|
|
1139
1369
|
static async create(options = {}) {
|
|
1140
1370
|
const { keyPath, pin, passphrase, network = DEFAULT_NETWORK, rpcUrl, sponsored, name } = options;
|
|
@@ -1255,6 +1485,11 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1255
1485
|
exportKey() {
|
|
1256
1486
|
return exportPrivateKey(this.keypair);
|
|
1257
1487
|
}
|
|
1488
|
+
async registerAdapter(adapter) {
|
|
1489
|
+
await adapter.init(this.client);
|
|
1490
|
+
if ("buildSaveTx" in adapter) this.registry.registerLending(adapter);
|
|
1491
|
+
if ("buildSwapTx" in adapter) this.registry.registerSwap(adapter);
|
|
1492
|
+
}
|
|
1258
1493
|
// -- Savings --
|
|
1259
1494
|
async save(params) {
|
|
1260
1495
|
const asset = (params.asset ?? "USDC").toUpperCase();
|
|
@@ -1277,15 +1512,15 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1277
1512
|
}
|
|
1278
1513
|
const fee = calculateFee("save", amount);
|
|
1279
1514
|
const saveAmount = amount;
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1282
|
-
this.
|
|
1283
|
-
|
|
1284
|
-
);
|
|
1285
|
-
const rates = await getRates(
|
|
1515
|
+
const adapter = await this.resolveLending(params.protocol, asset, "save");
|
|
1516
|
+
const gasResult = await executeWithGas(this.client, this.keypair, async () => {
|
|
1517
|
+
const { tx } = await adapter.buildSaveTx(this._address, saveAmount, asset, { collectFee: true });
|
|
1518
|
+
return tx;
|
|
1519
|
+
});
|
|
1520
|
+
const rates = await adapter.getRates(asset);
|
|
1286
1521
|
reportFee(this._address, "save", fee.amount, fee.rate, gasResult.digest);
|
|
1287
1522
|
this.emitBalanceChange("USDC", saveAmount, "save", gasResult.digest);
|
|
1288
|
-
let savingsBalance = saveAmount
|
|
1523
|
+
let savingsBalance = saveAmount;
|
|
1289
1524
|
try {
|
|
1290
1525
|
const positions = await this.positions();
|
|
1291
1526
|
savingsBalance = positions.positions.filter((p) => p.type === "save").reduce((sum, p) => sum + p.amount, 0);
|
|
@@ -1295,7 +1530,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1295
1530
|
success: true,
|
|
1296
1531
|
tx: gasResult.digest,
|
|
1297
1532
|
amount: saveAmount,
|
|
1298
|
-
apy: rates.
|
|
1533
|
+
apy: rates.saveApy,
|
|
1299
1534
|
fee: fee.amount,
|
|
1300
1535
|
gasCost: gasResult.gasCostSui,
|
|
1301
1536
|
gasMethod: gasResult.gasMethod,
|
|
@@ -1307,18 +1542,19 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1307
1542
|
if (asset !== "USDC") {
|
|
1308
1543
|
throw new T2000Error("ASSET_NOT_SUPPORTED", `Only USDC is supported for withdraw. Got: ${asset}`);
|
|
1309
1544
|
}
|
|
1545
|
+
const adapter = await this.resolveLending(params.protocol, asset, "withdraw");
|
|
1310
1546
|
let amount;
|
|
1311
1547
|
if (params.amount === "all") {
|
|
1312
|
-
const maxResult = await
|
|
1548
|
+
const maxResult = await adapter.maxWithdraw(this._address, asset);
|
|
1313
1549
|
amount = maxResult.maxAmount;
|
|
1314
1550
|
if (amount <= 0) {
|
|
1315
1551
|
throw new T2000Error("NO_COLLATERAL", "No savings to withdraw");
|
|
1316
1552
|
}
|
|
1317
1553
|
} else {
|
|
1318
1554
|
amount = params.amount;
|
|
1319
|
-
const hf = await this.
|
|
1555
|
+
const hf = await adapter.getHealth(this._address);
|
|
1320
1556
|
if (hf.borrowed > 0) {
|
|
1321
|
-
const maxResult = await
|
|
1557
|
+
const maxResult = await adapter.maxWithdraw(this._address, asset);
|
|
1322
1558
|
if (amount > maxResult.maxAmount) {
|
|
1323
1559
|
throw new T2000Error(
|
|
1324
1560
|
"WITHDRAW_WOULD_LIQUIDATE",
|
|
@@ -1335,7 +1571,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1335
1571
|
const withdrawAmount = amount;
|
|
1336
1572
|
let effectiveAmount = withdrawAmount;
|
|
1337
1573
|
const gasResult = await executeWithGas(this.client, this.keypair, async () => {
|
|
1338
|
-
const built = await buildWithdrawTx(this.
|
|
1574
|
+
const built = await adapter.buildWithdrawTx(this._address, withdrawAmount, asset);
|
|
1339
1575
|
effectiveAmount = built.effectiveAmount;
|
|
1340
1576
|
return built.tx;
|
|
1341
1577
|
});
|
|
@@ -1349,7 +1585,8 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1349
1585
|
};
|
|
1350
1586
|
}
|
|
1351
1587
|
async maxWithdraw() {
|
|
1352
|
-
|
|
1588
|
+
const adapter = await this.resolveLending(void 0, "USDC", "withdraw");
|
|
1589
|
+
return adapter.maxWithdraw(this._address, "USDC");
|
|
1353
1590
|
}
|
|
1354
1591
|
// -- Borrowing --
|
|
1355
1592
|
async borrow(params) {
|
|
@@ -1357,7 +1594,8 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1357
1594
|
if (asset !== "USDC") {
|
|
1358
1595
|
throw new T2000Error("ASSET_NOT_SUPPORTED", `Only USDC is supported for borrow. Got: ${asset}`);
|
|
1359
1596
|
}
|
|
1360
|
-
const
|
|
1597
|
+
const adapter = await this.resolveLending(params.protocol, asset, "borrow");
|
|
1598
|
+
const maxResult = await adapter.maxBorrow(this._address, asset);
|
|
1361
1599
|
if (params.amount > maxResult.maxAmount) {
|
|
1362
1600
|
throw new T2000Error("HEALTH_FACTOR_TOO_LOW", `Max safe borrow: $${maxResult.maxAmount.toFixed(2)}`, {
|
|
1363
1601
|
maxBorrow: maxResult.maxAmount,
|
|
@@ -1366,12 +1604,11 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1366
1604
|
}
|
|
1367
1605
|
const fee = calculateFee("borrow", params.amount);
|
|
1368
1606
|
const borrowAmount = params.amount;
|
|
1369
|
-
const gasResult = await executeWithGas(
|
|
1370
|
-
this.
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
);
|
|
1374
|
-
const hf = await getHealthFactor(this.client, this.keypair);
|
|
1607
|
+
const gasResult = await executeWithGas(this.client, this.keypair, async () => {
|
|
1608
|
+
const { tx } = await adapter.buildBorrowTx(this._address, borrowAmount, asset, { collectFee: true });
|
|
1609
|
+
return tx;
|
|
1610
|
+
});
|
|
1611
|
+
const hf = await adapter.getHealth(this._address);
|
|
1375
1612
|
reportFee(this._address, "borrow", fee.amount, fee.rate, gasResult.digest);
|
|
1376
1613
|
this.emitBalanceChange("USDC", borrowAmount, "borrow", gasResult.digest);
|
|
1377
1614
|
return {
|
|
@@ -1389,9 +1626,10 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1389
1626
|
if (asset !== "USDC") {
|
|
1390
1627
|
throw new T2000Error("ASSET_NOT_SUPPORTED", `Only USDC is supported for repay. Got: ${asset}`);
|
|
1391
1628
|
}
|
|
1629
|
+
const adapter = await this.resolveLending(params.protocol, asset, "repay");
|
|
1392
1630
|
let amount;
|
|
1393
1631
|
if (params.amount === "all") {
|
|
1394
|
-
const hf2 = await this.
|
|
1632
|
+
const hf2 = await adapter.getHealth(this._address);
|
|
1395
1633
|
amount = hf2.borrowed;
|
|
1396
1634
|
if (amount <= 0) {
|
|
1397
1635
|
throw new T2000Error("NO_COLLATERAL", "No outstanding borrow to repay");
|
|
@@ -1400,12 +1638,11 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1400
1638
|
amount = params.amount;
|
|
1401
1639
|
}
|
|
1402
1640
|
const repayAmount = amount;
|
|
1403
|
-
const gasResult = await executeWithGas(
|
|
1404
|
-
this.
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
);
|
|
1408
|
-
const hf = await getHealthFactor(this.client, this.keypair);
|
|
1641
|
+
const gasResult = await executeWithGas(this.client, this.keypair, async () => {
|
|
1642
|
+
const { tx } = await adapter.buildRepayTx(this._address, repayAmount, asset);
|
|
1643
|
+
return tx;
|
|
1644
|
+
});
|
|
1645
|
+
const hf = await adapter.getHealth(this._address);
|
|
1409
1646
|
this.emitBalanceChange("USDC", repayAmount, "repay", gasResult.digest);
|
|
1410
1647
|
return {
|
|
1411
1648
|
success: true,
|
|
@@ -1417,10 +1654,12 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1417
1654
|
};
|
|
1418
1655
|
}
|
|
1419
1656
|
async maxBorrow() {
|
|
1420
|
-
|
|
1657
|
+
const adapter = await this.resolveLending(void 0, "USDC", "borrow");
|
|
1658
|
+
return adapter.maxBorrow(this._address, "USDC");
|
|
1421
1659
|
}
|
|
1422
1660
|
async healthFactor() {
|
|
1423
|
-
const
|
|
1661
|
+
const adapter = await this.resolveLending(void 0, "USDC", "save");
|
|
1662
|
+
const hf = await adapter.getHealth(this._address);
|
|
1424
1663
|
if (hf.healthFactor < 1.2) {
|
|
1425
1664
|
this.emit("healthCritical", { healthFactor: hf.healthFactor, threshold: 1.5, severity: "critical" });
|
|
1426
1665
|
} else if (hf.healthFactor < 2) {
|
|
@@ -1438,19 +1677,21 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1438
1677
|
if (fromAsset === toAsset) {
|
|
1439
1678
|
throw new T2000Error("INVALID_AMOUNT", "Cannot swap same asset");
|
|
1440
1679
|
}
|
|
1680
|
+
let adapter;
|
|
1681
|
+
if (params.protocol) {
|
|
1682
|
+
const found = this.registry.getSwap(params.protocol);
|
|
1683
|
+
if (!found) throw new T2000Error("ASSET_NOT_SUPPORTED", `Swap adapter '${params.protocol}' not found`);
|
|
1684
|
+
adapter = found;
|
|
1685
|
+
} else {
|
|
1686
|
+
const best = await this.registry.bestSwapQuote(fromAsset, toAsset, params.amount);
|
|
1687
|
+
adapter = best.adapter;
|
|
1688
|
+
}
|
|
1441
1689
|
const fee = calculateFee("swap", params.amount);
|
|
1442
1690
|
const swapAmount = params.amount;
|
|
1443
1691
|
const slippageBps = params.maxSlippage ? params.maxSlippage * 100 : void 0;
|
|
1444
1692
|
let swapMeta = { estimatedOut: 0, toDecimals: 0 };
|
|
1445
1693
|
const gasResult = await executeWithGas(this.client, this.keypair, async () => {
|
|
1446
|
-
const built = await buildSwapTx(
|
|
1447
|
-
client: this.client,
|
|
1448
|
-
address: this._address,
|
|
1449
|
-
fromAsset,
|
|
1450
|
-
toAsset,
|
|
1451
|
-
amount: swapAmount,
|
|
1452
|
-
maxSlippageBps: slippageBps
|
|
1453
|
-
});
|
|
1694
|
+
const built = await adapter.buildSwapTx(this._address, fromAsset, toAsset, swapAmount, slippageBps);
|
|
1454
1695
|
swapMeta = { estimatedOut: built.estimatedOut, toDecimals: built.toDecimals };
|
|
1455
1696
|
return built.tx;
|
|
1456
1697
|
});
|
|
@@ -1489,17 +1730,39 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1489
1730
|
async swapQuote(params) {
|
|
1490
1731
|
const fromAsset = params.from.toUpperCase();
|
|
1491
1732
|
const toAsset = params.to.toUpperCase();
|
|
1492
|
-
const
|
|
1733
|
+
const best = await this.registry.bestSwapQuote(fromAsset, toAsset, params.amount);
|
|
1493
1734
|
const fee = calculateFee("swap", params.amount);
|
|
1494
|
-
return { ...quote, fee: { amount: fee.amount, rate: fee.rate } };
|
|
1735
|
+
return { ...best.quote, fee: { amount: fee.amount, rate: fee.rate } };
|
|
1495
1736
|
}
|
|
1496
1737
|
// -- Info --
|
|
1497
1738
|
async positions() {
|
|
1498
|
-
|
|
1739
|
+
const allPositions = await this.registry.allPositions(this._address);
|
|
1740
|
+
const positions = allPositions.flatMap(
|
|
1741
|
+
(p) => [
|
|
1742
|
+
...p.positions.supplies.map((s) => ({
|
|
1743
|
+
protocol: p.protocolId,
|
|
1744
|
+
asset: s.asset,
|
|
1745
|
+
type: "save",
|
|
1746
|
+
amount: s.amount,
|
|
1747
|
+
apy: s.apy
|
|
1748
|
+
})),
|
|
1749
|
+
...p.positions.borrows.map((b) => ({
|
|
1750
|
+
protocol: p.protocolId,
|
|
1751
|
+
asset: b.asset,
|
|
1752
|
+
type: "borrow",
|
|
1753
|
+
amount: b.amount,
|
|
1754
|
+
apy: b.apy
|
|
1755
|
+
}))
|
|
1756
|
+
]
|
|
1757
|
+
);
|
|
1758
|
+
return { positions };
|
|
1499
1759
|
}
|
|
1500
1760
|
async rates() {
|
|
1501
1761
|
return getRates(this.client);
|
|
1502
1762
|
}
|
|
1763
|
+
async allRates(asset = "USDC") {
|
|
1764
|
+
return this.registry.allRates(asset);
|
|
1765
|
+
}
|
|
1503
1766
|
async earnings() {
|
|
1504
1767
|
const result = await getEarnings(this.client, this.keypair);
|
|
1505
1768
|
if (result.totalYieldEarned > 0) {
|
|
@@ -1526,6 +1789,29 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
|
|
|
1526
1789
|
return attack(this.client, this.keypair, id, prompt, fee);
|
|
1527
1790
|
}
|
|
1528
1791
|
// -- Helpers --
|
|
1792
|
+
async resolveLending(protocol, asset, capability) {
|
|
1793
|
+
if (protocol) {
|
|
1794
|
+
const adapter = this.registry.getLending(protocol);
|
|
1795
|
+
if (!adapter) throw new T2000Error("ASSET_NOT_SUPPORTED", `Lending adapter '${protocol}' not found`);
|
|
1796
|
+
return adapter;
|
|
1797
|
+
}
|
|
1798
|
+
if (capability === "save") {
|
|
1799
|
+
const { adapter } = await this.registry.bestSaveRate(asset);
|
|
1800
|
+
return adapter;
|
|
1801
|
+
}
|
|
1802
|
+
if (capability === "borrow" || capability === "repay") {
|
|
1803
|
+
const adapters2 = this.registry.listLending().filter(
|
|
1804
|
+
(a) => a.supportedAssets.includes(asset) && a.capabilities.includes(capability) && (capability !== "borrow" || a.supportsSameAssetBorrow)
|
|
1805
|
+
);
|
|
1806
|
+
if (adapters2.length === 0) throw new T2000Error("ASSET_NOT_SUPPORTED", `No adapter supports ${capability} ${asset}`);
|
|
1807
|
+
return adapters2[0];
|
|
1808
|
+
}
|
|
1809
|
+
const adapters = this.registry.listLending().filter(
|
|
1810
|
+
(a) => a.supportedAssets.includes(asset) && a.capabilities.includes(capability)
|
|
1811
|
+
);
|
|
1812
|
+
if (adapters.length === 0) throw new T2000Error("ASSET_NOT_SUPPORTED", `No adapter supports ${capability} ${asset}`);
|
|
1813
|
+
return adapters[0];
|
|
1814
|
+
}
|
|
1529
1815
|
emitBalanceChange(asset, amount, cause, tx) {
|
|
1530
1816
|
this.emit("balanceChange", { asset, previous: 0, current: 0, cause, tx });
|
|
1531
1817
|
}
|
|
@@ -1623,17 +1909,312 @@ function parseMoveAbort(errorStr) {
|
|
|
1623
1909
|
}
|
|
1624
1910
|
return { reason: errorStr };
|
|
1625
1911
|
}
|
|
1912
|
+
var USDC_TYPE2 = SUPPORTED_ASSETS.USDC.type;
|
|
1913
|
+
SUPPORTED_ASSETS.USDC.decimals;
|
|
1914
|
+
var WAD = 1e18;
|
|
1915
|
+
var MIN_HEALTH_FACTOR2 = 1.5;
|
|
1916
|
+
function interpolateRate(utilBreakpoints, aprBreakpoints, utilizationPct) {
|
|
1917
|
+
if (utilBreakpoints.length === 0) return 0;
|
|
1918
|
+
if (utilizationPct <= utilBreakpoints[0]) return aprBreakpoints[0];
|
|
1919
|
+
if (utilizationPct >= utilBreakpoints[utilBreakpoints.length - 1]) {
|
|
1920
|
+
return aprBreakpoints[aprBreakpoints.length - 1];
|
|
1921
|
+
}
|
|
1922
|
+
for (let i = 1; i < utilBreakpoints.length; i++) {
|
|
1923
|
+
if (utilizationPct <= utilBreakpoints[i]) {
|
|
1924
|
+
const t = (utilizationPct - utilBreakpoints[i - 1]) / (utilBreakpoints[i] - utilBreakpoints[i - 1]);
|
|
1925
|
+
return aprBreakpoints[i - 1] + t * (aprBreakpoints[i] - aprBreakpoints[i - 1]);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
return aprBreakpoints[aprBreakpoints.length - 1];
|
|
1929
|
+
}
|
|
1930
|
+
function computeRatesFromReserve(reserve) {
|
|
1931
|
+
const decimals = reserve.mintDecimals;
|
|
1932
|
+
const available = Number(reserve.availableAmount) / 10 ** decimals;
|
|
1933
|
+
const borrowed = Number(reserve.borrowedAmount.value) / WAD / 10 ** decimals;
|
|
1934
|
+
const totalDeposited = available + borrowed;
|
|
1935
|
+
const utilizationPct = totalDeposited > 0 ? borrowed / totalDeposited * 100 : 0;
|
|
1936
|
+
const config = reserve.config.element;
|
|
1937
|
+
if (!config) return { borrowAprPct: 0, depositAprPct: 0, utilizationPct: 0 };
|
|
1938
|
+
const utils = config.interestRateUtils.map(Number);
|
|
1939
|
+
const aprs = config.interestRateAprs.map((a) => Number(a) / 100);
|
|
1940
|
+
const borrowAprPct = interpolateRate(utils, aprs, utilizationPct);
|
|
1941
|
+
const spreadFeeBps = Number(config.spreadFeeBps);
|
|
1942
|
+
const depositAprPct = utilizationPct / 100 * (borrowAprPct / 100) * (1 - spreadFeeBps / 1e4) * 100;
|
|
1943
|
+
return { borrowAprPct, depositAprPct, utilizationPct };
|
|
1944
|
+
}
|
|
1945
|
+
function cTokenRatio(reserve) {
|
|
1946
|
+
if (reserve.ctokenSupply === 0n) return 1;
|
|
1947
|
+
const available = Number(reserve.availableAmount);
|
|
1948
|
+
const borrowed = Number(reserve.borrowedAmount.value) / WAD;
|
|
1949
|
+
const spreadFees = Number(reserve.unclaimedSpreadFees.value) / WAD;
|
|
1950
|
+
const totalSupply = available + borrowed - spreadFees;
|
|
1951
|
+
return totalSupply / Number(reserve.ctokenSupply);
|
|
1952
|
+
}
|
|
1953
|
+
var SuilendAdapter = class {
|
|
1954
|
+
id = "suilend";
|
|
1955
|
+
name = "Suilend";
|
|
1956
|
+
version = "1.0.0";
|
|
1957
|
+
capabilities = ["save", "withdraw"];
|
|
1958
|
+
supportedAssets = ["USDC"];
|
|
1959
|
+
supportsSameAssetBorrow = false;
|
|
1960
|
+
client;
|
|
1961
|
+
suilend;
|
|
1962
|
+
lendingMarketType;
|
|
1963
|
+
initialized = false;
|
|
1964
|
+
async init(client) {
|
|
1965
|
+
let sdk;
|
|
1966
|
+
try {
|
|
1967
|
+
sdk = await import('@suilend/sdk');
|
|
1968
|
+
} catch {
|
|
1969
|
+
throw new T2000Error(
|
|
1970
|
+
"PROTOCOL_UNAVAILABLE",
|
|
1971
|
+
"Suilend SDK not installed. Run: npm install @suilend/sdk@^1"
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1974
|
+
this.client = client;
|
|
1975
|
+
this.lendingMarketType = sdk.LENDING_MARKET_TYPE;
|
|
1976
|
+
try {
|
|
1977
|
+
this.suilend = await sdk.SuilendClient.initialize(
|
|
1978
|
+
sdk.LENDING_MARKET_ID,
|
|
1979
|
+
sdk.LENDING_MARKET_TYPE,
|
|
1980
|
+
client
|
|
1981
|
+
);
|
|
1982
|
+
} catch (err) {
|
|
1983
|
+
throw new T2000Error(
|
|
1984
|
+
"PROTOCOL_UNAVAILABLE",
|
|
1985
|
+
`Failed to initialize Suilend: ${err instanceof Error ? err.message : String(err)}`
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
this.initialized = true;
|
|
1989
|
+
}
|
|
1990
|
+
ensureInit() {
|
|
1991
|
+
if (!this.initialized) {
|
|
1992
|
+
throw new T2000Error(
|
|
1993
|
+
"PROTOCOL_UNAVAILABLE",
|
|
1994
|
+
"SuilendAdapter not initialized. Call init() first."
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
findReserve(asset) {
|
|
1999
|
+
const upper = asset.toUpperCase();
|
|
2000
|
+
let coinType;
|
|
2001
|
+
if (upper === "USDC") coinType = USDC_TYPE2;
|
|
2002
|
+
else if (upper === "SUI") coinType = "0x2::sui::SUI";
|
|
2003
|
+
else if (asset.includes("::")) coinType = asset;
|
|
2004
|
+
else return void 0;
|
|
2005
|
+
try {
|
|
2006
|
+
const normalized = utils.normalizeStructTag(coinType);
|
|
2007
|
+
return this.suilend.lendingMarket.reserves.find(
|
|
2008
|
+
(r) => utils.normalizeStructTag(r.coinType.name) === normalized
|
|
2009
|
+
);
|
|
2010
|
+
} catch {
|
|
2011
|
+
return void 0;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
async getObligationCaps(address) {
|
|
2015
|
+
const SuilendClientStatic = (await import('@suilend/sdk')).SuilendClient;
|
|
2016
|
+
return SuilendClientStatic.getObligationOwnerCaps(
|
|
2017
|
+
address,
|
|
2018
|
+
[this.lendingMarketType],
|
|
2019
|
+
this.client
|
|
2020
|
+
);
|
|
2021
|
+
}
|
|
2022
|
+
resolveSymbol(coinType) {
|
|
2023
|
+
const normalized = utils.normalizeStructTag(coinType);
|
|
2024
|
+
if (normalized === utils.normalizeStructTag(USDC_TYPE2)) return "USDC";
|
|
2025
|
+
if (normalized === utils.normalizeStructTag("0x2::sui::SUI")) return "SUI";
|
|
2026
|
+
const parts = coinType.split("::");
|
|
2027
|
+
return parts[parts.length - 1] || "UNKNOWN";
|
|
2028
|
+
}
|
|
2029
|
+
async getRates(asset) {
|
|
2030
|
+
this.ensureInit();
|
|
2031
|
+
const reserve = this.findReserve(asset);
|
|
2032
|
+
if (!reserve) {
|
|
2033
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `Suilend does not support ${asset}`);
|
|
2034
|
+
}
|
|
2035
|
+
const { borrowAprPct, depositAprPct } = computeRatesFromReserve(reserve);
|
|
2036
|
+
return {
|
|
2037
|
+
asset,
|
|
2038
|
+
saveApy: depositAprPct,
|
|
2039
|
+
borrowApy: borrowAprPct
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
async getPositions(address) {
|
|
2043
|
+
this.ensureInit();
|
|
2044
|
+
const supplies = [];
|
|
2045
|
+
const borrows = [];
|
|
2046
|
+
const caps = await this.getObligationCaps(address);
|
|
2047
|
+
if (caps.length === 0) return { supplies, borrows };
|
|
2048
|
+
const obligation = await this.suilend.getObligation(caps[0].obligationId);
|
|
2049
|
+
for (const deposit of obligation.deposits) {
|
|
2050
|
+
const coinType = utils.normalizeStructTag(deposit.coinType.name);
|
|
2051
|
+
const reserve = this.suilend.lendingMarket.reserves.find(
|
|
2052
|
+
(r) => utils.normalizeStructTag(r.coinType.name) === coinType
|
|
2053
|
+
);
|
|
2054
|
+
if (!reserve) continue;
|
|
2055
|
+
const ctokenAmount = Number(deposit.depositedCtokenAmount.toString());
|
|
2056
|
+
const ratio = cTokenRatio(reserve);
|
|
2057
|
+
const amount = ctokenAmount * ratio / 10 ** reserve.mintDecimals;
|
|
2058
|
+
const { depositAprPct } = computeRatesFromReserve(reserve);
|
|
2059
|
+
supplies.push({ asset: this.resolveSymbol(coinType), amount, apy: depositAprPct });
|
|
2060
|
+
}
|
|
2061
|
+
for (const borrow of obligation.borrows) {
|
|
2062
|
+
const coinType = utils.normalizeStructTag(borrow.coinType.name);
|
|
2063
|
+
const reserve = this.suilend.lendingMarket.reserves.find(
|
|
2064
|
+
(r) => utils.normalizeStructTag(r.coinType.name) === coinType
|
|
2065
|
+
);
|
|
2066
|
+
if (!reserve) continue;
|
|
2067
|
+
const rawBorrowed = Number(borrow.borrowedAmount.value.toString()) / WAD;
|
|
2068
|
+
const amount = rawBorrowed / 10 ** reserve.mintDecimals;
|
|
2069
|
+
const reserveRate = Number(reserve.cumulativeBorrowRate.value.toString()) / WAD;
|
|
2070
|
+
const posRate = Number(borrow.cumulativeBorrowRate.value.toString()) / WAD;
|
|
2071
|
+
const compounded = posRate > 0 ? amount * (reserveRate / posRate) : amount;
|
|
2072
|
+
const { borrowAprPct } = computeRatesFromReserve(reserve);
|
|
2073
|
+
borrows.push({ asset: this.resolveSymbol(coinType), amount: compounded, apy: borrowAprPct });
|
|
2074
|
+
}
|
|
2075
|
+
return { supplies, borrows };
|
|
2076
|
+
}
|
|
2077
|
+
async getHealth(address) {
|
|
2078
|
+
this.ensureInit();
|
|
2079
|
+
const caps = await this.getObligationCaps(address);
|
|
2080
|
+
if (caps.length === 0) {
|
|
2081
|
+
return { healthFactor: Infinity, supplied: 0, borrowed: 0, maxBorrow: 0, liquidationThreshold: 0 };
|
|
2082
|
+
}
|
|
2083
|
+
const positions = await this.getPositions(address);
|
|
2084
|
+
const supplied = positions.supplies.reduce((s, p) => s + p.amount, 0);
|
|
2085
|
+
const borrowed = positions.borrows.reduce((s, p) => s + p.amount, 0);
|
|
2086
|
+
const reserve = this.findReserve("USDC");
|
|
2087
|
+
const closeLtv = reserve?.config?.element?.closeLtvPct ?? 75;
|
|
2088
|
+
const openLtv = reserve?.config?.element?.openLtvPct ?? 70;
|
|
2089
|
+
const liqThreshold = closeLtv / 100;
|
|
2090
|
+
const healthFactor = borrowed > 0 ? supplied * liqThreshold / borrowed : Infinity;
|
|
2091
|
+
const maxBorrow = Math.max(0, supplied * (openLtv / 100) - borrowed);
|
|
2092
|
+
return { healthFactor, supplied, borrowed, maxBorrow, liquidationThreshold: liqThreshold };
|
|
2093
|
+
}
|
|
2094
|
+
async buildSaveTx(address, amount, _asset, options) {
|
|
2095
|
+
this.ensureInit();
|
|
2096
|
+
const rawAmount = usdcToRaw(amount).toString();
|
|
2097
|
+
const tx = new transactions.Transaction();
|
|
2098
|
+
tx.setSender(address);
|
|
2099
|
+
const caps = await this.getObligationCaps(address);
|
|
2100
|
+
let capRef;
|
|
2101
|
+
if (caps.length === 0) {
|
|
2102
|
+
const [newCap] = this.suilend.createObligation(tx);
|
|
2103
|
+
capRef = newCap;
|
|
2104
|
+
} else {
|
|
2105
|
+
capRef = caps[0].id;
|
|
2106
|
+
}
|
|
2107
|
+
const allCoins = await this.fetchAllCoins(address, USDC_TYPE2);
|
|
2108
|
+
if (allCoins.length === 0) {
|
|
2109
|
+
throw new T2000Error("INSUFFICIENT_BALANCE", "No USDC coins found");
|
|
2110
|
+
}
|
|
2111
|
+
const primaryCoinId = allCoins[0].coinObjectId;
|
|
2112
|
+
if (allCoins.length > 1) {
|
|
2113
|
+
tx.mergeCoins(
|
|
2114
|
+
tx.object(primaryCoinId),
|
|
2115
|
+
allCoins.slice(1).map((c) => tx.object(c.coinObjectId))
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
const [depositCoin] = tx.splitCoins(tx.object(primaryCoinId), [rawAmount]);
|
|
2119
|
+
if (options?.collectFee) {
|
|
2120
|
+
addCollectFeeToTx(tx, depositCoin, "save");
|
|
2121
|
+
}
|
|
2122
|
+
this.suilend.deposit(depositCoin, USDC_TYPE2, capRef, tx);
|
|
2123
|
+
return { tx };
|
|
2124
|
+
}
|
|
2125
|
+
async buildWithdrawTx(address, amount, _asset) {
|
|
2126
|
+
this.ensureInit();
|
|
2127
|
+
const caps = await this.getObligationCaps(address);
|
|
2128
|
+
if (caps.length === 0) {
|
|
2129
|
+
throw new T2000Error("NO_COLLATERAL", "No Suilend position found");
|
|
2130
|
+
}
|
|
2131
|
+
const positions = await this.getPositions(address);
|
|
2132
|
+
const usdcSupply = positions.supplies.find((s) => s.asset === "USDC");
|
|
2133
|
+
const deposited = usdcSupply?.amount ?? 0;
|
|
2134
|
+
const effectiveAmount = Math.min(amount, deposited);
|
|
2135
|
+
if (effectiveAmount <= 0) {
|
|
2136
|
+
throw new T2000Error("NO_COLLATERAL", "Nothing to withdraw from Suilend");
|
|
2137
|
+
}
|
|
2138
|
+
const rawAmount = usdcToRaw(effectiveAmount).toString();
|
|
2139
|
+
const tx = new transactions.Transaction();
|
|
2140
|
+
tx.setSender(address);
|
|
2141
|
+
await this.suilend.withdrawAndSendToUser(
|
|
2142
|
+
address,
|
|
2143
|
+
caps[0].id,
|
|
2144
|
+
caps[0].obligationId,
|
|
2145
|
+
USDC_TYPE2,
|
|
2146
|
+
rawAmount,
|
|
2147
|
+
tx
|
|
2148
|
+
);
|
|
2149
|
+
return { tx, effectiveAmount };
|
|
2150
|
+
}
|
|
2151
|
+
async buildBorrowTx(_address, _amount, _asset, _options) {
|
|
2152
|
+
throw new T2000Error(
|
|
2153
|
+
"ASSET_NOT_SUPPORTED",
|
|
2154
|
+
"SuilendAdapter.buildBorrowTx() not available \u2014 Suilend requires different collateral/borrow assets. Deferred to Phase 10."
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
async buildRepayTx(_address, _amount, _asset) {
|
|
2158
|
+
throw new T2000Error(
|
|
2159
|
+
"ASSET_NOT_SUPPORTED",
|
|
2160
|
+
"SuilendAdapter.buildRepayTx() not available \u2014 deferred to Phase 10."
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
async maxWithdraw(address, _asset) {
|
|
2164
|
+
this.ensureInit();
|
|
2165
|
+
const health = await this.getHealth(address);
|
|
2166
|
+
let maxAmount;
|
|
2167
|
+
if (health.borrowed === 0) {
|
|
2168
|
+
maxAmount = health.supplied;
|
|
2169
|
+
} else {
|
|
2170
|
+
maxAmount = Math.max(
|
|
2171
|
+
0,
|
|
2172
|
+
health.supplied - health.borrowed * MIN_HEALTH_FACTOR2 / health.liquidationThreshold
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
const remainingSupply = health.supplied - maxAmount;
|
|
2176
|
+
const hfAfter = health.borrowed > 0 ? remainingSupply * health.liquidationThreshold / health.borrowed : Infinity;
|
|
2177
|
+
return {
|
|
2178
|
+
maxAmount,
|
|
2179
|
+
healthFactorAfter: hfAfter,
|
|
2180
|
+
currentHF: health.healthFactor
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2183
|
+
async maxBorrow(_address, _asset) {
|
|
2184
|
+
throw new T2000Error(
|
|
2185
|
+
"ASSET_NOT_SUPPORTED",
|
|
2186
|
+
"SuilendAdapter.maxBorrow() not available \u2014 deferred to Phase 10."
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
async fetchAllCoins(owner, coinType) {
|
|
2190
|
+
const all = [];
|
|
2191
|
+
let cursor = null;
|
|
2192
|
+
let hasNext = true;
|
|
2193
|
+
while (hasNext) {
|
|
2194
|
+
const page = await this.client.getCoins({ owner, coinType, cursor: cursor ?? void 0 });
|
|
2195
|
+
all.push(...page.data.map((c) => ({ coinObjectId: c.coinObjectId, balance: c.balance })));
|
|
2196
|
+
cursor = page.nextCursor;
|
|
2197
|
+
hasNext = page.hasNextPage;
|
|
2198
|
+
}
|
|
2199
|
+
return all;
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
1626
2202
|
|
|
1627
2203
|
exports.BPS_DENOMINATOR = BPS_DENOMINATOR;
|
|
1628
2204
|
exports.CLOCK_ID = CLOCK_ID;
|
|
2205
|
+
exports.CetusAdapter = CetusAdapter;
|
|
1629
2206
|
exports.DEFAULT_NETWORK = DEFAULT_NETWORK;
|
|
1630
2207
|
exports.MIST_PER_SUI = MIST_PER_SUI;
|
|
2208
|
+
exports.NaviAdapter = NaviAdapter;
|
|
2209
|
+
exports.ProtocolRegistry = ProtocolRegistry;
|
|
1631
2210
|
exports.SENTINEL = SENTINEL;
|
|
1632
2211
|
exports.SUI_DECIMALS = SUI_DECIMALS;
|
|
1633
2212
|
exports.SUPPORTED_ASSETS = SUPPORTED_ASSETS;
|
|
2213
|
+
exports.SuilendAdapter = SuilendAdapter;
|
|
1634
2214
|
exports.T2000 = T2000;
|
|
1635
2215
|
exports.T2000Error = T2000Error;
|
|
1636
2216
|
exports.USDC_DECIMALS = USDC_DECIMALS;
|
|
2217
|
+
exports.addCollectFeeToTx = addCollectFeeToTx;
|
|
1637
2218
|
exports.calculateFee = calculateFee;
|
|
1638
2219
|
exports.executeAutoTopUp = executeAutoTopUp;
|
|
1639
2220
|
exports.executeWithGas = executeWithGas;
|