@t2000/sdk 0.1.4 → 0.1.6
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 -5
- package/dist/index.cjs +261 -330
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -6
- package/dist/index.d.ts +0 -6
- package/dist/index.js +261 -330
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -236,39 +236,27 @@ function formatSui(amount) {
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
// src/wallet/send.ts
|
|
239
|
-
async function
|
|
239
|
+
async function buildSendTx({
|
|
240
240
|
client,
|
|
241
|
-
|
|
241
|
+
address,
|
|
242
242
|
to,
|
|
243
243
|
amount,
|
|
244
244
|
asset = "USDC"
|
|
245
245
|
}) {
|
|
246
246
|
const recipient = validateAddress(to);
|
|
247
247
|
const assetInfo = SUPPORTED_ASSETS[asset];
|
|
248
|
-
if (!assetInfo) {
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
if (amount <= 0) {
|
|
252
|
-
throw new T2000Error("INVALID_AMOUNT", "Amount must be greater than zero");
|
|
253
|
-
}
|
|
254
|
-
const senderAddress = keypair.getPublicKey().toSuiAddress();
|
|
248
|
+
if (!assetInfo) throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
|
|
249
|
+
if (amount <= 0) throw new T2000Error("INVALID_AMOUNT", "Amount must be greater than zero");
|
|
255
250
|
const rawAmount = displayToRaw(amount, assetInfo.decimals);
|
|
256
251
|
const tx = new Transaction();
|
|
252
|
+
tx.setSender(address);
|
|
257
253
|
if (asset === "SUI") {
|
|
258
254
|
const [coin] = tx.splitCoins(tx.gas, [rawAmount]);
|
|
259
255
|
tx.transferObjects([coin], recipient);
|
|
260
256
|
} else {
|
|
261
|
-
const coins = await client.getCoins({
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
});
|
|
265
|
-
if (coins.data.length === 0) {
|
|
266
|
-
throw new T2000Error("INSUFFICIENT_BALANCE", `No ${asset} coins found`);
|
|
267
|
-
}
|
|
268
|
-
const totalBalance = coins.data.reduce(
|
|
269
|
-
(sum, c) => sum + BigInt(c.balance),
|
|
270
|
-
0n
|
|
271
|
-
);
|
|
257
|
+
const coins = await client.getCoins({ owner: address, coinType: assetInfo.type });
|
|
258
|
+
if (coins.data.length === 0) throw new T2000Error("INSUFFICIENT_BALANCE", `No ${asset} coins found`);
|
|
259
|
+
const totalBalance = coins.data.reduce((sum, c) => sum + BigInt(c.balance), 0n);
|
|
272
260
|
if (totalBalance < rawAmount) {
|
|
273
261
|
throw new T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, {
|
|
274
262
|
available: Number(totalBalance) / 10 ** assetInfo.decimals,
|
|
@@ -277,28 +265,12 @@ async function buildAndExecuteSend({
|
|
|
277
265
|
}
|
|
278
266
|
const primaryCoin = tx.object(coins.data[0].coinObjectId);
|
|
279
267
|
if (coins.data.length > 1) {
|
|
280
|
-
tx.mergeCoins(
|
|
281
|
-
primaryCoin,
|
|
282
|
-
coins.data.slice(1).map((c) => tx.object(c.coinObjectId))
|
|
283
|
-
);
|
|
268
|
+
tx.mergeCoins(primaryCoin, coins.data.slice(1).map((c) => tx.object(c.coinObjectId)));
|
|
284
269
|
}
|
|
285
270
|
const [sendCoin] = tx.splitCoins(primaryCoin, [rawAmount]);
|
|
286
271
|
tx.transferObjects([sendCoin], recipient);
|
|
287
272
|
}
|
|
288
|
-
|
|
289
|
-
signer: keypair,
|
|
290
|
-
transaction: tx,
|
|
291
|
-
options: { showEffects: true }
|
|
292
|
-
});
|
|
293
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
294
|
-
const gasUsed = result.effects?.gasUsed;
|
|
295
|
-
const gasCost = gasUsed ? Math.abs(
|
|
296
|
-
(Number(gasUsed.computationCost) + Number(gasUsed.storageCost) - Number(gasUsed.storageRebate)) / 1e9
|
|
297
|
-
) : 0;
|
|
298
|
-
return {
|
|
299
|
-
digest: result.digest,
|
|
300
|
-
gasCost
|
|
301
|
-
};
|
|
273
|
+
return tx;
|
|
302
274
|
}
|
|
303
275
|
|
|
304
276
|
// src/wallet/balance.ts
|
|
@@ -396,12 +368,6 @@ var NAVI_BALANCE_DECIMALS = 9;
|
|
|
396
368
|
function clientOpt(client, fresh = false) {
|
|
397
369
|
return { client, ...ENV, ...fresh ? { disableCache: true } : {} };
|
|
398
370
|
}
|
|
399
|
-
function extractGasCost(effects) {
|
|
400
|
-
if (!effects?.gasUsed) return 0;
|
|
401
|
-
return Math.abs(
|
|
402
|
-
(Number(effects.gasUsed.computationCost) + Number(effects.gasUsed.storageCost) - Number(effects.gasUsed.storageRebate)) / 1e9
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
371
|
function rateToApy(rawRate) {
|
|
406
372
|
if (!rawRate || rawRate === "0") return 0;
|
|
407
373
|
return Number(BigInt(rawRate)) / 10 ** RATE_DECIMALS * 100;
|
|
@@ -434,8 +400,7 @@ async function updateOracle(tx, client, address) {
|
|
|
434
400
|
} catch {
|
|
435
401
|
}
|
|
436
402
|
}
|
|
437
|
-
async function
|
|
438
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
403
|
+
async function buildSaveTx(client, address, amount) {
|
|
439
404
|
const rawAmount = Number(usdcToRaw(amount));
|
|
440
405
|
const coins = await getCoins(address, { coinType: USDC_TYPE, client });
|
|
441
406
|
if (!coins || coins.length === 0) {
|
|
@@ -445,25 +410,9 @@ async function save(client, keypair, amount) {
|
|
|
445
410
|
tx.setSender(address);
|
|
446
411
|
const coinObj = mergeCoinsPTB(tx, coins, { balance: rawAmount });
|
|
447
412
|
await depositCoinPTB(tx, USDC_TYPE, coinObj, ENV);
|
|
448
|
-
|
|
449
|
-
signer: keypair,
|
|
450
|
-
transaction: tx,
|
|
451
|
-
options: { showEffects: true }
|
|
452
|
-
});
|
|
453
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
454
|
-
const rates = await getRates();
|
|
455
|
-
return {
|
|
456
|
-
success: true,
|
|
457
|
-
tx: result.digest,
|
|
458
|
-
amount,
|
|
459
|
-
apy: rates.USDC.saveApy,
|
|
460
|
-
fee: 0,
|
|
461
|
-
gasCost: extractGasCost(result.effects),
|
|
462
|
-
gasMethod: "self-funded"
|
|
463
|
-
};
|
|
413
|
+
return tx;
|
|
464
414
|
}
|
|
465
|
-
async function
|
|
466
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
415
|
+
async function buildWithdrawTx(client, address, amount) {
|
|
467
416
|
const state = await getLendingState(address, clientOpt(client, true));
|
|
468
417
|
const usdcPos = findUsdcPosition(state);
|
|
469
418
|
const deposited = usdcPos ? Number(usdcPos.supplyBalance) / 10 ** NAVI_BALANCE_DECIMALS : 0;
|
|
@@ -475,47 +424,18 @@ async function withdraw(client, keypair, amount) {
|
|
|
475
424
|
await updateOracle(tx, client, address);
|
|
476
425
|
const withdrawnCoin = await withdrawCoinPTB(tx, USDC_TYPE, rawAmount, ENV);
|
|
477
426
|
tx.transferObjects([withdrawnCoin], address);
|
|
478
|
-
|
|
479
|
-
signer: keypair,
|
|
480
|
-
transaction: tx,
|
|
481
|
-
options: { showEffects: true }
|
|
482
|
-
});
|
|
483
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
484
|
-
return {
|
|
485
|
-
success: true,
|
|
486
|
-
tx: result.digest,
|
|
487
|
-
amount: effectiveAmount,
|
|
488
|
-
gasCost: extractGasCost(result.effects),
|
|
489
|
-
gasMethod: "self-funded"
|
|
490
|
-
};
|
|
427
|
+
return { tx, effectiveAmount };
|
|
491
428
|
}
|
|
492
|
-
async function
|
|
493
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
429
|
+
async function buildBorrowTx(client, address, amount) {
|
|
494
430
|
const rawAmount = Number(usdcToRaw(amount));
|
|
495
431
|
const tx = new Transaction();
|
|
496
432
|
tx.setSender(address);
|
|
497
433
|
await updateOracle(tx, client, address);
|
|
498
434
|
const borrowedCoin = await borrowCoinPTB(tx, USDC_TYPE, rawAmount, ENV);
|
|
499
435
|
tx.transferObjects([borrowedCoin], address);
|
|
500
|
-
|
|
501
|
-
signer: keypair,
|
|
502
|
-
transaction: tx,
|
|
503
|
-
options: { showEffects: true }
|
|
504
|
-
});
|
|
505
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
506
|
-
const hf = await getHealthFactor$1(address, clientOpt(client, true));
|
|
507
|
-
return {
|
|
508
|
-
success: true,
|
|
509
|
-
tx: result.digest,
|
|
510
|
-
amount,
|
|
511
|
-
fee: 0,
|
|
512
|
-
healthFactor: hf,
|
|
513
|
-
gasCost: extractGasCost(result.effects),
|
|
514
|
-
gasMethod: "self-funded"
|
|
515
|
-
};
|
|
436
|
+
return tx;
|
|
516
437
|
}
|
|
517
|
-
async function
|
|
518
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
438
|
+
async function buildRepayTx(client, address, amount) {
|
|
519
439
|
const rawAmount = Number(usdcToRaw(amount));
|
|
520
440
|
const coins = await getCoins(address, { coinType: USDC_TYPE, client });
|
|
521
441
|
if (!coins || coins.length === 0) {
|
|
@@ -525,23 +445,7 @@ async function repay(client, keypair, amount) {
|
|
|
525
445
|
tx.setSender(address);
|
|
526
446
|
const coinObj = mergeCoinsPTB(tx, coins, { balance: rawAmount });
|
|
527
447
|
await repayCoinPTB(tx, USDC_TYPE, coinObj, { ...ENV, amount: rawAmount });
|
|
528
|
-
|
|
529
|
-
signer: keypair,
|
|
530
|
-
transaction: tx,
|
|
531
|
-
options: { showEffects: true }
|
|
532
|
-
});
|
|
533
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
534
|
-
const state = await getLendingState(address, clientOpt(client, true));
|
|
535
|
-
const usdcPos = findUsdcPosition(state);
|
|
536
|
-
const remainingDebt = usdcPos ? Number(usdcPos.borrowBalance) / 10 ** NAVI_BALANCE_DECIMALS : 0;
|
|
537
|
-
return {
|
|
538
|
-
success: true,
|
|
539
|
-
tx: result.digest,
|
|
540
|
-
amount,
|
|
541
|
-
remainingDebt,
|
|
542
|
-
gasCost: extractGasCost(result.effects),
|
|
543
|
-
gasMethod: "self-funded"
|
|
544
|
-
};
|
|
448
|
+
return tx;
|
|
545
449
|
}
|
|
546
450
|
async function getHealthFactor(client, keypair) {
|
|
547
451
|
const address = keypair.getPublicKey().toSuiAddress();
|
|
@@ -633,10 +537,6 @@ async function maxBorrowAmount(client, keypair) {
|
|
|
633
537
|
};
|
|
634
538
|
}
|
|
635
539
|
var DEFAULT_SLIPPAGE_BPS = 300;
|
|
636
|
-
function extractGasCost2(effects) {
|
|
637
|
-
if (!effects?.gasUsed) return 0;
|
|
638
|
-
return (Number(effects.gasUsed.computationCost) + Number(effects.gasUsed.storageCost) - Number(effects.gasUsed.storageRebate)) / 1e9;
|
|
639
|
-
}
|
|
640
540
|
function isA2B(from) {
|
|
641
541
|
return from === "USDC";
|
|
642
542
|
}
|
|
@@ -647,9 +547,8 @@ function getCetusSDK() {
|
|
|
647
547
|
}
|
|
648
548
|
return _cetusSDK;
|
|
649
549
|
}
|
|
650
|
-
async function
|
|
651
|
-
const {
|
|
652
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
550
|
+
async function buildSwapTx(params) {
|
|
551
|
+
const { address, fromAsset, toAsset, amount, maxSlippageBps = DEFAULT_SLIPPAGE_BPS } = params;
|
|
653
552
|
const a2b = isA2B(fromAsset);
|
|
654
553
|
const fromInfo = SUPPORTED_ASSETS[fromAsset];
|
|
655
554
|
const toInfo = SUPPORTED_ASSETS[toAsset];
|
|
@@ -680,32 +579,10 @@ async function executeSwap(params) {
|
|
|
680
579
|
amount: preSwapResult.amount.toString(),
|
|
681
580
|
amount_limit: amountLimit.toString()
|
|
682
581
|
});
|
|
683
|
-
const result = await client.signAndExecuteTransaction({
|
|
684
|
-
signer: keypair,
|
|
685
|
-
transaction: swapPayload,
|
|
686
|
-
options: { showEffects: true, showBalanceChanges: true }
|
|
687
|
-
});
|
|
688
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
689
|
-
let actualReceived = 0;
|
|
690
|
-
if (result.balanceChanges) {
|
|
691
|
-
for (const change of result.balanceChanges) {
|
|
692
|
-
if (change.coinType === toInfo.type && change.owner && typeof change.owner === "object" && "AddressOwner" in change.owner && change.owner.AddressOwner === address) {
|
|
693
|
-
const amt = Number(change.amount) / 10 ** toInfo.decimals;
|
|
694
|
-
if (amt > 0) actualReceived += amt;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
const expectedOutput = estimatedOut / 10 ** toInfo.decimals;
|
|
699
|
-
if (actualReceived === 0) actualReceived = expectedOutput;
|
|
700
|
-
const priceImpact = expectedOutput > 0 ? Math.abs(actualReceived - expectedOutput) / expectedOutput : 0;
|
|
701
582
|
return {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
toAmount: actualReceived,
|
|
706
|
-
toAsset,
|
|
707
|
-
priceImpact,
|
|
708
|
-
gasCost: extractGasCost2(result.effects)
|
|
583
|
+
tx: swapPayload,
|
|
584
|
+
estimatedOut,
|
|
585
|
+
toDecimals: toInfo.decimals
|
|
709
586
|
};
|
|
710
587
|
}
|
|
711
588
|
async function getPoolPrice(client) {
|
|
@@ -982,12 +859,127 @@ async function executeAutoTopUp(client, keypair) {
|
|
|
982
859
|
};
|
|
983
860
|
}
|
|
984
861
|
|
|
862
|
+
// src/gas/manager.ts
|
|
863
|
+
function extractGasCost(effects) {
|
|
864
|
+
if (!effects?.gasUsed) return 0;
|
|
865
|
+
return (Number(effects.gasUsed.computationCost) + Number(effects.gasUsed.storageCost) - Number(effects.gasUsed.storageRebate)) / 1e9;
|
|
866
|
+
}
|
|
867
|
+
async function getSuiBalance(client, address) {
|
|
868
|
+
const bal = await client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS.SUI.type });
|
|
869
|
+
return BigInt(bal.totalBalance);
|
|
870
|
+
}
|
|
871
|
+
async function trySelfFunded(client, keypair, tx) {
|
|
872
|
+
const address = keypair.getPublicKey().toSuiAddress();
|
|
873
|
+
const suiBalance = await getSuiBalance(client, address);
|
|
874
|
+
if (suiBalance < AUTO_TOPUP_THRESHOLD) return null;
|
|
875
|
+
tx.setSender(address);
|
|
876
|
+
const result = await client.signAndExecuteTransaction({
|
|
877
|
+
signer: keypair,
|
|
878
|
+
transaction: tx,
|
|
879
|
+
options: { showEffects: true }
|
|
880
|
+
});
|
|
881
|
+
await client.waitForTransaction({ digest: result.digest });
|
|
882
|
+
return {
|
|
883
|
+
digest: result.digest,
|
|
884
|
+
effects: result.effects,
|
|
885
|
+
gasMethod: "self-funded",
|
|
886
|
+
gasCostSui: extractGasCost(result.effects)
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
async function tryAutoTopUpThenSelfFund(client, keypair, tx) {
|
|
890
|
+
const address = keypair.getPublicKey().toSuiAddress();
|
|
891
|
+
const canTopUp = await shouldAutoTopUp(client, address);
|
|
892
|
+
if (!canTopUp) return null;
|
|
893
|
+
try {
|
|
894
|
+
await executeAutoTopUp(client, keypair);
|
|
895
|
+
} catch {
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
tx.setSender(address);
|
|
899
|
+
const result = await client.signAndExecuteTransaction({
|
|
900
|
+
signer: keypair,
|
|
901
|
+
transaction: tx,
|
|
902
|
+
options: { showEffects: true }
|
|
903
|
+
});
|
|
904
|
+
await client.waitForTransaction({ digest: result.digest });
|
|
905
|
+
return {
|
|
906
|
+
digest: result.digest,
|
|
907
|
+
effects: result.effects,
|
|
908
|
+
gasMethod: "auto-topup",
|
|
909
|
+
gasCostSui: extractGasCost(result.effects)
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
async function trySponsored(client, keypair, tx) {
|
|
913
|
+
const address = keypair.getPublicKey().toSuiAddress();
|
|
914
|
+
tx.setSender(address);
|
|
915
|
+
let txBytes;
|
|
916
|
+
try {
|
|
917
|
+
txBytes = await tx.build({ client, onlyTransactionKind: true });
|
|
918
|
+
} catch {
|
|
919
|
+
return null;
|
|
920
|
+
}
|
|
921
|
+
const txBytesBase64 = Buffer.from(txBytes).toString("base64");
|
|
922
|
+
let sponsoredResult;
|
|
923
|
+
try {
|
|
924
|
+
sponsoredResult = await requestGasSponsorship(txBytesBase64, address);
|
|
925
|
+
} catch {
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
928
|
+
const sponsoredTxBytes = Buffer.from(sponsoredResult.txBytes, "base64");
|
|
929
|
+
const { signature: agentSig } = await keypair.signTransaction(sponsoredTxBytes);
|
|
930
|
+
const result = await client.executeTransactionBlock({
|
|
931
|
+
transactionBlock: sponsoredResult.txBytes,
|
|
932
|
+
signature: [agentSig, sponsoredResult.sponsorSignature],
|
|
933
|
+
options: { showEffects: true }
|
|
934
|
+
});
|
|
935
|
+
await client.waitForTransaction({ digest: result.digest });
|
|
936
|
+
const gasCost = extractGasCost(result.effects);
|
|
937
|
+
reportGasUsage(address, result.digest, gasCost, 0, sponsoredResult.type);
|
|
938
|
+
return {
|
|
939
|
+
digest: result.digest,
|
|
940
|
+
effects: result.effects,
|
|
941
|
+
gasMethod: "sponsored",
|
|
942
|
+
gasCostSui: gasCost
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
async function executeWithGas(client, keypair, buildTx) {
|
|
946
|
+
const errors = [];
|
|
947
|
+
try {
|
|
948
|
+
const tx = await buildTx();
|
|
949
|
+
const result = await trySelfFunded(client, keypair, tx);
|
|
950
|
+
if (result) return result;
|
|
951
|
+
errors.push("self-funded: SUI below threshold");
|
|
952
|
+
} catch (err) {
|
|
953
|
+
errors.push(`self-funded: ${err instanceof Error ? err.message : String(err)}`);
|
|
954
|
+
}
|
|
955
|
+
try {
|
|
956
|
+
const tx = await buildTx();
|
|
957
|
+
const result = await tryAutoTopUpThenSelfFund(client, keypair, tx);
|
|
958
|
+
if (result) return result;
|
|
959
|
+
errors.push("auto-topup: not eligible (low USDC or sufficient SUI)");
|
|
960
|
+
} catch (err) {
|
|
961
|
+
errors.push(`auto-topup: ${err instanceof Error ? err.message : String(err)}`);
|
|
962
|
+
}
|
|
963
|
+
try {
|
|
964
|
+
const tx = await buildTx();
|
|
965
|
+
const result = await trySponsored(client, keypair, tx);
|
|
966
|
+
if (result) return result;
|
|
967
|
+
errors.push("sponsored: returned null");
|
|
968
|
+
} catch (err) {
|
|
969
|
+
errors.push(`sponsored: ${err instanceof Error ? err.message : String(err)}`);
|
|
970
|
+
}
|
|
971
|
+
throw new T2000Error(
|
|
972
|
+
"INSUFFICIENT_GAS",
|
|
973
|
+
`No SUI for gas and Gas Station unavailable. Fund your wallet with SUI or USDC. [${errors.join(" | ")}]`,
|
|
974
|
+
{ reason: "all_gas_methods_exhausted", errors }
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
|
|
985
978
|
// src/t2000.ts
|
|
986
979
|
var T2000 = class _T2000 extends EventEmitter {
|
|
987
980
|
keypair;
|
|
988
981
|
client;
|
|
989
982
|
_address;
|
|
990
|
-
_lastGasMethod = "self-funded";
|
|
991
983
|
constructor(keypair, client) {
|
|
992
984
|
super();
|
|
993
985
|
this.keypair = keypair;
|
|
@@ -1041,29 +1033,6 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1041
1033
|
return { agent, address, sponsored };
|
|
1042
1034
|
}
|
|
1043
1035
|
// -- Gas --
|
|
1044
|
-
/**
|
|
1045
|
-
* Ensure the agent has enough SUI for gas.
|
|
1046
|
-
* If SUI is low and USDC is available, auto-swaps $1 USDC → SUI.
|
|
1047
|
-
*/
|
|
1048
|
-
async ensureGas() {
|
|
1049
|
-
this._lastGasMethod = "self-funded";
|
|
1050
|
-
const needsTopUp = await shouldAutoTopUp(this.client, this._address);
|
|
1051
|
-
if (!needsTopUp) return;
|
|
1052
|
-
try {
|
|
1053
|
-
const result = await executeAutoTopUp(this.client, this.keypair);
|
|
1054
|
-
this._lastGasMethod = "auto-topup";
|
|
1055
|
-
this.emit("gasAutoTopUp", {
|
|
1056
|
-
usdcSpent: result.usdcSpent,
|
|
1057
|
-
suiReceived: result.suiReceived
|
|
1058
|
-
});
|
|
1059
|
-
} catch {
|
|
1060
|
-
this.emit("gasStationFallback", {
|
|
1061
|
-
reason: "auto-topup failed",
|
|
1062
|
-
method: "self-funded",
|
|
1063
|
-
suiUsed: 0
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
1036
|
/** SuiClient used by this agent — exposed for x402 and other integrations. */
|
|
1068
1037
|
get suiClient() {
|
|
1069
1038
|
return this.client;
|
|
@@ -1081,24 +1050,23 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1081
1050
|
if (!(asset in SUPPORTED_ASSETS)) {
|
|
1082
1051
|
throw new T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
|
|
1083
1052
|
}
|
|
1084
|
-
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
});
|
|
1053
|
+
const sendAmount = params.amount;
|
|
1054
|
+
const sendTo = params.to;
|
|
1055
|
+
const gasResult = await executeWithGas(
|
|
1056
|
+
this.client,
|
|
1057
|
+
this.keypair,
|
|
1058
|
+
() => buildSendTx({ client: this.client, address: this._address, to: sendTo, amount: sendAmount, asset })
|
|
1059
|
+
);
|
|
1092
1060
|
const balance = await this.balance();
|
|
1093
|
-
this.emitBalanceChange(asset,
|
|
1061
|
+
this.emitBalanceChange(asset, sendAmount, "send", gasResult.digest);
|
|
1094
1062
|
return {
|
|
1095
1063
|
success: true,
|
|
1096
|
-
tx:
|
|
1097
|
-
amount:
|
|
1064
|
+
tx: gasResult.digest,
|
|
1065
|
+
amount: sendAmount,
|
|
1098
1066
|
to: params.to,
|
|
1099
|
-
gasCost:
|
|
1067
|
+
gasCost: gasResult.gasCostSui,
|
|
1100
1068
|
gasCostUnit: "SUI",
|
|
1101
|
-
gasMethod:
|
|
1069
|
+
gasMethod: gasResult.gasMethod,
|
|
1102
1070
|
balance
|
|
1103
1071
|
};
|
|
1104
1072
|
}
|
|
@@ -1154,11 +1122,24 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1154
1122
|
amount = params.amount;
|
|
1155
1123
|
}
|
|
1156
1124
|
const fee = calculateFee("save", amount);
|
|
1157
|
-
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1125
|
+
const saveAmount = amount;
|
|
1126
|
+
const gasResult = await executeWithGas(
|
|
1127
|
+
this.client,
|
|
1128
|
+
this.keypair,
|
|
1129
|
+
() => buildSaveTx(this.client, this._address, saveAmount)
|
|
1130
|
+
);
|
|
1131
|
+
const rates = await getRates(this.client);
|
|
1132
|
+
reportFee(this._address, "save", fee.amount, fee.rate, gasResult.digest);
|
|
1133
|
+
this.emitBalanceChange("USDC", saveAmount, "save", gasResult.digest);
|
|
1134
|
+
return {
|
|
1135
|
+
success: true,
|
|
1136
|
+
tx: gasResult.digest,
|
|
1137
|
+
amount: saveAmount,
|
|
1138
|
+
apy: rates.USDC.saveApy,
|
|
1139
|
+
fee: fee.amount,
|
|
1140
|
+
gasCost: gasResult.gasCostSui,
|
|
1141
|
+
gasMethod: gasResult.gasMethod
|
|
1142
|
+
};
|
|
1162
1143
|
}
|
|
1163
1144
|
async withdraw(params) {
|
|
1164
1145
|
let amount;
|
|
@@ -1186,10 +1167,21 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1186
1167
|
}
|
|
1187
1168
|
}
|
|
1188
1169
|
}
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
this.
|
|
1192
|
-
|
|
1170
|
+
const withdrawAmount = amount;
|
|
1171
|
+
let effectiveAmount = withdrawAmount;
|
|
1172
|
+
const gasResult = await executeWithGas(this.client, this.keypair, async () => {
|
|
1173
|
+
const built = await buildWithdrawTx(this.client, this._address, withdrawAmount);
|
|
1174
|
+
effectiveAmount = built.effectiveAmount;
|
|
1175
|
+
return built.tx;
|
|
1176
|
+
});
|
|
1177
|
+
this.emitBalanceChange("USDC", effectiveAmount, "withdraw", gasResult.digest);
|
|
1178
|
+
return {
|
|
1179
|
+
success: true,
|
|
1180
|
+
tx: gasResult.digest,
|
|
1181
|
+
amount: effectiveAmount,
|
|
1182
|
+
gasCost: gasResult.gasCostSui,
|
|
1183
|
+
gasMethod: gasResult.gasMethod
|
|
1184
|
+
};
|
|
1193
1185
|
}
|
|
1194
1186
|
async maxWithdraw() {
|
|
1195
1187
|
return maxWithdrawAmount(this.client, this.keypair);
|
|
@@ -1204,27 +1196,52 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1204
1196
|
});
|
|
1205
1197
|
}
|
|
1206
1198
|
const fee = calculateFee("borrow", params.amount);
|
|
1207
|
-
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1199
|
+
const borrowAmount = params.amount;
|
|
1200
|
+
const gasResult = await executeWithGas(
|
|
1201
|
+
this.client,
|
|
1202
|
+
this.keypair,
|
|
1203
|
+
() => buildBorrowTx(this.client, this._address, borrowAmount)
|
|
1204
|
+
);
|
|
1205
|
+
const hf = await getHealthFactor(this.client, this.keypair);
|
|
1206
|
+
reportFee(this._address, "borrow", fee.amount, fee.rate, gasResult.digest);
|
|
1207
|
+
this.emitBalanceChange("USDC", borrowAmount, "borrow", gasResult.digest);
|
|
1208
|
+
return {
|
|
1209
|
+
success: true,
|
|
1210
|
+
tx: gasResult.digest,
|
|
1211
|
+
amount: borrowAmount,
|
|
1212
|
+
fee: fee.amount,
|
|
1213
|
+
healthFactor: hf.healthFactor,
|
|
1214
|
+
gasCost: gasResult.gasCostSui,
|
|
1215
|
+
gasMethod: gasResult.gasMethod
|
|
1216
|
+
};
|
|
1212
1217
|
}
|
|
1213
1218
|
async repay(params) {
|
|
1214
1219
|
let amount;
|
|
1215
1220
|
if (params.amount === "all") {
|
|
1216
|
-
const
|
|
1217
|
-
amount =
|
|
1221
|
+
const hf2 = await this.healthFactor();
|
|
1222
|
+
amount = hf2.borrowed;
|
|
1218
1223
|
if (amount <= 0) {
|
|
1219
1224
|
throw new T2000Error("NO_COLLATERAL", "No outstanding borrow to repay");
|
|
1220
1225
|
}
|
|
1221
1226
|
} else {
|
|
1222
1227
|
amount = params.amount;
|
|
1223
1228
|
}
|
|
1224
|
-
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1227
|
-
|
|
1229
|
+
const repayAmount = amount;
|
|
1230
|
+
const gasResult = await executeWithGas(
|
|
1231
|
+
this.client,
|
|
1232
|
+
this.keypair,
|
|
1233
|
+
() => buildRepayTx(this.client, this._address, repayAmount)
|
|
1234
|
+
);
|
|
1235
|
+
const hf = await getHealthFactor(this.client, this.keypair);
|
|
1236
|
+
this.emitBalanceChange("USDC", repayAmount, "repay", gasResult.digest);
|
|
1237
|
+
return {
|
|
1238
|
+
success: true,
|
|
1239
|
+
tx: gasResult.digest,
|
|
1240
|
+
amount: repayAmount,
|
|
1241
|
+
remainingDebt: hf.borrowed,
|
|
1242
|
+
gasCost: gasResult.gasCostSui,
|
|
1243
|
+
gasMethod: gasResult.gasMethod
|
|
1244
|
+
};
|
|
1228
1245
|
}
|
|
1229
1246
|
async maxBorrow() {
|
|
1230
1247
|
return maxBorrowAmount(this.client, this.keypair);
|
|
@@ -1249,28 +1266,51 @@ var T2000 = class _T2000 extends EventEmitter {
|
|
|
1249
1266
|
throw new T2000Error("INVALID_AMOUNT", "Cannot swap same asset");
|
|
1250
1267
|
}
|
|
1251
1268
|
const fee = calculateFee("swap", params.amount);
|
|
1252
|
-
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1269
|
+
const swapAmount = params.amount;
|
|
1270
|
+
const slippageBps = params.maxSlippage ? params.maxSlippage * 100 : void 0;
|
|
1271
|
+
let swapMeta = { estimatedOut: 0, toDecimals: 0 };
|
|
1272
|
+
const gasResult = await executeWithGas(this.client, this.keypair, async () => {
|
|
1273
|
+
const built = await buildSwapTx({
|
|
1274
|
+
client: this.client,
|
|
1275
|
+
address: this._address,
|
|
1276
|
+
fromAsset,
|
|
1277
|
+
toAsset,
|
|
1278
|
+
amount: swapAmount,
|
|
1279
|
+
maxSlippageBps: slippageBps
|
|
1280
|
+
});
|
|
1281
|
+
swapMeta = { estimatedOut: built.estimatedOut, toDecimals: built.toDecimals };
|
|
1282
|
+
return built.tx;
|
|
1260
1283
|
});
|
|
1261
|
-
|
|
1262
|
-
|
|
1284
|
+
const toInfo = SUPPORTED_ASSETS[toAsset];
|
|
1285
|
+
const txDetail = await this.client.getTransactionBlock({
|
|
1286
|
+
digest: gasResult.digest,
|
|
1287
|
+
options: { showBalanceChanges: true }
|
|
1288
|
+
});
|
|
1289
|
+
let actualReceived = 0;
|
|
1290
|
+
if (txDetail.balanceChanges) {
|
|
1291
|
+
for (const change of txDetail.balanceChanges) {
|
|
1292
|
+
if (change.coinType === toInfo.type && change.owner && typeof change.owner === "object" && "AddressOwner" in change.owner && change.owner.AddressOwner === this._address) {
|
|
1293
|
+
const amt = Number(change.amount) / 10 ** toInfo.decimals;
|
|
1294
|
+
if (amt > 0) actualReceived += amt;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
const expectedOutput = swapMeta.estimatedOut / 10 ** swapMeta.toDecimals;
|
|
1299
|
+
if (actualReceived === 0) actualReceived = expectedOutput;
|
|
1300
|
+
const priceImpact = expectedOutput > 0 ? Math.abs(actualReceived - expectedOutput) / expectedOutput : 0;
|
|
1301
|
+
reportFee(this._address, "swap", fee.amount, fee.rate, gasResult.digest);
|
|
1302
|
+
this.emitBalanceChange(fromAsset, swapAmount, "swap", gasResult.digest);
|
|
1263
1303
|
return {
|
|
1264
1304
|
success: true,
|
|
1265
|
-
tx:
|
|
1266
|
-
fromAmount:
|
|
1267
|
-
fromAsset
|
|
1268
|
-
toAmount:
|
|
1269
|
-
toAsset
|
|
1270
|
-
priceImpact
|
|
1305
|
+
tx: gasResult.digest,
|
|
1306
|
+
fromAmount: swapAmount,
|
|
1307
|
+
fromAsset,
|
|
1308
|
+
toAmount: actualReceived,
|
|
1309
|
+
toAsset,
|
|
1310
|
+
priceImpact,
|
|
1271
1311
|
fee: fee.amount,
|
|
1272
|
-
gasCost:
|
|
1273
|
-
gasMethod:
|
|
1312
|
+
gasCost: gasResult.gasCostSui,
|
|
1313
|
+
gasMethod: gasResult.gasMethod
|
|
1274
1314
|
};
|
|
1275
1315
|
}
|
|
1276
1316
|
async swapQuote(params) {
|
|
@@ -1401,115 +1441,6 @@ function parseMoveAbort(errorStr) {
|
|
|
1401
1441
|
return { reason: errorStr };
|
|
1402
1442
|
}
|
|
1403
1443
|
|
|
1404
|
-
// src/gas/manager.ts
|
|
1405
|
-
function extractGasCost3(effects) {
|
|
1406
|
-
if (!effects?.gasUsed) return 0;
|
|
1407
|
-
return (Number(effects.gasUsed.computationCost) + Number(effects.gasUsed.storageCost) - Number(effects.gasUsed.storageRebate)) / 1e9;
|
|
1408
|
-
}
|
|
1409
|
-
async function getSuiBalance(client, address) {
|
|
1410
|
-
const bal = await client.getBalance({ owner: address, coinType: SUPPORTED_ASSETS.SUI.type });
|
|
1411
|
-
return BigInt(bal.totalBalance);
|
|
1412
|
-
}
|
|
1413
|
-
async function trySelfFunded(client, keypair, tx) {
|
|
1414
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
1415
|
-
const suiBalance = await getSuiBalance(client, address);
|
|
1416
|
-
if (suiBalance < AUTO_TOPUP_THRESHOLD) return null;
|
|
1417
|
-
tx.setSender(address);
|
|
1418
|
-
const result = await client.signAndExecuteTransaction({
|
|
1419
|
-
signer: keypair,
|
|
1420
|
-
transaction: tx,
|
|
1421
|
-
options: { showEffects: true }
|
|
1422
|
-
});
|
|
1423
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
1424
|
-
return {
|
|
1425
|
-
digest: result.digest,
|
|
1426
|
-
effects: result.effects,
|
|
1427
|
-
gasMethod: "self-funded",
|
|
1428
|
-
gasCostSui: extractGasCost3(result.effects)
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
async function tryAutoTopUpThenSelfFund(client, keypair, tx) {
|
|
1432
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
1433
|
-
const canTopUp = await shouldAutoTopUp(client, address);
|
|
1434
|
-
if (!canTopUp) return null;
|
|
1435
|
-
try {
|
|
1436
|
-
await executeAutoTopUp(client, keypair);
|
|
1437
|
-
} catch {
|
|
1438
|
-
return null;
|
|
1439
|
-
}
|
|
1440
|
-
tx.setSender(address);
|
|
1441
|
-
const result = await client.signAndExecuteTransaction({
|
|
1442
|
-
signer: keypair,
|
|
1443
|
-
transaction: tx,
|
|
1444
|
-
options: { showEffects: true }
|
|
1445
|
-
});
|
|
1446
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
1447
|
-
return {
|
|
1448
|
-
digest: result.digest,
|
|
1449
|
-
effects: result.effects,
|
|
1450
|
-
gasMethod: "auto-topup",
|
|
1451
|
-
gasCostSui: extractGasCost3(result.effects)
|
|
1452
|
-
};
|
|
1453
|
-
}
|
|
1454
|
-
async function trySponsored(client, keypair, tx) {
|
|
1455
|
-
const address = keypair.getPublicKey().toSuiAddress();
|
|
1456
|
-
tx.setSender(address);
|
|
1457
|
-
let txBytes;
|
|
1458
|
-
try {
|
|
1459
|
-
txBytes = await tx.build({ client, onlyTransactionKind: true });
|
|
1460
|
-
} catch {
|
|
1461
|
-
return null;
|
|
1462
|
-
}
|
|
1463
|
-
const txBytesBase64 = Buffer.from(txBytes).toString("base64");
|
|
1464
|
-
let sponsoredResult;
|
|
1465
|
-
try {
|
|
1466
|
-
sponsoredResult = await requestGasSponsorship(txBytesBase64, address);
|
|
1467
|
-
} catch {
|
|
1468
|
-
return null;
|
|
1469
|
-
}
|
|
1470
|
-
const sponsoredTxBytes = Buffer.from(sponsoredResult.txBytes, "base64");
|
|
1471
|
-
const { signature: agentSig } = await keypair.signTransaction(sponsoredTxBytes);
|
|
1472
|
-
const result = await client.executeTransactionBlock({
|
|
1473
|
-
transactionBlock: sponsoredResult.txBytes,
|
|
1474
|
-
signature: [agentSig, sponsoredResult.sponsorSignature],
|
|
1475
|
-
options: { showEffects: true }
|
|
1476
|
-
});
|
|
1477
|
-
await client.waitForTransaction({ digest: result.digest });
|
|
1478
|
-
const gasCost = extractGasCost3(result.effects);
|
|
1479
|
-
reportGasUsage(address, result.digest, gasCost, 0, sponsoredResult.type);
|
|
1480
|
-
return {
|
|
1481
|
-
digest: result.digest,
|
|
1482
|
-
effects: result.effects,
|
|
1483
|
-
gasMethod: "sponsored",
|
|
1484
|
-
gasCostSui: gasCost
|
|
1485
|
-
};
|
|
1486
|
-
}
|
|
1487
|
-
async function executeWithGas(client, keypair, buildTx) {
|
|
1488
|
-
try {
|
|
1489
|
-
const tx = await buildTx();
|
|
1490
|
-
const result = await trySelfFunded(client, keypair, tx);
|
|
1491
|
-
if (result) return result;
|
|
1492
|
-
} catch {
|
|
1493
|
-
}
|
|
1494
|
-
try {
|
|
1495
|
-
const tx = await buildTx();
|
|
1496
|
-
const result = await tryAutoTopUpThenSelfFund(client, keypair, tx);
|
|
1497
|
-
if (result) return result;
|
|
1498
|
-
} catch {
|
|
1499
|
-
}
|
|
1500
|
-
try {
|
|
1501
|
-
const tx = await buildTx();
|
|
1502
|
-
const result = await trySponsored(client, keypair, tx);
|
|
1503
|
-
if (result) return result;
|
|
1504
|
-
} catch {
|
|
1505
|
-
}
|
|
1506
|
-
throw new T2000Error(
|
|
1507
|
-
"INSUFFICIENT_GAS",
|
|
1508
|
-
"No SUI for gas and Gas Station unavailable. Fund your wallet with SUI or USDC.",
|
|
1509
|
-
{ reason: "all_gas_methods_exhausted" }
|
|
1510
|
-
);
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
1444
|
export { BPS_DENOMINATOR, CLOCK_ID, DEFAULT_NETWORK, MIST_PER_SUI, SUI_DECIMALS, SUPPORTED_ASSETS, T2000, T2000Error, USDC_DECIMALS, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatSui, formatUsd, generateKeypair, getAddress, getGasStatus, getPoolPrice, getRates, getSwapQuote, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
|
|
1514
1445
|
//# sourceMappingURL=index.js.map
|
|
1515
1446
|
//# sourceMappingURL=index.js.map
|