@t2000/sdk 5.5.1 → 5.6.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
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var utils = require('@mysten/sui/utils');
3
4
  var transactions = require('@mysten/sui/transactions');
4
5
  var aggregatorSdk = require('@cetusprotocol/aggregator-sdk');
5
6
  var BN = require('bn.js');
@@ -7,7 +8,6 @@ var eventemitter3 = require('eventemitter3');
7
8
  var paymentKit = require('@mysten/payment-kit');
8
9
  var grpc = require('@mysten/sui/grpc');
9
10
  var graphql = require('@mysten/sui/graphql');
10
- var utils = require('@mysten/sui/utils');
11
11
  var ed25519 = require('@mysten/sui/keypairs/ed25519');
12
12
  var cryptography = require('@mysten/sui/cryptography');
13
13
  var promises = require('fs/promises');
@@ -118,6 +118,37 @@ var init_errors = __esm({
118
118
  };
119
119
  }
120
120
  });
121
+ function preflightFail(code, error) {
122
+ return { valid: false, code, error };
123
+ }
124
+ function checkPositiveAmount(amount, label = "Amount", max = exports.PREFLIGHT_MAX_AMOUNT) {
125
+ if (typeof amount !== "number" || !Number.isFinite(amount)) {
126
+ return preflightFail("INVALID_AMOUNT", `${label} must be a finite number`);
127
+ }
128
+ if (amount <= 0) {
129
+ return preflightFail("INVALID_AMOUNT", `${label} must be greater than zero`);
130
+ }
131
+ if (amount > max) {
132
+ return preflightFail("INVALID_AMOUNT", `${label} ${amount} exceeds the sane maximum (${max})`);
133
+ }
134
+ return exports.PREFLIGHT_OK;
135
+ }
136
+ function checkSuiAddress(address, label = "recipient") {
137
+ try {
138
+ if (typeof address === "string" && address.trim() !== "" && utils.isValidSuiAddress(utils.normalizeSuiAddress(address))) {
139
+ return exports.PREFLIGHT_OK;
140
+ }
141
+ } catch {
142
+ }
143
+ return preflightFail("INVALID_ADDRESS", `Invalid ${label} address: ${address}`);
144
+ }
145
+ exports.PREFLIGHT_MAX_AMOUNT = void 0; exports.PREFLIGHT_OK = void 0;
146
+ var init_preflight = __esm({
147
+ "src/preflight.ts"() {
148
+ exports.PREFLIGHT_MAX_AMOUNT = 1e6;
149
+ exports.PREFLIGHT_OK = { valid: true };
150
+ }
151
+ });
121
152
 
122
153
  // src/token-registry.ts
123
154
  var token_registry_exports = {};
@@ -244,6 +275,7 @@ var init_token_registry = __esm({
244
275
  // src/wallet/coinSelection.ts
245
276
  var coinSelection_exports = {};
246
277
  __export(coinSelection_exports, {
278
+ buildCoinToAddressBalanceMigration: () => buildCoinToAddressBalanceMigration,
247
279
  fetchAllCoins: () => fetchAllCoins,
248
280
  selectAndSplitCoin: () => selectAndSplitCoin,
249
281
  selectSuiCoin: () => selectSuiCoin
@@ -357,6 +389,32 @@ async function selectCoinObjectsOnly(tx, client, owner, coinType, amount, allowS
357
389
  });
358
390
  return { coin, effectiveAmount, swapAll };
359
391
  }
392
+ function buildCoinToAddressBalanceMigration(args) {
393
+ const { coins, coinType, owner, minAmount } = args;
394
+ const sorted = [...coins].filter((c) => c.balance > 0n).sort((a, b) => b.balance > a.balance ? 1 : b.balance < a.balance ? -1 : 0);
395
+ const selected = [];
396
+ let migratedRaw = 0n;
397
+ for (const c of sorted) {
398
+ if (migratedRaw >= minAmount) break;
399
+ selected.push(c);
400
+ migratedRaw += c.balance;
401
+ }
402
+ if (migratedRaw < minAmount) {
403
+ throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${coinType} coin objects to migrate`, {
404
+ available: migratedRaw.toString(),
405
+ required: minAmount.toString()
406
+ });
407
+ }
408
+ const tx = new transactions.Transaction();
409
+ for (const c of selected) {
410
+ tx.moveCall({
411
+ target: "0x2::coin::send_funds",
412
+ typeArguments: [coinType],
413
+ arguments: [tx.object(c.objectId), tx.pure.address(owner)]
414
+ });
415
+ }
416
+ return { tx, migratedRaw };
417
+ }
360
418
  async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext, mergeCache) {
361
419
  if (sponsoredContext) {
362
420
  const { SUI_TYPE: SUI_TYPE2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
@@ -382,10 +440,28 @@ __export(cetus_swap_exports, {
382
440
  findSwapRoute: () => findSwapRoute,
383
441
  isCetusRouteFresh: () => isCetusRouteFresh,
384
442
  isPrecomputedRouteCompatibleWithProviders: () => isPrecomputedRouteCompatibleWithProviders,
443
+ preflightSwap: () => preflightSwap,
385
444
  resolveTokenType: () => resolveTokenType,
386
445
  serializeCetusRoute: () => serializeCetusRoute,
387
446
  verifyCetusRouteCoinMatch: () => verifyCetusRouteCoinMatch
388
447
  });
448
+ function preflightSwap(input) {
449
+ if (typeof input.from !== "string" || input.from.trim() === "") {
450
+ return preflightFail("INVALID_ASSET", "A `from` token is required to swap");
451
+ }
452
+ if (typeof input.to !== "string" || input.to.trim() === "") {
453
+ return preflightFail("INVALID_ASSET", "A `to` token is required to swap");
454
+ }
455
+ const amountCheck = checkPositiveAmount(input.amount, "Amount", Number.POSITIVE_INFINITY);
456
+ if (!amountCheck.valid) return amountCheck;
457
+ const resolvedFrom = resolveTokenType(input.from);
458
+ const resolvedTo = resolveTokenType(input.to);
459
+ const sameToken = input.from.trim() === input.to.trim() || resolvedFrom !== null && resolvedFrom === resolvedTo;
460
+ if (sameToken) {
461
+ return preflightFail("INVALID_ASSET", `Cannot swap ${input.from} to itself`);
462
+ }
463
+ return exports.PREFLIGHT_OK;
464
+ }
389
465
  function serializeCetusRoute(route, context) {
390
466
  return {
391
467
  routerData: serializeRouterDataV3(route.routerData),
@@ -637,6 +713,7 @@ exports.OVERLAY_FEE_RATE = void 0; var clientCache;
637
713
  var init_cetus_swap = __esm({
638
714
  "src/protocols/cetus-swap.ts"() {
639
715
  init_token_registry();
716
+ init_preflight();
640
717
  init_token_registry();
641
718
  exports.OVERLAY_FEE_RATE = 1e-3;
642
719
  clientCache = /* @__PURE__ */ new Map();
@@ -1010,8 +1087,33 @@ async function executeTx(client, signer, buildTx, options = {}) {
1010
1087
 
1011
1088
  // src/wallet/pay.ts
1012
1089
  init_errors();
1090
+ init_preflight();
1091
+ function preflightPay(input) {
1092
+ if (typeof input.url !== "string" || input.url.trim() === "") {
1093
+ return preflightFail("FACILITATOR_REJECTION", "A target URL is required to pay");
1094
+ }
1095
+ let parsed;
1096
+ try {
1097
+ parsed = new URL(input.url);
1098
+ } catch {
1099
+ return preflightFail("FACILITATOR_REJECTION", `Invalid URL: ${input.url}`);
1100
+ }
1101
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
1102
+ return preflightFail(
1103
+ "FACILITATOR_REJECTION",
1104
+ `URL must be http(s): got ${parsed.protocol}//`
1105
+ );
1106
+ }
1107
+ if (input.maxPrice !== void 0) {
1108
+ const priceCheck = checkPositiveAmount(input.maxPrice, "maxPrice");
1109
+ if (!priceCheck.valid) return priceCheck;
1110
+ }
1111
+ return exports.PREFLIGHT_OK;
1112
+ }
1013
1113
  async function payWithMpp(args) {
1014
1114
  const { signer, client, options } = args;
1115
+ const pf = preflightPay({ url: options.url, maxPrice: options.maxPrice });
1116
+ if (!pf.valid) throw new exports.T2000Error(pf.code, pf.error);
1015
1117
  const method = (options.method ?? "GET").toUpperCase();
1016
1118
  const canHaveBody = method !== "GET" && method !== "HEAD";
1017
1119
  const reqInit = {
@@ -1091,40 +1193,26 @@ async function ensureAddressBalanceCovers(args) {
1091
1193
  required: amountRaw.toString()
1092
1194
  });
1093
1195
  }
1196
+ const coins = [];
1094
1197
  let coinSum = 0n;
1095
1198
  let cursor;
1096
1199
  let hasNext = true;
1097
1200
  while (hasNext) {
1098
1201
  const page = await client.core.listCoins({ owner, coinType: asset, cursor: cursor ?? void 0 });
1099
- for (const c of page.objects) coinSum += BigInt(c.balance);
1202
+ for (const c of page.objects) {
1203
+ coins.push({ objectId: c.objectId, balance: BigInt(c.balance) });
1204
+ coinSum += BigInt(c.balance);
1205
+ }
1100
1206
  cursor = page.cursor;
1101
1207
  hasNext = page.hasNextPage;
1102
1208
  }
1103
1209
  const addressBalance = total - coinSum;
1104
1210
  if (addressBalance >= amountRaw) return 0;
1105
1211
  const shortfall = amountRaw - addressBalance;
1106
- const { Transaction: Transaction6 } = await import('@mysten/sui/transactions');
1107
- const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
1212
+ const { buildCoinToAddressBalanceMigration: buildCoinToAddressBalanceMigration2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
1108
1213
  const grpcClient = await makeGrpcBuildClient(client);
1109
- const migration = await executeTx(
1110
- client,
1111
- signer,
1112
- async () => {
1113
- const tx = new Transaction6();
1114
- const { coin } = await selectAndSplitCoin2(tx, client, owner, asset, shortfall, {
1115
- sponsoredContext: true,
1116
- // coin-objects-only — never the address balance
1117
- allowSwapAll: false
1118
- });
1119
- tx.moveCall({
1120
- target: "0x2::coin::send_funds",
1121
- typeArguments: [asset],
1122
- arguments: [coin, tx.pure.address(owner)]
1123
- });
1124
- return tx;
1125
- },
1126
- { buildClient: grpcClient }
1127
- );
1214
+ const { tx } = buildCoinToAddressBalanceMigration2({ coins, coinType: asset, owner, minAmount: shortfall });
1215
+ const migration = await executeTx(client, signer, () => tx, { buildClient: grpcClient });
1128
1216
  return migration.gasCostSui;
1129
1217
  }
1130
1218
  async function finalize(response, opts) {
@@ -1246,6 +1334,25 @@ function normalizeAsset(input) {
1246
1334
  }
1247
1335
 
1248
1336
  // src/wallet/send.ts
1337
+ init_preflight();
1338
+ function preflightSend(input) {
1339
+ try {
1340
+ assertAllowedAsset("send", input.asset);
1341
+ } catch (e) {
1342
+ return preflightFail("INVALID_ASSET", e.message);
1343
+ }
1344
+ const amountCheck = checkPositiveAmount(input.amount);
1345
+ if (!amountCheck.valid) return amountCheck;
1346
+ if ((input.asset === "USDC" || input.asset === "USDsui") && input.amount < GASLESS_MIN_STABLE_AMOUNT) {
1347
+ return preflightFail(
1348
+ "INVALID_AMOUNT",
1349
+ `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${input.asset}. Got ${input.amount}.`
1350
+ );
1351
+ }
1352
+ const addressCheck = checkSuiAddress(input.to);
1353
+ if (!addressCheck.valid) return addressCheck;
1354
+ return exports.PREFLIGHT_OK;
1355
+ }
1249
1356
  async function buildSendTx({
1250
1357
  client,
1251
1358
  address,
@@ -1253,17 +1360,11 @@ async function buildSendTx({
1253
1360
  amount,
1254
1361
  asset
1255
1362
  }) {
1256
- assertAllowedAsset("send", asset);
1363
+ const pf = preflightSend({ to, amount, asset });
1364
+ if (!pf.valid) throw new exports.T2000Error(pf.code, pf.error);
1257
1365
  const recipient = validateAddress(to);
1258
1366
  const assetInfo = SUPPORTED_ASSETS[asset];
1259
1367
  if (!assetInfo) throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Asset ${asset} is not supported`);
1260
- if (amount <= 0) throw new exports.T2000Error("INVALID_AMOUNT", "Amount must be greater than zero");
1261
- if ((asset === "USDC" || asset === "USDsui") && amount < GASLESS_MIN_STABLE_AMOUNT) {
1262
- throw new exports.T2000Error(
1263
- "INVALID_AMOUNT",
1264
- `Minimum gasless transfer is ${GASLESS_MIN_STABLE_AMOUNT} ${asset}. Got ${amount}.`
1265
- );
1266
- }
1267
1368
  const rawAmount = displayToRaw(amount, assetInfo.decimals);
1268
1369
  const tx = new transactions.Transaction();
1269
1370
  tx.setSender(address);
@@ -2059,7 +2160,9 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
2059
2160
  amountUsd: approxUsdValue(params.from, params.amount) ?? 0,
2060
2161
  force: params.force
2061
2162
  });
2062
- const { findSwapRoute: findSwapRoute2, buildSwapTx: buildSwapTx2, resolveTokenType: resolveTokenType2 } = await Promise.resolve().then(() => (init_cetus_swap(), cetus_swap_exports));
2163
+ const { findSwapRoute: findSwapRoute2, buildSwapTx: buildSwapTx2, resolveTokenType: resolveTokenType2, preflightSwap: preflightSwap2 } = await Promise.resolve().then(() => (init_cetus_swap(), cetus_swap_exports));
2164
+ const pf = preflightSwap2({ from: params.from, to: params.to, amount: params.amount });
2165
+ if (!pf.valid) throw new exports.T2000Error(pf.code, pf.error);
2063
2166
  const fromType = resolveTokenType2(params.from);
2064
2167
  const toType = resolveTokenType2(params.to);
2065
2168
  if (!fromType) throw new exports.T2000Error("ASSET_NOT_SUPPORTED", `Unknown token: ${params.from}. Provide the full coin type.`);
@@ -2789,6 +2892,9 @@ function displayHandle(label) {
2789
2892
  return `${label}@${parentDisplay}`;
2790
2893
  }
2791
2894
 
2895
+ // src/index.ts
2896
+ init_preflight();
2897
+
2792
2898
  exports.AUDRIC_PARENT_NAME = AUDRIC_PARENT_NAME;
2793
2899
  exports.AUDRIC_PARENT_NFT_ID = AUDRIC_PARENT_NFT_ID;
2794
2900
  exports.CETUS_USDC_SUI_POOL = CETUS_USDC_SUI_POOL;
@@ -2830,6 +2936,8 @@ exports.buildAddLeafTx = buildAddLeafTx;
2830
2936
  exports.buildRevokeLeafTx = buildRevokeLeafTx;
2831
2937
  exports.buildSendTx = buildSendTx;
2832
2938
  exports.buildSwapTx = buildSwapTx;
2939
+ exports.checkPositiveAmount = checkPositiveAmount;
2940
+ exports.checkSuiAddress = checkSuiAddress;
2833
2941
  exports.classifyAction = classifyAction;
2834
2942
  exports.classifyLabel = classifyLabel;
2835
2943
  exports.classifyTransaction = classifyTransaction;
@@ -2877,6 +2985,11 @@ exports.normalizeAsset = normalizeAsset;
2877
2985
  exports.normalizeCoinType = normalizeCoinType;
2878
2986
  exports.parseSuiRpcTx = parseSuiRpcTx;
2879
2987
  exports.payWithMpp = payWithMpp;
2988
+ exports.preflightFail = preflightFail;
2989
+ exports.preflightPay = preflightPay;
2990
+ exports.preflightSend = preflightSend;
2991
+ exports.preflightSwap = preflightSwap;
2992
+ exports.queryBalance = queryBalance;
2880
2993
  exports.queryHistory = queryHistory;
2881
2994
  exports.queryTransaction = queryTransaction;
2882
2995
  exports.rawToStable = rawToStable;