@t2000/sdk 0.50.3 → 0.51.1

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/dist/index.cjs CHANGED
@@ -876,8 +876,8 @@ var SUPPORTED_ASSETS = {
876
876
  var STABLE_ASSETS = ["USDC"];
877
877
  var ALL_NAVI_ASSETS = Object.keys(SUPPORTED_ASSETS);
878
878
  var OPERATION_ASSETS = {
879
- save: ["USDC"],
880
- borrow: ["USDC"],
879
+ save: ["USDC", "USDsui"],
880
+ borrow: ["USDC", "USDsui"],
881
881
  withdraw: "*",
882
882
  repay: "*",
883
883
  send: "*",
@@ -886,7 +886,8 @@ var OPERATION_ASSETS = {
886
886
  function isAllowedAsset(op, asset) {
887
887
  const allowed = OPERATION_ASSETS[op];
888
888
  if (allowed === "*") return true;
889
- return allowed.includes(asset.toUpperCase());
889
+ const target = asset.toLowerCase();
890
+ return allowed.some((a) => a.toLowerCase() === target);
890
891
  }
891
892
  function assertAllowedAsset(op, asset) {
892
893
  if (!asset) return;
@@ -895,7 +896,7 @@ function assertAllowedAsset(op, asset) {
895
896
  const list = Array.isArray(allowed) ? allowed.join(", ") : "any";
896
897
  throw new exports.T2000Error(
897
898
  "INVALID_ASSET",
898
- `${op} only supports ${list}. Cannot use ${asset}.${op === "save" ? " Swap to USDC first." : ""}`
899
+ `${op} only supports ${list}. Cannot use ${asset}.${op === "save" ? " Swap to USDC or USDsui first." : ""}`
899
900
  );
900
901
  }
901
902
  }
@@ -6815,23 +6816,26 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6815
6816
  async save(params) {
6816
6817
  this.enforcer.assertNotLocked();
6817
6818
  assertAllowedAsset("save", params.asset);
6818
- const asset = "USDC";
6819
+ const asset = params.asset ?? "USDC";
6819
6820
  const assetInfo = SUPPORTED_ASSETS[asset];
6820
6821
  let amount;
6821
6822
  if (params.amount === "all") {
6822
- const bal = await queryBalance(this.client, this._address);
6823
- amount = (bal.available ?? 0) - 1;
6823
+ const assetBalance = await this._queryAssetBalance(assetInfo.type, assetInfo.decimals);
6824
+ amount = assetBalance - 1;
6824
6825
  if (amount <= 0) {
6825
- throw new exports.T2000Error("INSUFFICIENT_BALANCE", `No USDC available to save`, {
6826
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `No ${assetInfo.displayName} available to save`, {
6826
6827
  reason: "insufficient_balance",
6827
6828
  asset
6828
6829
  });
6829
6830
  }
6830
6831
  } else {
6831
6832
  amount = params.amount;
6832
- const bal = await queryBalance(this.client, this._address);
6833
- if (amount > (bal.available ?? 0)) {
6834
- throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance. Available: $${(bal.available ?? 0).toFixed(2)}, requested: $${amount.toFixed(2)}`);
6833
+ const assetBalance = await this._queryAssetBalance(assetInfo.type, assetInfo.decimals);
6834
+ if (amount > assetBalance) {
6835
+ throw new exports.T2000Error(
6836
+ "INSUFFICIENT_BALANCE",
6837
+ `Insufficient ${assetInfo.displayName} balance. Available: ${assetBalance.toFixed(assetInfo.decimals === 6 ? 2 : 4)}, requested: ${amount.toFixed(assetInfo.decimals === 6 ? 2 : 4)}`
6838
+ );
6835
6839
  }
6836
6840
  }
6837
6841
  const fee = calculateFee("save", amount);
@@ -6843,7 +6847,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
6843
6847
  const tx2 = new transactions.Transaction();
6844
6848
  tx2.setSender(this._address);
6845
6849
  const coins = await this._fetchCoins(assetInfo.type);
6846
- if (coins.length === 0) throw new exports.T2000Error("INSUFFICIENT_BALANCE", "No USDC coins found");
6850
+ if (coins.length === 0) throw new exports.T2000Error("INSUFFICIENT_BALANCE", `No ${assetInfo.displayName} coins found`);
6847
6851
  const merged = this._mergeCoinsInTx(tx2, coins);
6848
6852
  const rawAmount = BigInt(Math.floor(saveAmount * 10 ** assetInfo.decimals));
6849
6853
  const [inputCoin] = tx2.splitCoins(merged, [rawAmount]);
@@ -7027,6 +7031,24 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7027
7031
  gasMethod: gasResult.gasMethod
7028
7032
  };
7029
7033
  }
7034
+ /**
7035
+ * [v0.51.0] Per-asset wallet balance lookup.
7036
+ *
7037
+ * `queryBalance.available` rolls up only `STABLE_ASSETS` (USDC), so it cannot
7038
+ * answer "do they have enough USDsui to save 10?". This helper hits
7039
+ * `getBalance` for the specific coin type — same mechanism `queryBalance`
7040
+ * uses internally — and converts to a human-readable amount using the asset's
7041
+ * decimals. Returns 0 (not a throw) when the address holds none of the asset,
7042
+ * matching the caller's existing "insufficient balance" error path.
7043
+ */
7044
+ async _queryAssetBalance(coinType, decimals) {
7045
+ try {
7046
+ const bal = await this.client.getBalance({ owner: this._address, coinType });
7047
+ return Number(bal.totalBalance) / 10 ** decimals;
7048
+ } catch {
7049
+ return 0;
7050
+ }
7051
+ }
7030
7052
  async _fetchCoins(coinType) {
7031
7053
  const all = [];
7032
7054
  let cursor;
@@ -7109,7 +7131,8 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7109
7131
  // -- Borrowing --
7110
7132
  async borrow(params) {
7111
7133
  this.enforcer.assertNotLocked();
7112
- const asset = "USDC";
7134
+ assertAllowedAsset("borrow", params.asset);
7135
+ const asset = params.asset ?? "USDC";
7113
7136
  const adapter = await this.resolveLending(params.protocol, asset, "borrow");
7114
7137
  const maxResult = await adapter.maxBorrow(this._address, asset);
7115
7138
  if (maxResult.maxAmount <= 0) {
@@ -7134,6 +7157,7 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7134
7157
  success: true,
7135
7158
  tx: gasResult.digest,
7136
7159
  amount: borrowAmount,
7160
+ asset,
7137
7161
  fee: fee.amount,
7138
7162
  healthFactor: hf.healthFactor,
7139
7163
  gasCost: gasResult.gasCostSui,
@@ -7156,19 +7180,24 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7156
7180
  if (params.amount === "all") {
7157
7181
  return this._repayAllBorrows(borrows);
7158
7182
  }
7159
- borrows.sort((a, b2) => b2.apy - a.apy);
7160
- const target = borrows[0];
7183
+ const eligible = params.asset ? borrows.filter((b2) => b2.asset === params.asset) : borrows;
7184
+ if (eligible.length === 0) {
7185
+ throw new exports.T2000Error("NO_COLLATERAL", `No outstanding ${params.asset} borrow to repay`);
7186
+ }
7187
+ eligible.sort((a, b2) => b2.apy - a.apy);
7188
+ const target = eligible[0];
7161
7189
  const adapter = this.registry.getLending(target.protocolId);
7162
7190
  if (!adapter) throw new exports.T2000Error("PROTOCOL_UNAVAILABLE", `Protocol ${target.protocolId} not found`);
7163
7191
  const repayAmount = Math.min(params.amount, target.amount);
7192
+ const targetAssetInfo = SUPPORTED_ASSETS[target.asset] ?? SUPPORTED_ASSETS.USDC;
7164
7193
  const gasResult = await executeWithGas(this.client, this._signer, async () => {
7165
7194
  if (adapter.addRepayToTx) {
7166
7195
  const tx2 = new transactions.Transaction();
7167
7196
  tx2.setSender(this._address);
7168
- const usdcCoins = await this._fetchCoins(SUPPORTED_ASSETS.USDC.type);
7169
- if (usdcCoins.length === 0) throw new exports.T2000Error("INSUFFICIENT_BALANCE", "No USDC coins");
7170
- const merged = this._mergeCoinsInTx(tx2, usdcCoins);
7171
- const raw = BigInt(Math.floor(repayAmount * 10 ** SUPPORTED_ASSETS.USDC.decimals));
7197
+ const coins = await this._fetchCoins(targetAssetInfo.type);
7198
+ if (coins.length === 0) throw new exports.T2000Error("INSUFFICIENT_BALANCE", `No ${targetAssetInfo.displayName} coins`);
7199
+ const merged = this._mergeCoinsInTx(tx2, coins);
7200
+ const raw = BigInt(Math.floor(repayAmount * 10 ** targetAssetInfo.decimals));
7172
7201
  const [repayCoin] = tx2.splitCoins(merged, [raw]);
7173
7202
  await adapter.addRepayToTx(tx2, this._address, repayCoin, target.asset);
7174
7203
  return tx2;
@@ -7177,11 +7206,12 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7177
7206
  return tx;
7178
7207
  });
7179
7208
  const hf = await adapter.getHealth(this._address);
7180
- this.emitBalanceChange("USDC", repayAmount, "repay", gasResult.digest);
7209
+ this.emitBalanceChange(target.asset, repayAmount, "repay", gasResult.digest);
7181
7210
  return {
7182
7211
  success: true,
7183
7212
  tx: gasResult.digest,
7184
7213
  amount: repayAmount,
7214
+ asset: target.asset,
7185
7215
  remainingDebt: hf.borrowed,
7186
7216
  gasCost: gasResult.gasCostSui,
7187
7217
  gasMethod: gasResult.gasMethod
@@ -7200,15 +7230,20 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7200
7230
  if (canPTB) {
7201
7231
  const tx = new transactions.Transaction();
7202
7232
  tx.setSender(this._address);
7203
- const usdcCoins = await this._fetchCoins(SUPPORTED_ASSETS.USDC.type);
7204
- let usdcMerged;
7205
- if (usdcCoins.length > 0) {
7206
- usdcMerged = this._mergeCoinsInTx(tx, usdcCoins);
7233
+ const assetMerged = /* @__PURE__ */ new Map();
7234
+ const uniqueAssets = Array.from(new Set(entries.map((e) => e.borrow.asset)));
7235
+ for (const asset of uniqueAssets) {
7236
+ const info = SUPPORTED_ASSETS[asset];
7237
+ if (!info) throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Cannot repay unknown asset: ${asset}`);
7238
+ const coins = await this._fetchCoins(info.type);
7239
+ if (coins.length === 0) throw new exports.T2000Error("INSUFFICIENT_BALANCE", `No ${info.displayName} coins for repayment`);
7240
+ assetMerged.set(asset, this._mergeCoinsInTx(tx, coins));
7207
7241
  }
7208
7242
  for (const { borrow, adapter } of entries) {
7209
- const raw = BigInt(Math.floor(borrow.amount * 10 ** SUPPORTED_ASSETS.USDC.decimals));
7210
- if (!usdcMerged) throw new exports.T2000Error("INSUFFICIENT_BALANCE", "No USDC for repayment");
7211
- const [repayCoin] = tx.splitCoins(usdcMerged, [raw]);
7243
+ const info = SUPPORTED_ASSETS[borrow.asset];
7244
+ const merged = assetMerged.get(borrow.asset);
7245
+ const raw = BigInt(Math.floor(borrow.amount * 10 ** info.decimals));
7246
+ const [repayCoin] = tx.splitCoins(merged, [raw]);
7212
7247
  await adapter.addRepayToTx(tx, this._address, repayCoin, borrow.asset);
7213
7248
  totalRepaid += borrow.amount;
7214
7249
  }
@@ -7224,11 +7259,13 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
7224
7259
  });
7225
7260
  const firstAdapter = entries[0]?.adapter;
7226
7261
  const hf = firstAdapter ? await firstAdapter.getHealth(this._address) : { borrowed: 0 };
7227
- this.emitBalanceChange("USDC", totalRepaid, "repay", gasResult.digest);
7262
+ const dominantAsset = entries.reduce((acc, e) => acc.amount >= e.borrow.amount ? acc : e.borrow, entries[0]?.borrow ?? { asset: "USDC", amount: 0 }).asset;
7263
+ this.emitBalanceChange(dominantAsset, totalRepaid, "repay", gasResult.digest);
7228
7264
  return {
7229
7265
  success: true,
7230
7266
  tx: gasResult.digest,
7231
7267
  amount: totalRepaid,
7268
+ asset: dominantAsset,
7232
7269
  remainingDebt: hf.borrowed,
7233
7270
  gasCost: gasResult.gasCostSui,
7234
7271
  gasMethod: gasResult.gasMethod